<?php

namespace App\Inventory\Service;

use App\Inventory\Entity\InventoryItem;
use App\Inventory\Entity\InventoryPackage;
use App\Inventory\Entity\InventoryPackageItem;
use App\Inventory\Repository\InventoryItemRepository;
use App\Inventory\Repository\InventoryPackageRepository;

/**
 * Inventory domain logic: derived aggregates (stat counters, package totals and
 * availability) and package assembly from item references. Controllers stay thin
 * and delegate every non-HTTP decision here.
 */
class InventoryService
{
    public function __construct(
        private readonly InventoryItemRepository    $itemRepo,
        private readonly InventoryPackageRepository $packageRepo,
    ) {}

    /**
     * The four headline counters shown as stat cards, derived from item statuses.
     *
     * @param InventoryItem[] $items
     * @return array{total:int, low:int, inStock:int, outOfStock:int}
     */
    public function stats(array $items): array
    {
        $low = $inStock = $out = 0;
        foreach ($items as $item) {
            match ($item->getStatus()) {
                InventoryItem::STATUS_LOW_STOCK    => $low++,
                InventoryItem::STATUS_IN_STOCK     => $inStock++,
                InventoryItem::STATUS_OUT_OF_STOCK => $out++,
                default                            => null,
            };
        }

        return [
            'total'      => count($items),
            'low'        => $low,
            'inStock'    => $inStock,
            'outOfStock' => $out,
        ];
    }

    /**
     * Serialize a package with its component items, derived total price (Rial)
     * and availability (true only if every component has enough stock).
     */
    public function packageToArray(InventoryPackage $package): array
    {
        $items     = [];
        $total     = 0;
        $available = true;

        foreach ($package->getItems() as $line) {
            /** @var InventoryPackageItem $line */
            $item    = $line->getItem();
            $amount  = $line->getAmount();
            $total  += $item->getPrice() * $amount;

            if ($item->getStock() < $amount) {
                $available = false;
            }

            $items[] = [
                'itemUuid' => $item->getUuid(),
                'name'     => $item->getName(),
                'unit'     => $item->getUnit(),
                'price'    => $item->getPrice(),
                'amount'   => $amount,
            ];
        }

        return [
            'uuid'      => $package->getUuid(),
            'title'     => $package->getTitle(),
            'items'     => $items,
            'total'     => $total,
            'available' => $available,
        ];
    }

    /**
     * Replace a package's component lines from a list of {itemUuid, amount}.
     * Silently skips references the tenant does not own. Returns the number of
     * lines actually attached.
     *
     * @param array<int, array{itemUuid?:string, amount?:int|string}> $lines
     */
    public function syncPackageItems(InventoryPackage $package, array $lines, string $entityType, int $entityId): int
    {
        $package->clearItems();
        $count = 0;

        foreach ($lines as $line) {
            $uuid = trim((string) ($line['itemUuid'] ?? ''));
            if ($uuid === '') {
                continue;
            }
            $item = $this->itemRepo->findByUuid($uuid);
            // Only attach items the caller owns — never leak another tenant's stock.
            if ($item === null || $item->getEntityType() !== $entityType || $item->getEntityId() !== $entityId) {
                continue;
            }
            $amount = (int) ($line['amount'] ?? 1);
            $package->addItem(new InventoryPackageItem($item, $amount));
            $count++;
        }

        return $count;
    }
}
