<?php

namespace Modules\LotesSeries\Services;

use PDO;
use Exception;

/**
 * Serviço para gestão de lotes
 */
class LoteService {
    private PDO $pdo;
    private string $tenantId;

    public function __construct(string $tenantId) {
        $this->tenantId = $tenantId;
        $this->pdo = \Gestoreasy\Core\Tenant\ConnectionManager::forTenant($tenantId);
    }

    /**
     * Cria um novo lote
     */
    public function criarLote(array $dados): int {
        $this->pdo->beginTransaction();
        try {
            // Validar se produto existe
            $stmt = $this->pdo->prepare('SELECT id FROM products WHERE id = ?');
            $stmt->execute([$dados['product_id']]);
            if (!$stmt->fetch()) {
                throw new Exception('Produto não encontrado');
            }

            // Validar duplicata se configurado
            $config = $this->getConfig();
            if ($config['validar_lote_duplicado']) {
                $stmt = $this->pdo->prepare('
                    SELECT id FROM product_lots 
                    WHERE tenant_id = ? AND product_id = ? AND numero_lote = ?
                ');
                $stmt->execute([$this->tenantId, $dados['product_id'], $dados['numero_lote']]);
                if ($stmt->fetch()) {
                    throw new Exception('Lote já existe para este produto');
                }
            }

            // Calcular dias de validade se necessário
            if (empty($dados['dias_validade']) && !empty($dados['data_fabricacao']) && !empty($dados['data_validade'])) {
                $fabricacao = new \DateTime($dados['data_fabricacao']);
                $validade = new \DateTime($dados['data_validade']);
                $dados['dias_validade'] = $fabricacao->diff($validade)->days;
            }

            // Criar lote
            $stmt = $this->pdo->prepare('
                INSERT INTO product_lots (
                    tenant_id, product_id, numero_lote, codigo_lote,
                    data_fabricacao, data_validade, dias_validade,
                    quantidade_inicial, quantidade_atual,
                    warehouse_id, bin_id, fornecedor_id,
                    nota_fiscal_id, numero_nota_fiscal, observacoes
                ) VALUES (
                    ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
                )
            ');
            $stmt->execute([
                $this->tenantId,
                $dados['product_id'],
                $dados['numero_lote'],
                $dados['codigo_lote'] ?? null,
                $dados['data_fabricacao'] ?? null,
                $dados['data_validade'] ?? null,
                $dados['dias_validade'] ?? null,
                $dados['quantidade_inicial'] ?? 0,
                $dados['quantidade_atual'] ?? $dados['quantidade_inicial'] ?? 0,
                $dados['warehouse_id'] ?? null,
                $dados['bin_id'] ?? null,
                $dados['fornecedor_id'] ?? null,
                $dados['nota_fiscal_id'] ?? null,
                $dados['numero_nota_fiscal'] ?? null,
                $dados['observacoes'] ?? null,
            ]);

            $loteId = (int)$this->pdo->lastInsertId();

            // Registrar movimentação inicial
            if (($dados['quantidade_inicial'] ?? 0) > 0) {
                $this->registrarMovimentacao($loteId, 'entrada', $dados['quantidade_inicial'], [
                    'origem_tipo' => $dados['origem_tipo'] ?? 'compra',
                    'origem_id' => $dados['origem_id'] ?? null,
                    'origem_numero' => $dados['origem_numero'] ?? null,
                    'motivo' => 'Criação do lote'
                ]);
            }

            $this->pdo->commit();
            return $loteId;
        } catch (Exception $e) {
            $this->pdo->rollBack();
            throw $e;
        }
    }

    /**
     * Sugere lote baseado na política (FIFO/FEFO/LIFO)
     */
    public function sugerirLote(int $productId, float $quantidade, string $politica = 'fifo'): ?array {
        $config = $this->getConfig();
        $politica = $politica !== 'manual' ? $politica : ($config['politica_estoque'] ?? 'fifo');

        $where = 'product_id = ? AND tenant_id = ? AND status = "ativo" AND quantidade_atual >= ?';
        $orderBy = '';

        switch ($politica) {
            case 'fifo': // First In, First Out
                $orderBy = 'ORDER BY data_fabricacao ASC, created_at ASC';
                break;
            case 'fefo': // First Expired, First Out
                $orderBy = 'ORDER BY data_validade ASC, data_fabricacao ASC';
                break;
            case 'lifo': // Last In, First Out
                $orderBy = 'ORDER BY created_at DESC, data_fabricacao DESC';
                break;
            default:
                $orderBy = 'ORDER BY created_at ASC';
        }

        $sql = "SELECT * FROM product_lots WHERE {$where} {$orderBy} LIMIT 1";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([$productId, $this->tenantId, $quantidade]);
        $lote = $stmt->fetch(PDO::FETCH_ASSOC);

        return $lote ?: null;
    }

    /**
     * Baixa quantidade do lote
     */
    public function baixarLote(int $loteId, float $quantidade, array $dadosMovimentacao = []): void {
        $this->pdo->beginTransaction();
        try {
            // Buscar lote
            $stmt = $this->pdo->prepare('SELECT * FROM product_lots WHERE id = ? AND tenant_id = ?');
            $stmt->execute([$loteId, $this->tenantId]);
            $lote = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$lote) {
                throw new Exception('Lote não encontrado');
            }

            if ($lote['quantidade_atual'] < $quantidade) {
                throw new Exception('Quantidade insuficiente no lote');
            }

            // Atualizar quantidade
            $novaQuantidade = $lote['quantidade_atual'] - $quantidade;
            $stmt = $this->pdo->prepare('
                UPDATE product_lots 
                SET quantidade_atual = ?,
                    status = CASE WHEN ? <= 0 THEN "esgotado" ELSE status END
                WHERE id = ?
            ');
            $stmt->execute([$novaQuantidade, $novaQuantidade, $loteId]);

            // Registrar movimentação
            $this->registrarMovimentacao($loteId, 'saida', $quantidade, $dadosMovimentacao);

            $this->pdo->commit();
        } catch (Exception $e) {
            $this->pdo->rollBack();
            throw $e;
        }
    }

    /**
     * Registra movimentação de lote
     */
    private function registrarMovimentacao(int $loteId, string $tipo, float $quantidade, array $dados = []): void {
        $userContext = \Gestoreasy\Core\Auth\UserContext::getCurrent();
        
        $stmt = $this->pdo->prepare('
            INSERT INTO lot_movements (
                tenant_id, lot_id, tipo, quantidade,
                origem_tipo, origem_id, origem_numero,
                destino_tipo, destino_id, destino_numero,
                warehouse_id_origem, warehouse_id_destino,
                motivo, observacoes, user_id
            ) VALUES (
                ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
            )
        ');
        $stmt->execute([
            $this->tenantId,
            $loteId,
            $tipo,
            $quantidade,
            $dados['origem_tipo'] ?? null,
            $dados['origem_id'] ?? null,
            $dados['origem_numero'] ?? null,
            $dados['destino_tipo'] ?? null,
            $dados['destino_id'] ?? null,
            $dados['destino_numero'] ?? null,
            $dados['warehouse_id_origem'] ?? null,
            $dados['warehouse_id_destino'] ?? null,
            $dados['motivo'] ?? null,
            $dados['observacoes'] ?? null,
            $userContext?->userId,
        ]);
    }

    /**
     * Obtém lotes próximos do vencimento
     */
    public function getProximosVencer(int $dias = 30): array {
        $dataLimite = date('Y-m-d', strtotime("+{$dias} days"));
        
        $stmt = $this->pdo->prepare('
            SELECT l.*, p.codigo as produto_codigo
            FROM product_lots l
            INNER JOIN products p ON l.product_id = p.id
            WHERE l.tenant_id = ? 
            AND l.data_validade BETWEEN CURDATE() AND ?
            AND l.status = "ativo"
            AND l.quantidade_atual > 0
            ORDER BY l.data_validade ASC
        ');
        $stmt->execute([$this->tenantId, $dataLimite]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    /**
     * Obtém lotes vencidos
     */
    public function getVencidos(): array {
        $stmt = $this->pdo->prepare('
            SELECT l.*, p.codigo as produto_codigo
            FROM product_lots l
            INNER JOIN products p ON l.product_id = p.id
            WHERE l.tenant_id = ? 
            AND l.data_validade < CURDATE()
            AND l.status IN ("ativo", "vencido")
            AND l.quantidade_atual > 0
            ORDER BY l.data_validade ASC
        ');
        $stmt->execute([$this->tenantId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    /**
     * Obtém configuração
     */
    private function getConfig(): array {
        $stmt = $this->pdo->prepare('SELECT * FROM lotes_series_config WHERE tenant_id = ?');
        $stmt->execute([$this->tenantId]);
        $config = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$config) {
            // Criar configuração padrão
            $stmt = $this->pdo->prepare('
                INSERT INTO lotes_series_config (tenant_id) VALUES (?)
            ');
            $stmt->execute([$this->tenantId]);
            return [
                'politica_estoque' => 'fifo',
                'alerta_validade_dias' => 30,
                'bloquear_vencido' => true,
                'validar_lote_duplicado' => true,
            ];
        }

        return $config;
    }
}

