<?php

namespace WMS\Services;

use WMS\Core\Database;
use WMS\Repositories\StockRepository;
use WMS\Repositories\StockMovementRepository;
use WMS\Repositories\BatchRepository;

/**
 * Servicio de gestión de Stock.
 * Operaciones: mover, bloquear, liberar, consultar.
 */
class StockService
{
    private StockRepository $stockRepo;
    private StockMovementRepository $movementRepo;
    private BatchRepository $batchRepo;

    public function __construct()
    {
        $this->stockRepo    = new StockRepository();
        $this->movementRepo = new StockMovementRepository();
        $this->batchRepo    = new BatchRepository();
    }

    /**
     * Mueve stock entre bins dentro del mismo almacén.
     */
    public function moveStock(array $data, int $userId): array
    {
        $warehouseId = (int) $data['warehouse_id'];
        $itemId      = (int) $data['item_id'];
        $batchId     = isset($data['batch_id']) ? (int) $data['batch_id'] : null;
        $fromBinId   = (int) $data['from_bin_id'];
        $toBinId     = (int) $data['to_bin_id'];
        $quantity    = (float) $data['quantity'];
        $status      = $data['stock_status'] ?? 'AVAILABLE';

        if ($fromBinId === $toBinId) {
            throw new \RuntimeException('Bin origen y destino deben ser diferentes');
        }

        $db = Database::getConnection();
        $db->beginTransaction();

        try {
            // Validar stock no quede negativo
        $currentQty = (float)($fromStock['quantity'] ?? 0);
        $reservedQty = (float)($fromStock['reserved_qty'] ?? 0);
        $available = $currentQty - $reservedQty;
        if ($available < $quantity) {
            throw new \RuntimeException("Stock insuficiente. Disponible: {$available}, Solicitado: {$quantity}");
        }

        // Descontar del origen
            $fromStockId = $this->stockRepo->findOrCreatePosition(
                $warehouseId, $fromBinId, $itemId, $batchId, $status
            );
            $this->stockRepo->subtractQuantity($fromStockId, $quantity);

            // Agregar al destino
            $toStockId = $this->stockRepo->findOrCreatePosition(
                $warehouseId, $toBinId, $itemId, $batchId, $status
            );
            $this->stockRepo->addQuantity($toStockId, $quantity);

            // Registrar movimiento
            $this->movementRepo->logMovement([
                'warehouse_id'  => $warehouseId,
                'movement_type' => 'MOVE',
                'item_id'       => $itemId,
                'batch_id'      => $batchId,
                'from_bin_id'   => $fromBinId,
                'to_bin_id'     => $toBinId,
                'from_status'   => $status,
                'to_status'     => $status,
                'quantity'      => $quantity,
                'reason'        => $data['reason'] ?? null,
                'created_by'    => $userId,
            ]);

            $db->commit();

            return ['message' => 'Stock movido exitosamente', 'quantity' => $quantity];
        } catch (\Throwable $e) {
            $db->rollBack();
            throw $e;
        }
    }

    /**
     * Bloquea un lote: cambia estado de AVAILABLE a BLOCKED.
     */
    public function blockBatch(int $batchId, array $data, int $userId): array
    {
        $batch = $this->batchRepo->findById($batchId);
        if (!$batch) {
            throw new \RuntimeException('Lote no encontrado');
        }

        $db = Database::getConnection();
        $db->beginTransaction();

        try {
            // Cambiar estado del lote
            $this->batchRepo->updateStatus($batchId, 'BLOCKED');

            // Cambiar estado de todo el stock AVAILABLE de este lote a BLOCKED
            $stmt = $db->prepare(
                "SELECT id, warehouse_id, bin_id, item_id, quantity FROM stock
                 WHERE batch_id = ? AND stock_status IN ('AVAILABLE','QA') AND quantity > 0"
            );
            $stmt->execute([$batchId]);
            $stocks = $stmt->fetchAll();

            foreach ($stocks as $stock) {
                // Crear posición BLOCKED
                $blockedStockId = $this->stockRepo->findOrCreatePosition(
                    $stock['warehouse_id'], $stock['bin_id'], $stock['item_id'], $batchId, 'BLOCKED'
                );
                $this->stockRepo->addQuantity($blockedStockId, $stock['quantity']);

                // Vaciar posición AVAILABLE
                $this->stockRepo->subtractQuantity($stock['id'], $stock['quantity']);

                // Registrar movimiento
                $this->movementRepo->logMovement([
                    'warehouse_id'  => $stock['warehouse_id'],
                    'movement_type' => 'BLOCK',
                    'item_id'       => $stock['item_id'],
                    'batch_id'      => $batchId,
                    'from_bin_id'   => $stock['bin_id'],
                    'to_bin_id'     => $stock['bin_id'],
                    'from_status'   => $stock['stock_status'],
                    'to_status'     => 'BLOCKED',
                    'quantity'      => $stock['quantity'],
                    'reason'        => $data['reason'] ?? 'Bloqueo manual',
                    'created_by'    => $userId,
                ]);
            }

            // Registrar bloqueo
            $db->prepare(
                "INSERT INTO stock_blocks
                 (block_type, item_id, batch_id, warehouse_id, reason, status, blocked_by)
                 VALUES (?, ?, ?, ?, ?, 'ACTIVE', ?)"
            )->execute([
                $data['block_type'] ?? 'MANUAL',
                $batch['item_id'],
                $batchId,
                $data['warehouse_id'] ?? ($stocks[0]['warehouse_id'] ?? 0),
                $data['reason'] ?? 'Bloqueo manual',
                $userId,
            ]);

            $db->commit();

            return [
                'message'        => 'Lote bloqueado exitosamente',
                'batch_number'   => $batch['batch_number'],
                'records_affected' => count($stocks),
            ];
        } catch (\Throwable $e) {
            $db->rollBack();
            throw $e;
        }
    }

    /**
     * Libera un lote: cambia estado de BLOCKED/QA a AVAILABLE.
     */
    public function releaseBatch(int $batchId, array $data, int $userId): array
    {
        $batch = $this->batchRepo->findById($batchId);
        if (!$batch) {
            throw new \RuntimeException('Lote no encontrado');
        }

        $fromStatus = $batch['status']; // QA o BLOCKED
        if ($fromStatus === 'AVAILABLE') {
            throw new \RuntimeException('El lote ya está disponible');
        }

        $db = Database::getConnection();
        $db->beginTransaction();

        try {
            // Cambiar estado del lote
            $this->batchRepo->updateStatus($batchId, 'AVAILABLE');

            // Cambiar estado de todo el stock de este lote
            $stmt = $db->prepare(
                "SELECT id, warehouse_id, bin_id, item_id, quantity FROM stock
                 WHERE batch_id = ? AND stock_status = ? AND quantity > 0"
            );
            $stmt->execute([$batchId, $fromStatus]);
            $stocks = $stmt->fetchAll();

            foreach ($stocks as $stock) {
                // Crear posición AVAILABLE
                $availStockId = $this->stockRepo->findOrCreatePosition(
                    $stock['warehouse_id'], $stock['bin_id'], $stock['item_id'], $batchId, 'AVAILABLE'
                );
                $this->stockRepo->addQuantity($availStockId, $stock['quantity']);

                // Vaciar posición anterior
                $this->stockRepo->subtractQuantity($stock['id'], $stock['quantity']);

                // Registrar movimiento
                $this->movementRepo->logMovement([
                    'warehouse_id'  => $stock['warehouse_id'],
                    'movement_type' => 'UNBLOCK',
                    'item_id'       => $stock['item_id'],
                    'batch_id'      => $batchId,
                    'from_bin_id'   => $stock['bin_id'],
                    'to_bin_id'     => $stock['bin_id'],
                    'from_status'   => $fromStatus,
                    'to_status'     => 'AVAILABLE',
                    'quantity'      => $stock['quantity'],
                    'reason'        => $data['reason'] ?? 'Liberación de lote',
                    'created_by'    => $userId,
                ]);
            }

            // Actualizar bloqueo activo
            $db->prepare(
                "UPDATE stock_blocks SET status = 'RELEASED', released_at = NOW(),
                 released_by = ?, release_notes = ?
                 WHERE batch_id = ? AND status = 'ACTIVE'"
            )->execute([$userId, $data['reason'] ?? null, $batchId]);

            $db->commit();

            return [
                'message'        => 'Lote liberado exitosamente',
                'batch_number'   => $batch['batch_number'],
                'records_affected' => count($stocks),
            ];
        } catch (\Throwable $e) {
            $db->rollBack();
            throw $e;
        }
    }

    /**
     * Consulta stock por item, lote o bin.
     */
    public function queryStock(array $filters): array
    {
        if (!empty($filters['item_id'])) {
            return $this->stockRepo->getStockByItem(
                (int) $filters['item_id'],
                isset($filters['warehouse_id']) ? (int) $filters['warehouse_id'] : null,
                $filters['stock_status'] ?? null
            );
        }

        if (!empty($filters['bin_id'])) {
            return $this->stockRepo->getStockByBin((int) $filters['bin_id']);
        }

        if (!empty($filters['warehouse_id'])) {
            return $this->stockRepo->getSummaryByWarehouse((int) $filters['warehouse_id']);
        }

        if (!empty($filters['batch_number'])) {
            $db = Database::getConnection();
            $stmt = $db->prepare(
                "SELECT s.*, i.item_code, i.item_name, b.batch_number, b.expiry_date, bn.code as bin_code
                 FROM stock s
                 LEFT JOIN items i ON i.id = s.item_id
                 LEFT JOIN batches b ON b.id = s.batch_id
                 LEFT JOIN bins bn ON bn.id = s.bin_id
                 WHERE b.batch_number = ?
                 ORDER BY s.id"
            );
            $stmt->execute([$filters['batch_number']]);
            return $stmt->fetchAll();
        }

        return [];
    }
}
