<?php

namespace App\Livewire\Financial;

use App\Domain\Financial\Models\Invoice;
use App\Domain\Financial\Models\PaymentPlan;
use App\Domain\Shared\Models\SystemSetting;
use App\Domain\Shared\Traits\UsesBranchScope;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Livewire\Attributes\Layout;
use Livewire\Component;

#[Layout('layouts.app')]
class PaymentPlanCreate extends Component
{
    use UsesBranchScope;

    public ?int $invoiceId = null;
    public int $installmentCount = 3;
    public string $frequency = 'monthly';
    public string $startDate = '';
    public int $downPayment = 0;
    public array $installments = [];

    // Settings-driven constraints
    public int $maxMonths = 12;
    public int $minDownPaymentPercent = 20;
    public int $maxDownPaymentPercent = 70;
    public int $minInvoiceAmount = 10000;
    public int $graceDays = 7;

    public function mount(?int $invoice = null): void
    {
        $this->authorize('invoices.create');
        $this->invoiceId = $invoice;
        $this->startDate = now()->addDays(7)->toDateString();

        $this->maxMonths = (int) SystemSetting::get('installment_max_months', 12);
        $this->minDownPaymentPercent = (int) SystemSetting::get('installment_min_down_payment_percent', 20);
        $this->maxDownPaymentPercent = (int) SystemSetting::get('installment_max_down_payment_percent', 70);
        $this->minInvoiceAmount = (int) SystemSetting::get('installment_min_amount', 10000);
        $this->graceDays = (int) SystemSetting::get('installment_grace_days', 7);
        $this->frequency = SystemSetting::get('installment_allowed_frequencies', 'monthly');

        $this->calculateInstallments();
    }

    public function updatedInstallmentCount(): void
    {
        $this->calculateInstallments();
    }

    public function updatedFrequency(): void
    {
        $this->calculateInstallments();
    }

    public function updatedStartDate(): void
    {
        $this->calculateInstallments();
    }

    public function updatedDownPayment(): void
    {
        $this->calculateInstallments();
    }

    /**
     * The invoice this plan is for, if the id in component state names one.
     *
     * Every read of it goes through here so there is one place to look: the id
     * is bound to the <select> and therefore settable from the browser, and
     * BranchScope is what stops it naming another branch's invoice — the query
     * returns null rather than somebody else's balance.
     */
    private function resolveInvoice(): ?Invoice
    {
        return $this->invoiceId ? Invoice::find($this->invoiceId) : null;
    }

    private function calculateInstallments(): void
    {
        $invoice = $this->resolveInvoice();

        if (!$invoice) {
            $this->installments = [];
            return;
        }

        $remaining = $invoice->due_amount - ($this->downPayment * 100);
        if ($remaining <= 0 || $this->installmentCount < 1) {
            $this->installments = [];
            return;
        }

        $perInstallment = intdiv($remaining, $this->installmentCount);
        $lastExtra = $remaining - ($perInstallment * $this->installmentCount);
        $startDate = Carbon::parse($this->startDate);

        $this->installments = [];
        for ($i = 0; $i < $this->installmentCount; $i++) {
            $dueDate = match ($this->frequency) {
                'weekly' => $startDate->copy()->addWeeks($i),
                'biweekly' => $startDate->copy()->addWeeks($i * 2),
                'monthly' => $startDate->copy()->addMonths($i),
                default => $startDate->copy()->addMonths($i),
            };

            $amount = $perInstallment + ($i === $this->installmentCount - 1 ? $lastExtra : 0);
            $this->installments[] = [
                'number' => $i + 1,
                'due_date' => $dueDate->toDateString(),
                'amount' => $amount,
            ];
        }
    }

    public function save(): void
    {
        $this->authorize('invoices.create');

        $invoice = $this->resolveInvoice();

        abort_if($invoice === null, 404);

        if ($invoice->due_amount < $this->minInvoiceAmount) {
            session()->flash('error', __('مبلغ الفاتورة أقل من الحد الأدنى للتقسيط'));
            return;
        }

        if ($this->installmentCount > $this->maxMonths) {
            session()->flash('error', __('عدد الأقساط يتجاوز الحد الأقصى المسموح') . " ({$this->maxMonths})");
            return;
        }

        $downPaymentPiasters = $this->downPayment * 100;
        $downPaymentPercent = $invoice->due_amount > 0
            ? round(($downPaymentPiasters / $invoice->due_amount) * 100)
            : 0;

        if ($downPaymentPercent < $this->minDownPaymentPercent) {
            session()->flash('error', __('المقدم أقل من الحد الأدنى المطلوب') . " ({$this->minDownPaymentPercent}%)");
            return;
        }

        if ($downPaymentPercent > $this->maxDownPaymentPercent) {
            session()->flash('error', __('المقدم يتجاوز الحد الأقصى المسموح') . " ({$this->maxDownPaymentPercent}%)");
            return;
        }

        DB::transaction(function () use ($invoice) {
            $plan = PaymentPlan::create([
                'academy_id' => $invoice->academy_id,
                'invoice_id' => $invoice->id,
                'total_installments' => $this->installmentCount,
                'paid_installments' => 0,
                'installment_amount' => $this->installments[0]['amount'] ?? 0,
                'frequency' => $this->frequency,
                'start_date' => $this->startDate,
                'next_due_date' => $this->installments[0]['due_date'] ?? $this->startDate,
                'status' => 'active',
            ]);

            foreach ($this->installments as $inst) {
                $plan->installments()->create([
                    'sequence' => $inst['number'],
                    'due_date' => $inst['due_date'],
                    'amount' => $inst['amount'],
                    'status' => 'pending',
                ]);
            }
        });

        session()->flash('success', __('تم إنشاء خطة الدفع بنجاح'));
        $this->redirect(route('invoices.show', $invoice));
    }

    public function render()
    {
        $invoice = $this->resolveInvoice();

        // Branch comes from BranchScope on invoices.branch_id. This used to
        // narrow through whereHasMorph on the participant instead, which also
        // dropped every invoice of this branch's own that is billed to anything
        // other than a participant.
        $invoices = Invoice::whereIn('status', ['sent', 'partially_paid', 'overdue'])
            ->where('due_amount', '>', 0)
            ->orderByDesc('created_at')
            ->limit(50)
            ->get();

        return view('livewire.financial.payment-plan-create', [
            'invoice' => $invoice,
            'invoices' => $invoices,
            'maxMonths' => $this->maxMonths,
            'minDownPaymentPercent' => $this->minDownPaymentPercent,
            'maxDownPaymentPercent' => $this->maxDownPaymentPercent,
            'minInvoiceAmount' => $this->minInvoiceAmount,
        ]);
    }
}
