<?php

namespace App\Livewire\Inventory;

use App\Domain\Inventory\Models\InventoryLevel;
use App\Domain\Inventory\Models\Product;
use App\Domain\Inventory\Models\StockCount;
use App\Domain\Inventory\Models\Warehouse;
use App\Domain\Inventory\Services\StockCountService;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Traits\UsesBranchScope;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('جرد مخزني جديد')]
class StockCountForm extends Component
{
    use UsesBranchScope;

    public ?int $warehouse_id = null;

    // Locked: only mount() sets it, from the route-bound (and therefore
    // branch-scoped) StockCount. save() writes counted quantities onto whatever
    // this points at, so it must not be settable from the browser.
    #[Locked]
    public ?int $stockCountId = null;

    public string $notes = '';

    /** @var array<int, array{product_id: int, name_ar: string, sku: string, system_quantity: int, counted_quantity: ?int}> */
    public array $items = [];

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

        if ($stockCount && $stockCount->exists) {
            $this->stockCountId = $stockCount->id;
            $this->warehouse_id = $stockCount->warehouse_id;
            $this->notes = $stockCount->notes ?? '';
            $this->loadExistingItems($stockCount);
        }
    }

    public function rules(): array
    {
        // exists: is a raw table query — no academy scope, no branch scope — and
        // $warehouse_id is a plain public property driving updatedWarehouseId().
        // Only this branch's stores: a branch counts its own stock.
        $academyId = app('current_academy')->id;
        $branchId = $this->getActiveBranchId();

        $warehouseExists = Rule::exists('warehouses', 'id')
            ->where('academy_id', $academyId)
            ->whereNull('deleted_at');

        if ($branchId !== null) {
            $warehouseExists->where(
                fn ($q) => $q->where('branch_id', $branchId)
            );
        }

        return [
            'warehouse_id' => ['required', $warehouseExists],
            'items' => 'required|array|min:1',
            'items.*.counted_quantity' => 'nullable|integer|min:0',
            'notes' => 'nullable|string|max:1000',
        ];
    }

    public function messages(): array
    {
        return [
            'warehouse_id.required' => 'يجب اختيار المستودع',
            'warehouse_id.exists' => 'المستودع غير موجود',
            'items.required' => 'يجب وجود منتجات للجرد',
            'items.min' => 'يجب وجود منتج واحد على الأقل',
            'items.*.counted_quantity.integer' => 'الكمية يجب أن تكون عددًا صحيحًا',
            'items.*.counted_quantity.min' => 'الكمية لا يمكن أن تكون سالبة',
            'notes.max' => 'الملاحظات يجب ألا تتجاوز 1000 حرف',
        ];
    }

    public function updatedWarehouseId(): void
    {
        if ($this->stockCountId) {
            return; // Don't reload if editing existing count
        }

        $this->items = [];

        if (!$this->warehouse_id) {
            return;
        }

        $this->loadWarehouseProducts();
    }

    private function loadWarehouseProducts(): void
    {
        // InventoryLevel is strictly branch-scoped, so a $warehouse_id pointing
        // at another branch's store returns nothing here rather than dumping
        // that store's whole stock sheet — names, SKUs, cost prices — into
        // $this->items.
        $levels = InventoryLevel::where('warehouse_id', $this->warehouse_id)
            ->with('product:id,name_ar,sku,cost_price')
            ->get();

        $this->items = $levels->map(fn ($level) => [
            'product_id' => $level->product_id,
            'name_ar' => $level->product->name_ar ?? '',
            'sku' => $level->product->sku ?? '',
            'cost_price' => $level->product->cost_price ?? 0,
            'system_quantity' => $level->quantity_on_hand,
            'counted_quantity' => null,
        ])->toArray();
    }

    private function loadExistingItems(StockCount $stockCount): void
    {
        $stockCount->load('items.product:id,name_ar,sku,cost_price');

        $this->items = $stockCount->items->map(fn ($item) => [
            'item_id' => $item->id,
            'product_id' => $item->product_id,
            'name_ar' => $item->product->name_ar ?? '',
            'sku' => $item->product->sku ?? '',
            'cost_price' => $item->product->cost_price ?? 0,
            'system_quantity' => $item->system_quantity,
            'counted_quantity' => $item->counted_quantity,
        ])->toArray();
    }

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

        try {
            $service = app(StockCountService::class);
            $user = auth()->user();

            if ($this->stockCountId) {
                // Continue existing count — record counts for items
                $stockCount = StockCount::findOrFail($this->stockCountId);

                foreach ($this->items as $item) {
                    if ($item['counted_quantity'] === null || $item['counted_quantity'] === '') {
                        continue;
                    }

                    $countItem = $stockCount->items()
                        ->where('id', $item['item_id'] ?? 0)
                        ->orWhere(function ($q) use ($item, $stockCount) {
                            $q->where('stock_count_id', $stockCount->id)
                              ->where('product_id', $item['product_id']);
                        })
                        ->first();

                    if ($countItem) {
                        $service->recordCount($countItem, (int) $item['counted_quantity'], $user);
                    }
                }

                if ($this->notes) {
                    $stockCount->update(['notes' => $this->notes]);
                }

                session()->flash('success', __('تم تحديث بيانات الجرد بنجاح'));
                $this->redirect(route('inventory.stock-counts'), navigate: true);
            } else {
                // Create new stock count
                $productIds = collect($this->items)->pluck('product_id')->toArray();
                $stockCount = $service->create($this->warehouse_id, $productIds, $user);

                if ($this->notes) {
                    $stockCount->update(['notes' => $this->notes]);
                }

                // Record any counts already entered
                foreach ($this->items as $index => $item) {
                    if ($item['counted_quantity'] === null || $item['counted_quantity'] === '') {
                        continue;
                    }

                    $countItem = $stockCount->items()->where('product_id', $item['product_id'])->first();
                    if ($countItem) {
                        $service->recordCount($countItem, (int) $item['counted_quantity'], $user);
                    }
                }

                session()->flash('success', __('تم إنشاء جلسة الجرد بنجاح'));
                $this->redirect(route('inventory.stock-counts'), navigate: true);
            }
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        } catch (\Exception $e) {
            session()->flash('error', __('حدث خطأ أثناء حفظ الجرد'));
        }
    }

    public function render()
    {
        return view('livewire.inventory.stock-count-form', [
            // Branch comes from Warehouse's own scope — no hand-written
            // clause here, and none needed.
            'warehouses' => Warehouse::active()
                ->orderBy('name_ar')->get(['id', 'name_ar', 'code']),
        ]);
    }
}
