<?php

namespace App\Livewire\Invoices;

use App\Domain\Financial\Services\InvoiceService;
use App\Domain\Participant\Models\Participant;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Traits\UsesBranchScope;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('إنشاء فاتورة')]
class InvoiceCreate extends Component
{
    use UsesBranchScope;

    public ?int $participant_id = null;
    public string $contact_name = '';
    public string $contact_phone = '';
    public string $issue_date = '';
    public string $due_date = '';
    public string $notes = '';
    public float $discount_amount = 0;
    public float $tax_amount = 0;

    public array $items = [
        ['description' => '', 'quantity' => 1, 'unit_price' => 0],
    ];

    public function mount(): void
    {
        $this->authorize('invoices.create');
        $this->issue_date = now()->format('Y-m-d');
        $this->due_date = now()->addDays(30)->format('Y-m-d');
    }

    public function updatedParticipantId(): void
    {
        if ($this->participant_id) {
            $participant = Participant::with('person')->find($this->participant_id);
            if ($participant) {
                $this->contact_name = $participant->person?->name_ar ?? '';
                $this->contact_phone = $participant->person?->phone ?? '';
            }
        }
    }

    public function addItem(): void
    {
        $this->items[] = ['description' => '', 'quantity' => 1, 'unit_price' => 0];
    }

    public function removeItem(int $index): void
    {
        if (count($this->items) > 1) {
            unset($this->items[$index]);
            $this->items = array_values($this->items);
        }
    }

    public function rules(): array
    {
        // save() writes participant_id straight into billable_id, and there was
        // no rule for it at all — not even an exists. `exists:` is a raw table
        // query anyway, so no global scope reaches it; the clauses have to be
        // spelled out or a crafted id bills this invoice to another branch's
        // (or another academy's) player and drops it on their screens.
        $branchId = $this->getActiveBranchId();

        $participantExists = Rule::exists('participants', 'id')->whereNull('deleted_at');

        // Mirrors the academy global scope, which keys off the bound
        // current_academy and not the user's column. Passing a null id to
        // ->where() would compile to `academy_id is null` and match nothing.
        if (app()->has('current_academy')) {
            $participantExists->where('academy_id', app('current_academy')->id);
        }

        // Null branch means all-branches mode, where there is nothing to
        // narrow to — not "match rows with no branch".
        if ($branchId !== null) {
            $participantExists->where('branch_id', $branchId);
        }

        return [
            'participant_id' => ['nullable', 'integer', $participantExists],
            'contact_name' => 'required|string|max:255',
            'issue_date' => 'required|date',
            'due_date' => 'required|date|after_or_equal:issue_date',
            'items' => 'required|array|min:1',
            'items.*.description' => 'required|string|max:255',
            'items.*.quantity' => 'required|integer|min:1',
            'items.*.unit_price' => 'required|numeric|min:0',
        ];
    }

    public function messages(): array
    {
        return [
            'participant_id.exists' => 'العميل المختار غير موجود',
            'contact_name.required' => 'اسم العميل مطلوب',
            'issue_date.required' => 'تاريخ الإصدار مطلوب',
            'due_date.required' => 'تاريخ الاستحقاق مطلوب',
            'due_date.after_or_equal' => 'تاريخ الاستحقاق يجب أن يكون بعد تاريخ الإصدار',
            'items.required' => 'يجب إضافة عنصر واحد على الأقل',
            'items.min' => 'يجب إضافة عنصر واحد على الأقل',
            'items.*.description.required' => 'وصف العنصر مطلوب',
            'items.*.quantity.required' => 'الكمية مطلوبة',
            'items.*.quantity.min' => 'الكمية يجب أن تكون 1 على الأقل',
            'items.*.unit_price.required' => 'سعر الوحدة مطلوب',
            'items.*.unit_price.min' => 'سعر الوحدة لا يمكن أن يكون سالباً',
        ];
    }

    public function save(InvoiceService $service): void
    {
        $this->validate();

        try {
            $invoiceData = [
                'academy_id' => auth()->user()->academy_id,
                'number' => $service->generateNumber(auth()->user()->academy_id),
                'type' => 'standard',
                'billable_type' => $this->participant_id ? Participant::class : null,
                'billable_id' => $this->participant_id,
                'contact_name' => $this->contact_name,
                'contact_phone' => $this->contact_phone ?: null,
                'discount_amount' => (int) round($this->discount_amount * 100),
                'tax_amount' => (int) round($this->tax_amount * 100),
                'total_amount' => 0,
                'paid_amount' => 0,
                'currency' => 'EGP',
                'issue_date' => $this->issue_date,
                'due_date' => $this->due_date,
                'notes' => $this->notes ?: null,
            ];

            $itemsData = collect($this->items)->map(fn ($item) => [
                'description' => $item['description'],
                'quantity' => (int) $item['quantity'],
                'unit_price' => (int) round((float) $item['unit_price'] * 100),
                'discount_amount' => 0,
                'tax_amount' => 0,
            ])->toArray();

            $invoice = $service->create($invoiceData, $itemsData, auth()->user());

            session()->flash('success', __('تم إنشاء الفاتورة بنجاح') . ' #' . $invoice->number);
            $this->redirect(route('invoices.show', $invoice), navigate: true);
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        }
    }

    #[Computed]
    public function subtotal(): float
    {
        return collect($this->items)->sum(fn ($item) => ($item['quantity'] ?? 0) * ($item['unit_price'] ?? 0));
    }

    #[Computed]
    public function total(): float
    {
        return $this->subtotal - $this->discount_amount + $this->tax_amount;
    }

    public function render()
    {
        return view('livewire.invoices.invoice-create', [
            // Participant is branch-scoped at the model, so this list already
            // holds only the active branch's players.
            'participants' => Participant::with('person')
                ->whereIn('status', ['registered', 'active'])
                ->orderByDesc('created_at')
                ->limit(200)
                ->get(),
        ]);
    }
}
