<?php
namespace Modules\Wms\Controllers;

use Gestoreasy\Core\Http\Response;
use Gestoreasy\Core\Tenant\ConnectionManager;
use Gestoreasy\Core\Audit\AuditLogger;

class BinMovementsController
{
    public function list($request)
    {
        $pdo = ConnectionManager::forTenant();
        $rows = $pdo->query('SELECT id, product_id, from_bin_id, to_bin_id, quantity, created_at FROM bin_movements ORDER BY created_at DESC')->fetchAll();
        return Response::json(['data' => $rows]);
    }

    public function exportCsv($request)
    {
        $pdo = ConnectionManager::forTenant();
        $sep = (string)($request->query['sep'] ?? ';');
        if (!in_array($sep, [';', ',', '\t'], true)) { $sep = ';'; }
        if ($sep === '\t') { $sep = "\t"; }
        $q = trim((string)($request->query['q'] ?? ''));
        $start = (string)($request->query['start'] ?? '');
        $end = (string)($request->query['end'] ?? '');

        $where = ['1=1'];
        $params = [];
        if ($q !== '') {
            $where[] = '(bm.product_id LIKE :q OR p.codigo LIKE :q OR p.descricao LIKE :q OR from_bin.code LIKE :q OR to_bin.code LIKE :q)';
            $params[':q'] = '%' . $q . '%';
        }
        if ($start !== '') { $where[] = 'bm.created_at >= :start'; $params[':start'] = $start; }
        if ($end !== '') { $where[] = 'bm.created_at <= :end'; $params[':end'] = $end; }
        $whereSql = implode(' AND ', $where);

        $sql = "SELECT bm.id, bm.product_id, p.codigo, p.descricao, bm.from_bin_id, from_bin.code AS from_code, bm.to_bin_id, to_bin.code AS to_code, bm.quantity, bm.created_at
                FROM bin_movements bm
                LEFT JOIN products p ON p.id = bm.product_id
                LEFT JOIN bins from_bin ON from_bin.id = bm.from_bin_id
                LEFT JOIN bins to_bin ON to_bin.id = bm.to_bin_id
                WHERE $whereSql
                ORDER BY bm.created_at DESC";
        $stmt = $pdo->prepare($sql);
        foreach ($params as $k => $v) { $stmt->bindValue($k, $v); }
        $stmt->execute();
        $rows = $stmt->fetchAll();

        $bom = chr(0xEF) . chr(0xBB) . chr(0xBF);
        $out = fopen('php://temp', 'r+');
        fputs($out, $bom);
        $headers = ['ID', 'Produto ID', 'Código', 'Descrição', 'Bin Origem', 'Bin Destino', 'Quantidade', 'Data'];
        fputcsv($out, $headers, $sep);
        foreach ($rows as $r) {
            $dt = isset($r['created_at']) ? (string)$r['created_at'] : '';
            $dt = str_replace('T', ' ', $dt);
            fputcsv($out, [
                (int)($r['id'] ?? 0),
                (int)($r['product_id'] ?? 0),
                $r['codigo'] ?? null,
                $r['descricao'] ?? null,
                $r['from_code'] ?? ($r['from_bin_id'] ?? null),
                $r['to_code'] ?? ($r['to_bin_id'] ?? null),
                isset($r['quantity']) ? (float)$r['quantity'] : null,
                $dt ? substr($dt, 0, 19) : null,
            ], $sep);
        }
        rewind($out);
        $csv = stream_get_contents($out);
        fclose($out);
        return new Response(200, $csv, [
            'Content-Type' => 'text/csv; charset=utf-8',
            'Content-Disposition' => 'attachment; filename="wms_movimentacoes.csv"'
        ]);
    }

    public function transfer($request)
    {
        $pdo = ConnectionManager::forTenant();
        $p = $request->body;
        $pid = (int)($p['product_id'] ?? 0);
        $from = (int)($p['from_bin_id'] ?? 0);
        $to = (int)($p['to_bin_id'] ?? 0);
        $qty = (float)($p['quantity'] ?? 0);
        $allowNegative = (bool)($p['allow_negative'] ?? false);
        
        if ($pid <= 0 || $from <= 0 || $to <= 0 || $qty <= 0) {
            return Response::json(['error' => 'product_id, from_bin_id, to_bin_id e quantity são obrigatórios'], 422);
        }

        try {
            $pdo->beginTransaction();

            // Validar se há estoque disponível no bin de origem
            if (!$allowNegative) {
                $estoqueDisponivel = $this->getEstoqueDisponivelBin($pdo, $pid, $from);
                if ($estoqueDisponivel < $qty) {
                    $pdo->rollBack();
                    return Response::json([
                        'error' => 'Estoque insuficiente no bin de origem',
                        'available' => $estoqueDisponivel,
                        'required' => $qty,
                        'product_id' => $pid,
                        'from_bin_id' => $from
                    ], 422);
                }
            }

            // Registrar movimentação
            $pdo->prepare('INSERT INTO bin_movements (product_id, from_bin_id, to_bin_id, quantity) VALUES (:pid, :from, :to, :qty)')
                ->execute([':pid' => $pid, ':from' => $from, ':to' => $to, ':qty' => $qty]);

            // Atualizar estoque do produto (se integração estiver habilitada)
            $this->atualizarEstoqueProduto($pdo, $pid, $from, $to, $qty);

            // Registrar movimentação de estoque
            $this->registrarMovimentacaoEstoque($pdo, $pid, $from, $to, $qty);

            $pdo->commit();
            
            AuditLogger::log('bin_movements', 'transfer', [
                'product_id' => $pid,
                'from_bin_id' => $from,
                'to_bin_id' => $to,
                'quantity' => $qty
            ]);
            
            return Response::json(['data' => true]);
        } catch (\Exception $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            return Response::json(['error' => 'Erro ao transferir: ' . $e->getMessage()], 500);
        }
    }

    /**
     * Obtém estoque disponível em um bin específico
     */
    private function getEstoqueDisponivelBin(\PDO $pdo, int $productId, int $binId): float {
        // Calcular: entradas (to_bin_id) - saídas (from_bin_id) para este produto neste bin
        $stmt = $pdo->prepare('
            SELECT 
                COALESCE(SUM(CASE WHEN to_bin_id = ? THEN quantity ELSE 0 END), 0) -
                COALESCE(SUM(CASE WHEN from_bin_id = ? THEN quantity ELSE 0 END), 0) AS estoque
            FROM bin_movements
            WHERE product_id = ?
        ');
        $stmt->execute([$binId, $binId, $productId]);
        $result = $stmt->fetch(\PDO::FETCH_ASSOC);
        
        return (float)($result['estoque'] ?? 0);
    }

    /**
     * Atualiza estoque do produto (integração com módulo Estoque)
     */
    private function atualizarEstoqueProduto(\PDO $pdo, int $productId, int $fromBinId, int $toBinId, float $qty): void {
        try {
            // Verificar se deve atualizar estoque geral
            // Se o bin de destino for um bin de expedição, reduzir estoque
            // Se o bin de origem for um bin de recebimento, aumentar estoque
            
            $stmt = $pdo->prepare('SELECT code FROM bins WHERE id = ?');
            
            // Verificar bin de destino
            $stmt->execute([$toBinId]);
            $toBin = $stmt->fetch(\PDO::FETCH_ASSOC);
            if ($toBin && (strpos(strtoupper($toBin['code']), 'SHIP') !== false || strpos(strtoupper($toBin['code']), 'EXP') !== false)) {
                // Bin de expedição - reduzir estoque
                $this->reduzirEstoque($pdo, $productId, $qty);
            }
            
            // Verificar bin de origem
            $stmt->execute([$fromBinId]);
            $fromBin = $stmt->fetch(\PDO::FETCH_ASSOC);
            if ($fromBin && (strpos(strtoupper($fromBin['code']), 'RECV') !== false || strpos(strtoupper($fromBin['code']), 'REC') !== false)) {
                // Bin de recebimento - aumentar estoque ao sair
                // (já foi adicionado quando entrou, então não fazer nada aqui)
            }
        } catch (\Exception $e) {
            // Ignorar erros de integração (não crítico)
            error_log("Erro ao atualizar estoque do produto: " . $e->getMessage());
        }
    }

    /**
     * Reduz estoque do produto
     */
    private function reduzirEstoque(\PDO $pdo, int $productId, float $qty): void {
        try {
            // Tentar usar stored procedure se existir
            $stmt = $pdo->prepare('CALL sp_update_product_stock(?,?,?,?,?,?)');
            $stmt->execute([
                $productId,
                -$qty,
                'wms_transfer',
                null,
                'Transferência WMS',
                0 // não permitir negativo
            ]);
        } catch (\Exception $e) {
            // Fallback: atualizar diretamente
            $stmt = $pdo->prepare('UPDATE products SET estoque = estoque - ? WHERE id = ?');
            $stmt->execute([$qty, $productId]);
        }
    }

    /**
     * Registra movimentação de estoque
     */
    private function registrarMovimentacaoEstoque(\PDO $pdo, int $productId, int $fromBinId, int $toBinId, float $qty): void {
        try {
            $stmt = $pdo->prepare('
                INSERT INTO inventory_movements (
                    product_id, tipo, quantidade, motivo, 
                    origin_type, origin_id, created_at
                ) VALUES (?, ?, ?, ?, ?, ?, NOW())
            ');
            $stmt->execute([
                $productId,
                'wms_transfer',
                -$qty, // negativo porque está saindo
                "Transferência WMS: Bin {$fromBinId} -> Bin {$toBinId}",
                'wms_bin_movement',
                $fromBinId
            ]);
        } catch (\Exception $e) {
            // Ignorar se tabela não existir
            error_log("Erro ao registrar movimentação: " . $e->getMessage());
        }
    }

    public function levelsByBin($request)
    {
        $pdo = ConnectionManager::forTenant();
        // Nível por bin = entradas (to_bin_id) - saídas (from_bin_id)
        $sql = "SELECT b.id AS bin_id,
                       COALESCE(inq.qty_in, 0) - COALESCE(outq.qty_out, 0) AS level
                FROM bins b
                LEFT JOIN (
                    SELECT to_bin_id AS bin, SUM(quantity) AS qty_in
                    FROM bin_movements GROUP BY to_bin_id
                ) inq ON inq.bin = b.id
                LEFT JOIN (
                    SELECT from_bin_id AS bin, SUM(quantity) AS qty_out
                    FROM bin_movements GROUP BY from_bin_id
                ) outq ON outq.bin = b.id";
        $rows = $pdo->query($sql)->fetchAll();
        return Response::json(['data' => $rows]);
    }
}