<?php

namespace App\Http\Controllers\Admin;

use App\Core\Services\Security\SecureFileUploadService;
use App\Http\Controllers\Controller;
use App\Models\Company;
use App\Models\CreditNote;
use App\Models\Currency;
use App\Models\Invoice;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\View\View;

class CreditNoteController extends Controller
{
    public function index(Request $request): View
    {
        $perPage = (int) $request->get('per_page', 10);
        if (! in_array($perPage, [10, 25, 50, 100], true)) {
            $perPage = 10;
        }
        $search = trim((string) $request->get('search', ''));

        $query = CreditNote::with(['invoice.customer', 'customer']);

        if ($search !== '') {
            $query->where(function ($q) use ($search) {
                $q->where('credit_note_number', 'like', '%'.$search.'%')
                    ->orWhereHas('invoice', function ($inv) use ($search) {
                        $inv->where('invoice_number', 'like', '%'.$search.'%');
                    })
                    ->orWhereHas('customer', function ($customerQuery) use ($search) {
                        $customerQuery->where('name', 'like', '%'.$search.'%')
                            ->orWhere('email', 'like', '%'.$search.'%');
                    });
            });
        }

        $creditNotes = $query->orderBy('created_at', 'desc')->paginate($perPage)->withQueryString();

        return view('admin.credit-notes.index', [
            'creditNotes' => $creditNotes,
            'perPage' => $perPage,
            'search' => $search,
        ]);
    }

    public function create(): View
    {
        $invoices = Invoice::with('customer')
            ->orderBy('created_at', 'desc')
            ->get();

        $defaultCurrencyCode = Currency::getDefaultCode();
        $defaultBillFromCompany = Company::resolveBillFromCompany();

        return view('admin.credit-notes.create', [
            'invoices' => $invoices,
            'defaultCurrencyCode' => $defaultCurrencyCode,
            'defaultBillFromCompanyId' => $defaultBillFromCompany?->id,
        ]);
    }

    public function store(Request $request, SecureFileUploadService $secureUpload): RedirectResponse
    {
        $rawItems = $request->input('items', []);
        $filteredItems = [];
        if (is_array($rawItems)) {
            foreach ($rawItems as $row) {
                if (! is_array($row)) {
                    continue;
                }
                if (filled(trim((string) ($row['product'] ?? '')))) {
                    $filteredItems[] = $row;
                }
            }
        }
        $request->merge(['items' => $filteredItems]);
        if ($request->input('company_id') === '' || $request->input('company_id') === null) {
            $request->merge(['company_id' => null]);
        }
        if ($request->input('signature_type') === 'manual') {
            $request->merge(['signature_id' => null]);
        }

        $validated = $request->validate([
            'invoice_id' => 'required|exists:invoices,id',
            'customer_id' => 'required|exists:customers,id',
            'company_id' => 'nullable|exists:companies,id',
            'credit_note_date' => 'required|date',
            'status' => 'required|in:pending,approved,rejected',
            'items' => 'required|array|min:1',
            'items.*.product' => 'required|string|max:500',
            'items.*.product_id' => 'nullable|exists:products,id',
            'items.*.quantity' => 'required|numeric|min:0.01',
            'items.*.rate' => 'required|numeric|min:0',
            'items.*.discount' => 'nullable|numeric|min:0',
            'items.*.tax' => 'nullable|numeric|min:0|max:100',
            'items.*.unit' => 'nullable|string|max:64',
            'notes' => 'nullable|string',
            'terms' => 'nullable|string',
            'payment_method' => 'nullable|string|max:64',
            'bank_account_id' => 'nullable|required_if:payment_method,bank_transfer|exists:bank_accounts,id',
            'signature_type' => 'required|in:manual,esignature',
            'signature_id' => 'nullable|required_if:signature_type,esignature|exists:signatures,id',
            'manual_signature_type' => 'nullable|required_if:signature_type,manual|in:draw,upload,type',
            'signature_canvas_data' => 'nullable|string',
            'signature_upload' => 'nullable|file|image|max:5120',
            'signature_text' => 'nullable|string|max:255',
            'signature_font' => 'nullable|string|max:32',
        ]);

        $invoice = Invoice::with('customer')->findOrFail($validated['invoice_id']);
        if ((int) $validated['customer_id'] !== (int) $invoice->customer_id) {
            return back()->withErrors(['customer_id' => 'Customer must match the selected invoice.'])->withInput();
        }

        $companyId = $validated['company_id'] ?? null;
        if ($companyId === null || $companyId === '') {
            $company = Company::resolveBillFromCompany();
            if ($company === null) {
                return back()->withErrors([
                    'company_id' => 'Could not resolve Bill From. Add a company name in Settings, pick a company from the Bill From search, or create a company under Companies.',
                ])->withInput();
            }
            $companyId = $company->id;
        }

        $signatureType = $validated['signature_type'];
        $manualType = $validated['manual_signature_type'] ?? null;

        if ($signatureType === 'manual') {
            if ($manualType === null || $manualType === '') {
                return back()->withErrors(['manual_signature_type' => 'Choose how to add your manual signature (draw, upload, or type), or switch to eSignature.'])->withInput();
            }
            if ($manualType === 'draw' && empty($validated['signature_canvas_data'])) {
                return back()->withErrors(['signature_canvas_data' => 'Draw your signature on the canvas.'])->withInput();
            }
            if ($manualType === 'upload' && ! $request->hasFile('signature_upload')) {
                return back()->withErrors(['signature_upload' => 'Upload a signature image.'])->withInput();
            }
            if ($manualType === 'type' && empty(trim((string) ($validated['signature_text'] ?? '')))) {
                return back()->withErrors(['signature_text' => 'Enter your typed signature.'])->withInput();
            }
        }

        $savedFilePaths = [];

        try {
            DB::beginTransaction();

            $amount = 0;
            $totalTax = 0;
            $totalDiscount = 0;

            foreach ($validated['items'] as $item) {
                $itemSubtotal = (float) $item['quantity'] * (float) $item['rate'];
                $itemDiscount = (float) ($item['discount'] ?? 0);
                $itemTax = ($itemSubtotal - $itemDiscount) * ((float) ($item['tax'] ?? 0) / 100);

                $amount += $itemSubtotal;
                $totalDiscount += $itemDiscount;
                $totalTax += $itemTax;
            }

            $total = $amount - $totalDiscount + $totalTax;

            $signatureId = ($signatureType === 'esignature') ? ($validated['signature_id'] ?? null) : null;
            $signatureFilePath = null;
            $typedText = null;
            $typedFont = null;

            if ($signatureType === 'manual' && $manualType !== null) {
                if ($manualType === 'upload' && $request->hasFile('signature_upload')) {
                    $result = $secureUpload->upload($request->file('signature_upload'), 'credit-note-signatures', 'public');
                    if (! $result['success']) {
                        throw new \RuntimeException($result['message'] ?? 'Signature upload failed.');
                    }
                    $signatureFilePath = $result['path'];
                    $savedFilePaths[] = $signatureFilePath;
                } elseif ($manualType === 'draw' && ! empty($validated['signature_canvas_data'])) {
                    $data = $validated['signature_canvas_data'];
                    if (str_starts_with($data, 'data:image') && str_contains($data, 'base64,')) {
                        $parts = explode(',', $data, 2);
                        $binary = base64_decode($parts[1] ?? '', true);
                        if ($binary !== false && strlen($binary) > 50) {
                            $signatureFilePath = 'credit-note-signatures/'.Str::random(32).'.png';
                            Storage::disk('public')->put($signatureFilePath, $binary);
                            $savedFilePaths[] = $signatureFilePath;
                        }
                    }
                } elseif ($manualType === 'type') {
                    $typedText = trim((string) $validated['signature_text']);
                    $typedFont = $validated['signature_font'] ?? 'dancing';
                }
            }

            $currency = $invoice->currency ?? Currency::getDefaultCode();

            $creditNote = CreditNote::create([
                'credit_note_number' => CreditNote::generateCreditNoteNumber(),
                'invoice_id' => $invoice->id,
                'customer_id' => $invoice->customer_id,
                'company_id' => $companyId,
                'credit_note_date' => $validated['credit_note_date'],
                'status' => $validated['status'],
                'amount' => $amount,
                'tax_amount' => $totalTax,
                'discount_amount' => $totalDiscount,
                'total' => $total,
                'currency' => $currency,
                'notes' => $validated['notes'] ?? null,
                'terms' => $validated['terms'] ?? null,
                'payment_method' => $validated['payment_method'] ?? null,
                'bank_account_id' => ($validated['payment_method'] ?? null) === 'bank_transfer'
                    ? ($validated['bank_account_id'] ?? null)
                    : null,
                'signature_type' => $signatureType,
                'signature_id' => $signatureId,
                'signature_file_path' => $signatureFilePath,
                'typed_signature_text' => $typedText,
                'typed_signature_font' => $typedFont,
                'created_by' => Auth::id(),
            ]);

            foreach ($validated['items'] as $index => $item) {
                $itemSubtotal = (float) $item['quantity'] * (float) $item['rate'];
                $itemDiscount = (float) ($item['discount'] ?? 0);
                $itemTax = ($itemSubtotal - $itemDiscount) * ((float) ($item['tax'] ?? 0) / 100);
                $itemAmount = $itemSubtotal - $itemDiscount + $itemTax;

                $creditNote->items()->create([
                    'product_id' => $item['product_id'] ?? null,
                    'description' => $item['product'],
                    'unit' => $item['unit'] ?? null,
                    'quantity' => $item['quantity'],
                    'rate' => $item['rate'],
                    'discount' => $itemDiscount,
                    'tax_rate' => (float) ($item['tax'] ?? 0),
                    'tax_amount' => $itemTax,
                    'amount' => $itemAmount,
                    'sort_order' => $index,
                ]);
            }

            DB::commit();
        } catch (\Throwable $e) {
            DB::rollBack();
            foreach ($savedFilePaths as $path) {
                if ($path && Storage::disk('public')->exists($path)) {
                    Storage::disk('public')->delete($path);
                }
            }

            return back()
                ->withInput()
                ->withErrors(['error' => 'Could not save credit note: '.$e->getMessage()]);
        }

        return redirect()
            ->route('admin.credit-notes.index')
            ->with('success', 'Credit note created successfully.');
    }

    public function show(CreditNote $creditNote): View
    {
        $creditNote->load(['invoice.customer', 'customer', 'company', 'items']);

        return view('admin.credit-notes.show', ['creditNote' => $creditNote]);
    }

    public function edit(CreditNote $creditNote): RedirectResponse
    {
        return redirect()->route('admin.credit-notes.index')->with('info', 'Credit note editing is not available yet.');
    }

    public function update(Request $request, CreditNote $creditNote): RedirectResponse
    {
        return redirect()->route('admin.credit-notes.index')->with('info', 'Credit note update is not implemented yet.');
    }

    public function destroy(CreditNote $creditNote): RedirectResponse
    {
        $creditNote->delete();

        return redirect()->route('admin.credit-notes.index')->with('success', 'Credit note deleted successfully');
    }
}
