<?php

namespace App\Models;

use Config\Lookups;

/**
 * Inventory module — see CreateInventoryItemsTable. quantity_in_stock is
 * a maintained running total fed by the inventory_transactions ledger
 * (see CreateInventoryTransactionsTable) — every change goes through
 * receiveStock()/consumeStock()/adjustStock() below, the only paths that
 * write both the ledger row and the updated total together.
 */
class InventoryItemModel extends TenantModel
{
    protected $table            = 'inventory_items';
    protected $primaryKey       = 'id';
    protected $useAutoIncrement = true;
    protected $returnType       = 'object';
    protected $useTimestamps    = true;

    protected $allowedFields = [
        'name',
        'category',
        'quantity_in_stock',
        'unit',
        'unit_cost',
        'reorder_level',
    ];

    public function __construct()
    {
        parent::__construct();

        $categories = array_keys((new Lookups())->inventoryCategories);

        $this->validationRules = [
            'name'              => 'required|max_length[150]',
            'category'          => 'required|in_list[' . implode(',', $categories) . ']',
            'quantity_in_stock' => 'permit_empty|integer',
            'unit'              => 'required|max_length[20]',
            'unit_cost'         => 'permit_empty|decimal',
            'reorder_level'     => 'permit_empty|integer',
        ];
    }

    public function lowStock(): array
    {
        return $this->where('quantity_in_stock <= reorder_level', null, false)->orderBy('quantity_in_stock', 'asc')->findAll();
    }

    public function countLowStock(): int
    {
        return $this->where('quantity_in_stock <= reorder_level', null, false)->countAllResults();
    }

    public function totalValue(): string
    {
        $result = $this->select('SUM(quantity_in_stock * unit_cost) as total')->first();

        return $result->total ?? '0.00';
    }

    public function countCategories(): int
    {
        return count($this->select('category')->distinct()->findAll());
    }

    /** @return array<string, string> category => summed value */
    public function valueByCategory(): array
    {
        $rows = $this->select('category, SUM(quantity_in_stock * unit_cost) as total')
            ->groupBy('category')
            ->findAll();

        $out = [];

        foreach ($rows as $row) {
            $out[$row->category] = $row->total;
        }

        return $out;
    }

    /** New stock coming in (a delivery) — the one write path for a positive quantity_in_stock change. */
    public function receiveStock(int $id, int $quantity, ?string $reference, ?int $createdBy): void
    {
        $this->recordTransaction($id, 'received', abs($quantity), $reference, $createdBy);
    }

    /** Stock used up (e.g. by housekeeping/F&B) — clamped at 0, never goes negative. */
    public function consumeStock(int $id, int $quantity, ?string $reference, ?int $createdBy): void
    {
        $item = $this->find($id);

        if ($item === null) {
            return;
        }

        $applied = min(abs($quantity), (int) $item->quantity_in_stock);
        $this->recordTransaction($id, 'consumed', -$applied, $reference, $createdBy);
    }

    /** A manual correction (stock count, damage, etc.) to an exact new quantity — logs the real delta, not a fabricated one. */
    public function adjustStock(int $id, int $newQuantity, ?string $reference, ?int $createdBy): void
    {
        $item = $this->find($id);

        if ($item === null) {
            return;
        }

        $delta = $newQuantity - (int) $item->quantity_in_stock;

        if ($delta === 0) {
            return;
        }

        $this->recordTransaction($id, 'adjusted', $delta, $reference, $createdBy);
    }

    private function recordTransaction(int $id, string $type, int $signedQuantity, ?string $reference, ?int $createdBy): void
    {
        $item = $this->find($id);

        if ($item === null) {
            return;
        }

        (new InventoryTransactionModel())->insert([
            'inventory_item_id' => $id,
            'type'              => $type,
            'quantity'          => $signedQuantity,
            'reference'         => $reference,
            'created_by'        => $createdBy,
        ]);

        $this->update($id, ['quantity_in_stock' => max(0, (int) $item->quantity_in_stock + $signedQuantity)]);
    }
}
