<?php

namespace App\Livewire\Inventory;

use App\Domain\Inventory\Enums\MovementType;
use App\Domain\Inventory\Models\Product;
use App\Domain\Inventory\Models\Warehouse;
use App\Domain\Inventory\Services\InventoryService;
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 StockAdjustment extends Component
{
    use UsesBranchScope;

    public ?int $product_id = null;
    public ?int $warehouse_id = null;
    public int $quantity = 0;
    public string $adjustment_type = 'adjustment_up';
    public string $reason = '';
    public string $reference_number = '';

    public function mount(): void
    {
        $this->authorize('inventory.adjust');
    }

    public function rules(): array
    {
        // exists: is a raw table query — neither the academy scope nor the
        // branch scope reaches it — and both ids are plain public properties
        // bound with wire:model, so the browser can send whatever it likes.
        // The adjustment has to name this branch's own product and its own
        // store.
        $academyId = app('current_academy')->id;
        $branchId = $this->getActiveBranchId();

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

        return [
            'product_id' => ['required', $inBranch(
                Rule::exists('products', 'id')->where('academy_id', $academyId)->whereNull('deleted_at')
            )],
            'warehouse_id' => ['required', $inBranch(
                Rule::exists('warehouses', 'id')->where('academy_id', $academyId)->whereNull('deleted_at')
            )],
            'quantity' => 'required|integer|min:1',
            'adjustment_type' => 'required|in:adjustment_up,adjustment_down',
            'reason' => 'required|string|max:500',
            'reference_number' => 'nullable|string|max:50',
        ];
    }

    public function messages(): array
    {
        return [
            'product_id.required' => 'يجب اختيار المنتج',
            'product_id.exists' => 'المنتج غير موجود',
            'warehouse_id.required' => 'يجب اختيار المستودع',
            'warehouse_id.exists' => 'المستودع غير موجود',
            'quantity.required' => 'الكمية مطلوبة',
            'quantity.integer' => 'الكمية يجب أن تكون عددًا صحيحًا',
            'quantity.min' => 'الكمية يجب أن تكون 1 على الأقل',
            'adjustment_type.required' => 'نوع التسوية مطلوب',
            'adjustment_type.in' => 'نوع التسوية غير صالح',
            'reason.required' => 'سبب التسوية مطلوب',
            'reason.max' => 'سبب التسوية يجب ألا يتجاوز 500 حرف',
        ];
    }

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

        try {
            // Resolved through the scoped models, not by raw id: findOrFail on
            // Product/Warehouse now carries BranchScope, so a foreign-branch id
            // that somehow got past validation still 404s here.
            $product = Product::findOrFail($this->product_id);
            $warehouse = Warehouse::findOrFail($this->warehouse_id);

            $type = $this->adjustment_type === 'adjustment_up'
                ? MovementType::AdjustmentUp
                : MovementType::AdjustmentDown;

            // inventory_movements has no reference_number column — the old raw
            // insert here named one and would have failed on every save — so the
            // operator's reference is kept where it can actually be read back.
            $reason = $this->reference_number !== ''
                ? $this->reason.' — '.__('مرجع').': '.$this->reference_number
                : $this->reason;

            // createMovement is the only sanctioned way to move stock: it locks
            // the level row, records before/after and writes the movement in one
            // transaction. The raw DB::table() update/insert this replaced did
            // none of that.
            app(InventoryService::class)->createMovement(
                product: $product,
                warehouse: $warehouse,
                type: $type,
                quantity: $this->quantity,
                actor: auth()->user(),
                reason: $reason,
            );

            session()->flash('success', __('تمت التسوية بنجاح'));
            $this->reset(['product_id', 'warehouse_id', 'quantity', 'reason', 'reference_number']);
            $this->adjustment_type = 'adjustment_up';
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        } catch (\Exception $e) {
            session()->flash('error', __('حدث خطأ أثناء التسوية'));
        }
    }

    public function render()
    {
        // Both pickers are branch-filtered by the models' own BranchScope —
        // this branch's rows plus the academy-wide ones.
        return view('livewire.inventory.stock-adjustment', [
            'products' => Product::active()
                ->where('track_inventory', true)
                ->orderBy('name_ar')
                ->get(['id', 'name_ar', 'sku']),
            'warehouses' => Warehouse::active()->orderBy('name_ar')->get(['id', 'name_ar', 'code']),
        ]);
    }
}
