<?php

namespace App\Http\Controllers\Admin\Finance;

use App\Enums\AccountCategory;
use App\Http\Controllers\Admin\PageController;
use App\Http\Requests\Finance\StoreInvoiceRequest;
use App\Http\Requests\Finance\StoreQuotationRequest;
use App\Models\Account;
use App\Models\EmailMessage;
use App\Models\Invoice;
use App\Models\InvoiceLine;
use App\Models\Quotation;
use App\Models\Student;
use App\Services\Finance\DocumentNumberService;
use App\Services\Finance\InvoiceService;
use App\Services\Finance\QuotationService;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;

class InvoiceController extends PageController
{
    public function __construct(
        private readonly InvoiceService $invoiceService,
        private readonly QuotationService $quotationService,
        private readonly DocumentNumberService $documentNumbers,
    ) {}

    public function index(): View
    {
        $invoices = Invoice::query()
            ->with('student')
            ->latest('issue_date')
            ->paginate(20);

        $quotations = Quotation::query()
            ->with('student')
            ->latest()
            ->limit(10)
            ->get();

        return $this->page('admin.invoices.index', [
            'crumbTitle' => 'Invoicing & Quotations',
            'crumbSub' => 'Generate, send & track invoices',
            'invoices' => $invoices,
            'quotations' => $quotations,
        ]);
    }

    public function create(): View
    {
        return $this->page('admin.invoices.create', [
            'crumbTitle' => 'Invoicing & Quotations',
            'crumbSub' => 'Create new invoice',
            'students' => Student::query()->orderBy('first_name')->get(),
            'revenueAccounts' => Account::query()
                ->where('category', AccountCategory::Income->value)
                ->orderBy('code')
                ->get(),
        ]);
    }

    public function store(StoreInvoiceRequest $request): RedirectResponse
    {
        $validated = $request->validated();

        try {
            $invoice = DB::transaction(function () use ($validated) {
                $subtotal = '0.00';

                foreach ($validated['lines'] as $line) {
                    $lineTotal = bcmul((string) $line['quantity'], (string) $line['unit_price'], 2);
                    $subtotal = bcadd($subtotal, $lineTotal, 2);
                }

                $taxAmount = (string) ($validated['tax_amount'] ?? '0.00');
                $totalAmount = bcadd($subtotal, $taxAmount, 2);

                $invoice = Invoice::query()->create([
                    'invoice_number' => $this->documentNumbers->next('INV', Invoice::class, 'invoice_number'),
                    'student_id' => $validated['student_id'],
                    'issue_date' => $validated['issue_date'],
                    'due_date' => $validated['due_date'] ?? null,
                    'status' => 'draft',
                    'subtotal' => $subtotal,
                    'tax_amount' => $taxAmount,
                    'total_amount' => $totalAmount,
                    'amount_paid' => 0,
                    'balance_due' => $totalAmount,
                    'notes' => $validated['notes'] ?? null,
                ]);

                foreach ($validated['lines'] as $line) {
                    $lineTotal = bcmul((string) $line['quantity'], (string) $line['unit_price'], 2);

                    InvoiceLine::query()->create([
                        'invoice_id' => $invoice->id,
                        'description' => $line['description'],
                        'quantity' => $line['quantity'],
                        'unit_price' => $line['unit_price'],
                        'line_total' => $lineTotal,
                        'account_id' => $line['account_id'] ?? null,
                    ]);
                }

                return $this->invoiceService->issue($invoice->fresh(['lines', 'student']));
            });
        } catch (\Throwable $e) {
            return back()->withInput()->with('error', $e->getMessage());
        }

        return redirect()
            ->route('finance.invoices.show', $invoice)
            ->with('success', "Invoice {$invoice->invoice_number} issued successfully.");
    }

    public function show(Invoice $invoice): View
    {
        $invoice->load(['student', 'lines.account', 'paymentAllocations.receipt']);
        $journalEntry = $this->invoiceService->latestJournalEntry($invoice);
        $journalEntry?->load(['lines.account']);

        $emailHistory = EmailMessage::query()
            ->where('related_type', Invoice::class)
            ->where('related_id', $invoice->id)
            ->with('user')
            ->latest()
            ->limit(10)
            ->get();

        return $this->page('admin.invoices.show', [
            'crumbTitle' => 'Invoicing & Quotations',
            'crumbSub' => $invoice->invoice_number,
            'invoice' => $invoice,
            'journalEntry' => $journalEntry,
            'emailHistory' => $emailHistory,
        ]);
    }

    public function pdf(Invoice $invoice): Response
    {
        $invoice->load(['student', 'lines']);

        return Pdf::loadView('exports.invoice', [
            'invoice' => $invoice,
            'schoolName' => config('southend.app_name'),
        ])->download("invoice-{$invoice->invoice_number}.pdf");
    }

    public function pdfQuotation(Quotation $quotation): Response
    {
        $quotation->load(['student', 'lines']);

        return Pdf::loadView('exports.quotation', [
            'quotation' => $quotation,
            'schoolName' => config('southend.app_name'),
        ])->download("quotation-{$quotation->quotation_number}.pdf");
    }

    public function createQuotation(): View
    {
        return $this->page('admin.invoices.create-quotation', [
            'crumbTitle' => 'Invoicing & Quotations',
            'crumbSub' => 'Create quotation',
            'students' => Student::query()->orderBy('first_name')->get(),
        ]);
    }

    public function storeQuotation(StoreQuotationRequest $request): RedirectResponse
    {
        try {
            $quotation = $this->quotationService->create(
                $request->safe()->except('lines'),
                $request->validated('lines'),
            );
        } catch (\Throwable $e) {
            return back()->withInput()->with('error', $e->getMessage());
        }

        return $this->redirectWithSuccess('finance.invoices.index', "Quotation {$quotation->quotation_number} created.");
    }

    public function convertQuotation(Request $request, Quotation $quotation): RedirectResponse
    {
        $validated = $request->validate([
            'student_id' => ['required', 'exists:students,id'],
            'default_account_id' => ['nullable', 'exists:accounts,id'],
        ]);

        try {
            $invoice = $this->quotationService->convertToInvoice(
                $quotation,
                (int) $validated['student_id'],
                isset($validated['default_account_id']) ? (int) $validated['default_account_id'] : null,
            );
        } catch (\Throwable $e) {
            return back()->with('error', $e->getMessage());
        }

        return redirect()
            ->route('finance.invoices.show', $invoice)
            ->with('success', "Quotation converted to invoice {$invoice->invoice_number}.");
    }
}
