<?php

namespace App\Livewire\Inventory;

use App\Domain\Inventory\Models\Product;
use App\Domain\Inventory\Models\PurchaseOrder;
use App\Domain\Inventory\Models\Warehouse;
use App\Domain\Inventory\Services\PurchaseOrderService;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Traits\UsesBranchScope;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('أمر شراء')]
class PurchaseOrderForm extends Component
{
    use UsesBranchScope;

    public ?PurchaseOrder $purchaseOrder = null;
    public bool $editing = false;

    public string $supplier_name = '';
    public string $supplier_contact = '';
    public string $warehouse_id = '';
    public string $order_date = '';
    public string $expected_delivery_date = '';
    public string $tax_amount = '0';
    public string $shipping_cost = '0';
    public string $notes = '';

    public array $items = [];

    public function mount(?PurchaseOrder $purchaseOrder = null): void
    {
        $this->authorize('inventory.manage');

        $this->order_date = now()->format('Y-m-d');

        if ($purchaseOrder && $purchaseOrder->exists) {
            $this->purchaseOrder = $purchaseOrder;
            $this->editing = true;

            $this->supplier_name = $purchaseOrder->supplier_name ?? '';
            $this->supplier_contact = $purchaseOrder->supplier_contact ?? '';
            $this->warehouse_id = (string) ($purchaseOrder->warehouse_id ?? '');
            $this->order_date = $purchaseOrder->order_date?->format('Y-m-d') ?? '';
            $this->expected_delivery_date = $purchaseOrder->expected_delivery_date?->format('Y-m-d') ?? '';
            $this->tax_amount = (string) (($purchaseOrder->tax_amount ?? 0) / 100);
            $this->shipping_cost = (string) (($purchaseOrder->shipping_cost ?? 0) / 100);
            $this->notes = $purchaseOrder->notes ?? '';

            $purchaseOrder->load('items');
            foreach ($purchaseOrder->items as $item) {
                $this->items[] = [
                    'product_id' => (string) $item->product_id,
                    'quantity_ordered' => (string) $item->quantity_ordered,
                    'unit_cost' => (string) ($item->unit_cost / 100),
                    'notes' => $item->notes ?? '',
                ];
            }
        }

        if (empty($this->items)) {
            $this->items[] = ['product_id' => '', 'quantity_ordered' => '1', 'unit_cost' => '', 'notes' => ''];
        }
    }

    public function addItem(): void
    {
        $this->items[] = ['product_id' => '', 'quantity_ordered' => '1', 'unit_cost' => '', 'notes' => ''];
    }

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

        if (empty($this->items)) {
            $this->addItem();
        }
    }

    public function rules(): array
    {
        // exists: runs a raw table query, so neither the academy scope nor the
        // branch scope reaches it — and $warehouse_id and $items are plain
        // public properties. Without these predicates the form would file this
        // branch's order against another branch's store, or order a product
        // that branch alone stocks.
        $academyId = app('current_academy')->id;
        $branchId = $this->getActiveBranchId();

        $inBranch = fn ($rule) => $branchId === null
            ? $rule
            : $rule->where(fn ($q) => $q->where('branch_id', $branchId));

        return [
            'supplier_name' => 'required|string|max:255',
            'supplier_contact' => 'nullable|string|max:255',
            'warehouse_id' => ['required', $inBranch(
                Rule::exists('warehouses', 'id')->where('academy_id', $academyId)->whereNull('deleted_at')
            )],
            'order_date' => 'required|date',
            'expected_delivery_date' => 'nullable|date|after_or_equal:order_date',
            'tax_amount' => 'nullable|numeric|min:0',
            'shipping_cost' => 'nullable|numeric|min:0',
            'notes' => 'nullable|string|max:2000',
            'items' => 'required|array|min:1',
            'items.*.product_id' => ['required', $inBranch(
                Rule::exists('products', 'id')->where('academy_id', $academyId)->whereNull('deleted_at')
            )],
            'items.*.quantity_ordered' => 'required|integer|min:1',
            'items.*.unit_cost' => 'required|numeric|min:0',
        ];
    }

    public function messages(): array
    {
        return [
            'supplier_name.required' => 'اسم المورد مطلوب',
            'warehouse_id.required' => 'المستودع مطلوب',
            'warehouse_id.exists' => 'المستودع غير موجود',
            'order_date.required' => 'تاريخ الطلب مطلوب',
            'expected_delivery_date.after_or_equal' => 'تاريخ التسليم يجب أن يكون بعد تاريخ الطلب',
            'items.required' => 'يجب إضافة صنف واحد على الأقل',
            'items.min' => 'يجب إضافة صنف واحد على الأقل',
            'items.*.product_id.required' => 'اختر المنتج',
            'items.*.product_id.exists' => 'المنتج غير موجود',
            'items.*.quantity_ordered.required' => 'الكمية مطلوبة',
            'items.*.quantity_ordered.min' => 'الكمية يجب أن تكون 1 على الأقل',
            'items.*.unit_cost.required' => 'سعر الوحدة مطلوب',
            'items.*.unit_cost.min' => 'سعر الوحدة لا يمكن أن يكون سالب',
        ];
    }

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

        try {
            $branchId = $this->getActiveBranchId() ?? auth()->user()->branch_id;

            // Deliberately cross-branch: purchase_orders is unique(academy_id,
            // order_number), so the day's counter has to see every branch's
            // orders. Left branch-scoped, two branches ordering on the same day
            // would both mint PO-…-001 and the second one would hit the
            // constraint.
            $orderNumber = 'PO-' . now()->format('Ymd') . '-' . str_pad(
                PurchaseOrder::withoutBranchScope()->whereDate('created_at', today())->count() + 1,
                3, '0', STR_PAD_LEFT
            );

            $data = [
                'academy_id' => app('current_academy')->id,
                'branch_id' => $branchId,
                'warehouse_id' => (int) $this->warehouse_id,
                'order_number' => $this->editing ? $this->purchaseOrder->order_number : $orderNumber,
                'supplier_name' => $this->supplier_name,
                'supplier_contact' => $this->supplier_contact ?: null,
                'order_date' => $this->order_date,
                'expected_delivery_date' => $this->expected_delivery_date ?: null,
                'tax_amount' => (int) round((float) $this->tax_amount * 100),
                'shipping_cost' => (int) round((float) $this->shipping_cost * 100),
                'notes' => $this->notes ?: null,
                'metadata' => [],
            ];

            $items = collect($this->items)->map(fn ($item) => [
                'product_id' => (int) $item['product_id'],
                'quantity_ordered' => (int) $item['quantity_ordered'],
                'unit_cost' => (int) round((float) $item['unit_cost'] * 100),
                'notes' => $item['notes'] ?? null,
            ])->toArray();

            $service = app(PurchaseOrderService::class);

            $service->create($data, $items, auth()->user());
            session()->flash('success', __('تم إنشاء أمر الشراء بنجاح'));

            $this->redirect(route('inventory.purchase-orders'), navigate: true);
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        }
    }

    public function render()
    {
        // Both pickers are branch-filtered by the models' own BranchScope, so
        // neither carries a hand-written branch clause.
        return view('livewire.inventory.purchase-order-form', [
            'warehouses' => Warehouse::query()
                ->where('is_active', true)
                ->get(),
            'products' => Product::query()
                ->where('is_active', true)
                ->select('id', 'name_ar', 'sku')
                ->orderBy('name_ar')
                ->get(),
        ]);
    }
}
