<?php

namespace App\Livewire\Invoices;

use App\Domain\Financial\Enums\InvoiceStatus;
use App\Domain\Financial\Enums\PaymentMethod;
use App\Domain\Financial\Models\Invoice;
use App\Domain\Financial\Services\InvoiceService;
use App\Domain\Financial\Services\PaymentService;
use App\Domain\Financial\Services\RefundService;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Traits\UsesBranchScope;
use Illuminate\Support\Facades\DB;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('تفاصيل الفاتورة')]
class InvoiceShow extends Component
{
    use UsesBranchScope;

    public Invoice $invoice;

    // Payment form
    public float $paymentAmount = 0;
    public string $paymentMethod = 'cash';
    public string $paymentDate = '';
    public string $paymentNotes = '';

    // Edit payment method (super_admin only)
    public ?string $editingPaymentId = null;
    public string $editingPaymentMethod = '';

    public function mount(Invoice $invoice): void
    {
        $this->authorize('invoices.view');

        // BranchScope does not save us here. SubstituteBindings sits at index 5
        // of the web group and ResolveBranchContext is appended at index 8, so
        // the route parameter is resolved into a model before enforcement is
        // switched on — /invoices/{id} would hand back any invoice in the
        // academy, and recordPayment(), cancelInvoice() and refundPayment()
        // would then all act on it. Every later request re-hydrates the model
        // inside the Livewire controller, where the scope is active, so this is
        // the one gap and mount() is where it closes.
        $branchId = $this->getActiveBranchId();

        abort_if($branchId && $invoice->branch_id && (int) $invoice->branch_id !== $branchId, 403);

        $this->invoice = $invoice->load(['items', 'payments', 'billable', 'creator']);
        $this->paymentDate = now()->format('Y-m-d');
        $this->paymentAmount = $invoice->due_amount / 100;
    }

    public function recordPayment(PaymentService $service): void
    {
        $this->validate([
            'paymentAmount' => 'required|numeric|min:0.01',
            'paymentMethod' => 'required|in:cash,card,bank_transfer,wallet,online,cheque,other',
            'paymentDate' => 'required|date',
        ], [
            'paymentAmount.required' => 'المبلغ مطلوب',
            'paymentAmount.min' => 'المبلغ يجب أن يكون أكبر من صفر',
            'paymentMethod.required' => 'طريقة الدفع مطلوبة',
            'paymentDate.required' => 'تاريخ الدفع مطلوب',
        ]);

        $amountInPiasters = (int) round($this->paymentAmount * 100);

        if ($amountInPiasters > $this->invoice->due_amount) {
            session()->flash('error', __('المبلغ أكبر من المستحق'));
            return;
        }

        try {
            $service->recordPayment([
                'academy_id' => $this->invoice->academy_id,
                // mount() has already proved this invoice belongs to the active
                // branch, so in every normal case this is the till taking the
                // money. In all-branches mode there is no till, and the branch
                // that raised the invoice is the one owed the money — the old
                // chain ended at the payer's home branch, which credits the
                // receipt to whichever branch the player trains at rather than
                // to the branch carrying the receivable.
                'branch_id' => $this->getActiveBranchId() ?? $this->invoice->branch_id,
                'invoice_id' => $this->invoice->id,
                'reference' => 'PAY-' . now()->format('YmdHis') . '-' . rand(100, 999),
                'direction' => 'inbound',
                'method' => $this->paymentMethod,
                'payer_type' => $this->invoice->billable_type,
                'payer_id' => $this->invoice->billable_id,
                'amount' => $amountInPiasters,
                'currency' => 'EGP',
                'payment_date' => $this->paymentDate,
                'notes' => $this->paymentNotes ?: null,
                'received_by' => auth()->id(),
                'created_by' => auth()->id(),
            ], auth()->user());

            $this->invoice->refresh()->load(['items', 'payments', 'billable', 'creator']);
            $this->paymentAmount = $this->invoice->due_amount / 100;
            $this->paymentNotes = '';
            $this->dispatch('payment-recorded');
            session()->flash('success', __('تم تسجيل الدفعة بنجاح'));
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        } catch (\Exception $e) {
            session()->flash('error', __('حدث خطأ أثناء تسجيل الدفعة'));
        }
    }

    public function startEditPaymentMethod(string $paymentUuid): void
    {
        abort_unless(auth()->user()->is_super_admin, 403);

        $payment = $this->invoice->payments->firstWhere('uuid', $paymentUuid);
        if (! $payment) {
            return;
        }

        $this->editingPaymentId = $paymentUuid;
        $this->editingPaymentMethod = $payment->method->value ?? $payment->method;
    }

    public function savePaymentMethod(): void
    {
        abort_unless(auth()->user()->is_super_admin, 403);

        $this->validate([
            'editingPaymentMethod' => 'required|in:cash,card,bank_transfer,wallet,online,cheque,other',
        ], [
            'editingPaymentMethod.required' => 'طريقة الدفع مطلوبة',
            'editingPaymentMethod.in' => 'طريقة الدفع غير صالحة',
        ]);

        $payment = $this->invoice->payments->firstWhere('uuid', $this->editingPaymentId);
        if (! $payment) {
            $this->editingPaymentId = null;
            return;
        }

        $oldMethod = $payment->method->value ?? $payment->method;
        $newMethod = $this->editingPaymentMethod;

        if ($oldMethod !== $newMethod) {
            $payment->method = $newMethod;
            $payment->save();
        }

        $this->invoice->refresh()->load(['items', 'payments', 'billable', 'creator']);
        $this->editingPaymentId = null;
        $this->editingPaymentMethod = '';
        session()->flash('success', __('تم تعديل طريقة الدفع'));
    }

    public function cancelEditPaymentMethod(): void
    {
        $this->editingPaymentId = null;
        $this->editingPaymentMethod = '';
    }

    public function cancelInvoice(InvoiceService $service): void
    {
        try {
            $service->cancel($this->invoice, auth()->user());
            $this->invoice->refresh();
            session()->flash('success', __('تم إلغاء الفاتورة'));
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        } catch (\Exception $e) {
            session()->flash('error', __('لا يمكن إلغاء هذه الفاتورة'));
        }
    }

    #[Computed]
    public function canCancel(): bool
    {
        return in_array($this->invoice->status, [InvoiceStatus::Draft, InvoiceStatus::Sent])
            && $this->invoice->paid_amount === 0;
    }

    #[Computed]
    public function canRecordPayment(): bool
    {
        return $this->invoice->due_amount > 0
            && !in_array($this->invoice->status, [InvoiceStatus::Cancelled, InvoiceStatus::Refunded]);
    }

    #[Computed]
    public function canRefund(): bool
    {
        return $this->invoice->paid_amount > 0
            && !in_array($this->invoice->status, [InvoiceStatus::Cancelled, InvoiceStatus::Refunded]);
    }

    public function refundPayment(string $paymentUuid, RefundService $service): void
    {
        $this->authorize('refunds.initiate');

        try {
            DB::transaction(function () use ($paymentUuid, $service) {
                $service->processRefunds([$paymentUuid], 'استرداد من صفحة الفاتورة', auth()->user());
            });

            $this->invoice->refresh()->load(['items', 'payments', 'billable', 'creator']);
            $this->paymentAmount = max(0, $this->invoice->due_amount) / 100;
            session()->flash('success', __('تم استرداد الدفعة بنجاح'));
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        } catch (\Exception $e) {
            session()->flash('error', __('حدث خطأ أثناء الاسترداد'));
        }
    }

    public function render()
    {
        return view('livewire.invoices.invoice-show', [
            'paymentMethods' => collect(PaymentMethod::cases())->mapWithKeys(
                fn (PaymentMethod $m) => [$m->value => $m->label()]
            )->toArray(),
        ]);
    }
}
