<?php

namespace WMS\Repositories;

/**
 * Repositorio de Contenedores / LPN.
 */
class ContainerRepository extends BaseRepository
{
    protected string $table = 'containers';

    /**
     * Subquery que deriva la ubicación física (bin) actual del LPN a partir del
     * stock de sus líneas. Un contenedor no tiene bin propio en el esquema: su
     * ubicación = el/los bin(s) donde hoy reside el stock (item+lote) de sus líneas.
     *
     * Optima: el JOIN a stock usa el índice idx_stock_wh_item (warehouse_id, item_id)
     * y la búsqueda en container_lines usa uq_container_line (container_id, ...).
     *
     * %WH% y %CID% se reemplazan por la referencia al contenedor (alias SQL o '?').
     */
    private const SQL_LOCATION =
        "(SELECT GROUP_CONCAT(DISTINCT bn.code ORDER BY bn.code SEPARATOR ', ')
            FROM container_lines cl2
            JOIN stock s2 ON s2.warehouse_id = %WH%
                         AND s2.item_id = cl2.item_id
                         AND (cl2.batch_id IS NULL OR s2.batch_id <=> cl2.batch_id)
                         AND s2.quantity > 0
            JOIN bins bn ON bn.id = s2.bin_id
           WHERE cl2.container_id = %CID%)";

    /**
     * Obtiene un contenedor con sus líneas de detalle y su ubicación derivada.
     */
    public function getWithLines(int $id): ?array
    {
        $container = $this->findById($id);
        if (!$container) {
            return null;
        }

        $stmt = $this->db->prepare(
            "SELECT cl.*, i.item_code, i.item_name, bt.batch_number, bt.expiry_date
             FROM container_lines cl
             JOIN items i ON cl.item_id = i.id
             LEFT JOIN batches bt ON cl.batch_id = bt.id
             WHERE cl.container_id = ?
             ORDER BY cl.line_num"
        );
        $stmt->execute([$id]);
        $container['lines'] = $stmt->fetchAll();

        // Ubicación derivada del stock actual del LPN (1 query liviana, index-supported).
        $locSql = str_replace(['%WH%', '%CID%'], ['?', '?'], self::SQL_LOCATION);
        $locStmt = $this->db->prepare("SELECT " . $locSql . " AS bin_code");
        $locStmt->execute([(int) $container['warehouse_id'], $id]);
        $container['bin_code'] = $locStmt->fetchColumn() ?: null;

        return $container;
    }

    /**
     * Busca un contenedor por su código LPN.
     */
    public function findByCode(string $code): ?array
    {
        $stmt = $this->db->prepare("SELECT * FROM {$this->table} WHERE container_code = ?");
        $stmt->execute([$code]);
        return $stmt->fetch() ?: null;
    }

    /**
     * Obtiene contenedores de un almacén, opcionalmente filtrados por estado.
     * Incluye bin_code derivado (ubicación actual del stock del LPN).
     */
    public function getByWarehouse(int $warehouseId, ?string $status = null): array
    {
        $locSql = str_replace(['%WH%', '%CID%'], ['c.warehouse_id', 'c.id'], self::SQL_LOCATION);

        $sql = "SELECT c.*, w.code AS warehouse_code,
                       (SELECT COUNT(*) FROM container_lines cl WHERE cl.container_id = c.id) AS line_count,
                       " . $locSql . " AS bin_code
                FROM containers c
                JOIN warehouses w ON c.warehouse_id = w.id
                WHERE c.warehouse_id = ?";
        $params = [$warehouseId];

        if ($status) {
            $sql .= " AND c.status = ?";
            $params[] = $status;
        }

        $sql .= " ORDER BY c.created_at DESC";
        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetchAll();
    }
}
