<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreInvoiceRequest;
use App\Http\Requests\UpdateInvoiceRequest;
use App\Http\Requests\UpdateInvoiceStatusRequest;
use App\Http\Requests\VoidInvoiceRequest;
use App\Enums\InvoiceStatus;
use App\Enums\PaymentMethod;
use App\Models\Booking;
use App\Models\Customer;
use App\Models\Invoice;
use App\Models\Quotation;
use App\Models\Space;
use App\Services\BookingService;
use App\Services\InvoiceService;
use App\Services\PdfService;
use App\Services\SettingsService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;

class InvoiceController extends Controller
{
    public function index(Request $request, InvoiceService $invoiceService): View
    {
        $this->authorize('viewAny', Invoice::class);

        $invoices = Invoice::query()
            ->with(['customer', 'booking'])
            ->when($request->filled('search'), function ($query) use ($request): void {
                $search = $request->string('search')->toString();
                $query->where(function ($builder) use ($search): void {
                    $builder->where('invoice_number', 'like', "%{$search}%")
                        ->orWhereHas('customer', fn ($q) => $q->where('name', 'like', "%{$search}%"));
                });
            })
            ->when($request->filled('status'), fn ($query) => $query->where('status', $request->string('status')))
            ->latest()
            ->paginate(15)
            ->withQueryString();

        $invoices->getCollection()->transform(fn (Invoice $invoice) => $invoiceService->refreshOverdueStatus($invoice));

        return view('invoices.index', compact('invoices'));
    }

    public function create(Request $request): View
    {
        $this->authorize('create', Invoice::class);

        $customers = Customer::query()->where('is_active', true)->orderBy('name')->get();
        $spaces = Space::query()->with('layouts')->orderBy('name')->get();
        $packages = \App\Models\Package::query()
            ->where('status', \App\Enums\PackageStatus::Active)
            ->orderBy('name')
            ->get();
        $eventTypes = \App\Models\EventType::query()->where('is_active', true)->orderBy('sort_order')->get();
        $coordinators = \App\Models\User::query()->where('is_active', true)->orderBy('name')->get();
        $booking = $request->filled('booking_id')
            ? Booking::query()->with(['customer', 'spaces.space.layouts'])->find($request->integer('booking_id'))
            : null;
        $quotation = $request->filled('quotation_id')
            ? Quotation::query()->with(['items', 'booking.spaces.space.layouts'])->find($request->integer('quotation_id'))
            : null;

        return view('invoices.create', compact('customers', 'spaces', 'packages', 'booking', 'quotation', 'eventTypes', 'coordinators'));
    }

    public function store(StoreInvoiceRequest $request, InvoiceService $invoiceService, BookingService $bookingService): RedirectResponse
    {
        $this->authorize('create', Invoice::class);

        $validated = $request->validated();

        try {
            $invoice = DB::transaction(function () use ($validated, $invoiceService, $bookingService): Invoice {
                if (empty($validated['guest_count']) && ! empty($validated['spaces'])) {
                    $validated['guest_count'] = (int) collect($validated['spaces'])->max(
                        fn ($space) => (int) ($space['guest_count'] ?? 0)
                    );
                }

                if (empty($validated['booking_id']) && ! empty($validated['spaces'])) {
                    $booking = $bookingService->createFromDocumentPayload($validated);
                    $validated['booking_id'] = $booking->id;
                }

                foreach ([
                    'spaces', 'event_name', 'event_type_id', 'description', 'event_start', 'event_end',
                    'contact_name', 'contact_phone', 'contact_email', 'seating_arrangement',
                    'coordinator_id', 'customer_notes', 'internal_notes',
                    'space_id', 'setup_start', 'breakdown_end',
                ] as $key) {
                    unset($validated[$key]);
                }

                return $invoiceService->createDirect($validated);
            });
        } catch (\App\Exceptions\DocumentException $e) {
            return back()->withInput()->with('error', $e->getMessage());
        } catch (\DomainException $e) {
            return back()->withInput()->with('error', $e->getMessage());
        }

        return redirect()
            ->route('invoices.show', $invoice)
            ->with('success', 'Invoice created successfully.');
    }

    public function show(Invoice $invoice, InvoiceService $invoiceService): View
    {
        $this->authorize('view', $invoice);

        $invoice = $invoiceService->refreshOverdueStatus($invoice);
        $invoice->load(['items', 'customer', 'booking.spaces.space.layouts', 'booking.eventType', 'booking.coordinator', 'quotation', 'payments.receiver', 'attachments.uploader']);
        $invoice = $invoiceService->enforcePaymentDrivenStatus($invoice);
        $invoice->load(['payments.receiver']);

        $paymentMethods = collect(PaymentMethod::cases())
            ->mapWithKeys(fn (PaymentMethod $method) => [$method->value => $method->label()]);

        $statusOptions = $invoiceService->statusOptionsForShow($invoice);

        return view('invoices.show', compact('invoice', 'paymentMethods', 'statusOptions'));
    }

    public function edit(Invoice $invoice): View
    {
        $this->authorize('update', $invoice);

        $invoice->load(['items', 'booking.spaces.space']);
        $customers = Customer::query()->where('is_active', true)->orderBy('name')->get();
        $spaces = Space::query()->with('layouts')->orderBy('name')->get();
        $packages = \App\Models\Package::query()
            ->where('status', \App\Enums\PackageStatus::Active)
            ->orderBy('name')
            ->get();

        return view('invoices.edit', compact('invoice', 'customers', 'spaces', 'packages'));
    }

    public function update(UpdateInvoiceRequest $request, Invoice $invoice, InvoiceService $invoiceService): RedirectResponse
    {
        $this->authorize('update', $invoice);

        $invoiceService->updateDraft($invoice, $request->validated());

        return redirect()
            ->route('invoices.show', $invoice)
            ->with('success', 'Invoice updated successfully.');
    }

    public function destroy(Invoice $invoice, InvoiceService $invoiceService): RedirectResponse
    {
        $this->authorize('delete', $invoice);

        $invoiceService->deleteDraft($invoice);

        return redirect()
            ->route('invoices.index')
            ->with('success', 'Draft invoice deleted.');
    }

    public function createFromBooking(Booking $booking, InvoiceService $invoiceService): RedirectResponse
    {
        $this->authorize('create', Invoice::class);

        $invoice = $invoiceService->createFromBooking($booking);

        return redirect()
            ->route('invoices.show', $invoice)
            ->with('success', 'Invoice created from booking.');
    }

    public function createFromQuotation(Quotation $quotation, InvoiceService $invoiceService): RedirectResponse
    {
        $this->authorize('create', Invoice::class);

        $invoice = $invoiceService->createFromQuotation($quotation);

        return redirect()
            ->route('invoices.show', $invoice)
            ->with('success', 'Invoice created from quotation.');
    }

    public function issue(Invoice $invoice, InvoiceService $invoiceService): RedirectResponse
    {
        $this->authorize('issue', $invoice);

        $invoiceService->issue($invoice);

        return back()->with('success', 'Invoice issued successfully.');
    }

    public function void(VoidInvoiceRequest $request, Invoice $invoice, InvoiceService $invoiceService): RedirectResponse
    {
        $this->authorize('void', $invoice);

        $invoiceService->void($invoice, $request->validated('reason'));

        return back()->with('success', 'Invoice voided.');
    }

    public function updateStatus(UpdateInvoiceStatusRequest $request, Invoice $invoice, InvoiceService $invoiceService): RedirectResponse
    {
        $this->authorize('updateStatus', $invoice);

        try {
            $status = InvoiceStatus::from($request->validated('status'));
            $invoiceService->updateStatus($invoice, $status, $request->validated('reason'));
        } catch (\App\Exceptions\DocumentException $e) {
            return back()->with('error', $e->getMessage());
        }

        return back()->with('success', 'Invoice status updated.');
    }

    public function createQuotation(Invoice $invoice, InvoiceService $invoiceService): RedirectResponse
    {
        $this->authorize('create', Quotation::class);

        $quotation = $invoiceService->createQuotationFromInvoice($invoice);

        return redirect()
            ->route('quotations.show', $quotation)
            ->with('success', 'Draft quotation created from invoice.');
    }

    public function pdf(Invoice $invoice, PdfService $pdfService)
    {
        $this->authorize('view', $invoice);

        return $pdfService->invoice($invoice);
    }

    public function print(Invoice $invoice, SettingsService $settings): View
    {
        $this->authorize('view', $invoice);

        $invoice->load(['items', 'customer', 'booking']);

        return view('pdf.invoice-print', [
            'invoice' => $invoice,
            'settings' => $settings->all(),
        ]);
    }
}
