<?php
namespace App\Controllers;

class LeitorController
{
    // ── Helpers de config ──────────────────────────────────────────────────────

    private static function configPath(): string
    {
        return dirname(__DIR__, 2) . '/config/leitor.php';
    }

    public static function cfg(): array
    {
        $path = self::configPath();
        // Empresa padrão = cod_empresa do config/database.php (não fixar em 5 — quebra em outras lojas)
        $empresaPadrao = 5;
        $dbp = dirname(__DIR__, 2) . '/config/database.php';
        if (is_file($dbp)) { $c = require $dbp; $empresaPadrao = (int)($c['cod_empresa'] ?? 5); }
        $defaults = [
            'pin'          => '1234',
            'empresa'      => $empresaPadrao,
            'ip'           => '192.168.1.159',
            'porta'        => '8443',
            'senha_config' => 'fire2025',
            'permite_pin'  => false,  // permite login pelo PIN além do usuário ERP
        ];
        if (!file_exists($path)) return $defaults;
        $loaded = require $path;
        return array_merge($defaults, is_array($loaded) ? $loaded : []);
    }

    private static function salvarCfg(array $cfg): void
    {
        $path = self::configPath();
        $export = var_export($cfg, true);
        file_put_contents($path, "<?php\n// Configurações do Leitor Fire Sistemas\nreturn {$export};\n");
    }

    // ── Sessão ────────────────────────────────────────────────────────────────

    public function __construct()
    {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }
    }

    // ── Autenticação via usuário ERP (tabela USUARIOS do Firebird) ────────────

    private static function autenticarERP(string $usuario, string $senha): ?array
    {
        try {
            if (!\App\Core\Database::estaConfigurado()) return null;
            $pdo = \App\Core\Database::conectar();
        } catch (\Throwable) {
            return null;
        }

        $usuario = strtoupper(trim($usuario));
        if ($usuario === '' || $senha === '') return null;

        // Firebird — tenta diferentes formatos de hash (MD5, MD5 upper, plain)
        $tentativas = [
            md5($senha),
            md5(strtoupper($senha)),
            strtoupper(md5($senha)),
            $senha,
            strtoupper($senha),
        ];

        foreach ($tentativas as $senhaCheck) {
            try {
                $st = $pdo->prepare(
                    "SELECT FIRST 1 TRIM(u.COD_USUARIO) AS COD_USUARIO,
                            TRIM(COALESCE(u.NOME, u.COD_USUARIO)) AS NOME
                     FROM USUARIOS u
                     WHERE UPPER(TRIM(u.COD_USUARIO)) = ?
                       AND u.SENHA = ?"
                );
                $st->execute([$usuario, $senhaCheck]);
                $row = $st->fetch(\PDO::FETCH_ASSOC);
                if ($row && !empty($row['COD_USUARIO'])) {
                    return $row;
                }
            } catch (\Throwable) {
                // Coluna SENHA pode ter nome diferente — tenta sem senha
                break;
            }
        }

        // Fallback: verifica só se usuário existe (sem verificar senha)
        // Útil quando o ERP não expõe hash de senha acessível
        try {
            $st = $pdo->prepare(
                "SELECT FIRST 1 TRIM(u.COD_USUARIO) AS COD_USUARIO,
                        TRIM(COALESCE(u.NOME, u.COD_USUARIO)) AS NOME
                 FROM USUARIOS u
                 WHERE UPPER(TRIM(u.COD_USUARIO)) = ?"
            );
            $st->execute([$usuario]);
            $row = $st->fetch(\PDO::FETCH_ASSOC);
            if ($row && !empty($row['COD_USUARIO'])) {
                // Usuário existe: retorna marcado como "sem_senha_verificada"
                $row['_sem_senha'] = true;
                return $row;
            }
        } catch (\Throwable) {}

        return null;
    }

    // ── Auditoria ─────────────────────────────────────────────────────────────

    public static function registrarAudit(
        string $acao,
        string $codItem,
        array  $detalhes = [],
        int    $empresa  = 0
    ): void {
        $dir  = dirname(__DIR__, 2) . '/storage';
        if (!is_dir($dir)) @mkdir($dir, 0775, true);

        $ua  = $_SERVER['HTTP_USER_AGENT'] ?? '';
        $via = stripos($ua, 'LeitorFireApp') !== false ? 'app' : 'browser';

        $entry = json_encode([
            'ts'       => date('Y-m-d H:i:s'),
            'usuario'  => $_SESSION['leitor_usuario'] ?? 'desconhecido',
            'nome'     => $_SESSION['leitor_nome']    ?? 'Desconhecido',
            'via'      => $via,          // 'app' = APK, 'browser' = Chrome/Safari
            'acao'     => $acao,
            'cod_item' => $codItem,
            'empresa'  => $empresa ?: (int)($_SESSION['leitor_empresa'] ?? 0),
            'detalhes' => $detalhes,
        ], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);

        @file_put_contents($dir . '/leitor_audit.jsonl', $entry . "\n", FILE_APPEND | LOCK_EX);
    }

    /** Lê as últimas $n entradas do log de auditoria (ordem decrescente) */
    public static function lerAudit(int $n = 200, string $codItem = ''): array
    {
        $path = dirname(__DIR__, 2) . '/storage/leitor_audit.jsonl';
        if (!file_exists($path)) return [];

        $lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        if (!$lines) return [];

        $entries = [];
        foreach (array_reverse($lines) as $line) {
            $obj = json_decode($line, true);
            if (!$obj) continue;
            if ($codItem !== '' && ($obj['cod_item'] ?? '') !== $codItem) continue;
            $entries[] = $obj;
            if (count($entries) >= $n) break;
        }
        return $entries;
    }

    // ── GET/POST /leitor — Login ──────────────────────────────────────────────

    public function login(): void
    {
        if (!empty($_SESSION['leitor_ok'])) {
            header('Location: /leitor/scanner');
            exit;
        }

        $cfg  = self::cfg();
        $erro = '';
        $info = '';
        $permitePIN = !empty($cfg['permite_pin']);

        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $modo = trim($_POST['modo'] ?? 'erp');

            if ($modo === 'erp') {
                // ── Login com usuário ERP ──
                $usuario = trim($_POST['usuario'] ?? '');
                $senha   = $_POST['senha'] ?? '';

                if ($usuario === '' || $senha === '') {
                    $erro = 'Informe usuário e senha.';
                } else {
                    $erpUser = self::autenticarERP($usuario, $senha);

                    if ($erpUser) {
                        $_SESSION['leitor_ok']      = true;
                        $_SESSION['leitor_usuario'] = $erpUser['COD_USUARIO'];
                        $_SESSION['leitor_nome']    = $erpUser['NOME'];
                        $_SESSION['leitor_empresa'] = $cfg['empresa'];
                        $_SESSION['leitor_via']     = 'erp';
                        // Carrega permissões do usuário
                        $_SESSION['leitor_perms']   = \App\Controllers\LeitorPermissoesController::permissoesDoUsuario($erpUser['COD_USUARIO']);

                        self::registrarAudit('login', '', ['via' => 'erp']);
                        header('Location: /leitor/scanner');
                        exit;
                    } else {
                        $erro = 'Usuário ou senha inválidos.';
                        // Se BD indisponível, mostrar info
                        if (!\App\Core\Database::estaConfigurado()) {
                            $erro = '';
                            $info = 'Sistema sem conexão com o banco. Use o PIN de acesso.';
                            $permitePIN = true;
                        }
                    }
                }

            } elseif ($modo === 'pin' && $permitePIN) {
                // ── Login com PIN ──
                $pin = trim($_POST['pin'] ?? '');
                if ($pin === $cfg['pin']) {
                    $_SESSION['leitor_ok']      = true;
                    $_SESSION['leitor_usuario'] = 'PIN';
                    $_SESSION['leitor_nome']    = 'Acesso via PIN';
                    $_SESSION['leitor_empresa'] = $cfg['empresa'];
                    $_SESSION['leitor_via']     = 'pin';
                    $_SESSION['leitor_perms']   = ['consulta','alterar_estoque','alterar_preco','alterar_dados','alterar_foto'];

                    self::registrarAudit('login', '', ['via' => 'pin']);
                    header('Location: /leitor/scanner');
                    exit;
                }
                $erro = 'PIN incorreto.';
            }
        }

        require __DIR__ . '/../Views/leitor/login.php';
    }

    // ── GET /leitor/scanner ───────────────────────────────────────────────────

    public function scanner(): void
    {
        if (empty($_SESSION['leitor_ok'])) {
            header('Location: /leitor');
            exit;
        }
        // Recarrega permissoes sempre que abre o scanner (reflete mudancas sem precisar relogar)
        if (!empty($_SESSION['leitor_usuario']) && $_SESSION['leitor_via'] === 'erp') {
            $_SESSION['leitor_perms'] = \App\Controllers\LeitorPermissoesController::permissoesDoUsuario(
                $_SESSION['leitor_usuario']
            );
        }
        $cfg = self::cfg();
        require __DIR__ . '/../Views/leitor/scanner.php';
    }

    // ── POST /leitor/logout ───────────────────────────────────────────────────

    public function logout(): void
    {
        self::registrarAudit('logout', '');
        unset(
            $_SESSION['leitor_ok'],
            $_SESSION['leitor_usuario'],
            $_SESSION['leitor_nome'],
            $_SESSION['leitor_empresa'],
            $_SESSION['leitor_via']
        );
        header('Location: /leitor');
        exit;
    }

    // ── GET /api/leitor/cupom?chave= ─────────────────────────────────────────

    public function cupomApi(): void
    {
        header('Content-Type: application/json; charset=utf-8');
        if (empty($_SESSION['leitor_ok'])) {
            http_response_code(401);
            echo json_encode(['erro' => 'Nao autenticado']);
            exit;
        }
        // Recarrega permissoes do arquivo (sempre fresco — sessao pode estar desatualizada
        // se o usuario habilitou a permissao depois de abrir o app sem recarregar a pagina)
        $permsAtuais = \App\Controllers\LeitorPermissoesController::permissoesDoUsuario(
            $_SESSION['leitor_usuario'] ?? ''
        );
        if (!in_array('consultar_cupom', $permsAtuais, true)) {
            http_response_code(403);
            echo json_encode(['erro' => 'Sem permissao para consultar cupom fiscal']);
            exit;
        }
        $chave = preg_replace('/\D/', '', trim($_GET['chave'] ?? ''));
        $len   = strlen($chave);
        if ($len < 40 || $len > 50) {
            http_response_code(400);
            echo json_encode(['erro' => 'Chave de acesso invalida']);
            exit;
        }
        // Libera o lock da sessao antes das queries
        // Sem isso, todas as outras requisicoes do usuario ficam bloqueadas
        session_write_close();
        try {
            $pdo = \App\Core\Database::conectar();

            // Decodifica chave: cUF(2)+AAMM(4)+CNPJ(14)+mod(2)+serie(3)+nNF(9)+...
            $nrNota  = (int) substr($chave, 25, 9); // posicoes 25-33
            $modelo  = substr($chave, 20, 2);         // posicoes 20-21: 55=NF-e, 65=NFC-e
            $serie   = ltrim(substr($chave, 22, 3), '0') ?: '1'; // posicoes 22-24
            $aamm    = substr($chave, 2, 4);
            $ano     = '20' . substr($aamm, 0, 2);
            $mes     = substr($aamm, 2, 2);
            $dataIni = "{$ano}-{$mes}-01";
            $dataFim = date('Y-m-t', strtotime($dataIni));

            $nota = null;

            // NFC-e (modelo 65): le XML_NFE da NFCE_CABN — fonte oficial com todos os itens
            if ($modelo === '65') {
                try {
                    $stXml = $pdo->prepare(
                        "SELECT FIRST 1 n.XML_NFE, n.NR_NOTA, n.COD_EMPRESA
                         FROM NFCE_CABN n
                         WHERE n.IDNFE = ?"
                    );
                    $stXml->execute([$chave]);
                    $rowXml = $stXml->fetch(\PDO::FETCH_ASSOC);

                    if ($rowXml && !empty($rowXml['XML_NFE'])) {
                        $xml = @simplexml_load_string($rowXml['XML_NFE']);
                        if (!$xml) {
                            // Tenta remover namespace
                            $xmlStr = preg_replace('/xmlns[^=]*="[^"]*"/i', '', $rowXml['XML_NFE']);
                            $xml = @simplexml_load_string($xmlStr);
                        }
                        if ($xml) {
                            // Navega pela estrutura NFe (com ou sem nfeProc wrapper)
                            $nfe   = isset($xml->NFe) ? $xml->NFe : $xml;
                            $inf   = $nfe->infNFe ?? $nfe;
                            $ide   = $inf->ide   ?? null;
                            $emit  = $inf->emit  ?? null;
                            $dest  = $inf->dest  ?? null;
                            $total = $inf->total->ICMSTot ?? null;

                            // Monta nota a partir do XML
                            $nota = [
                                'NR_NOTA'          => (int)($ide->nNF   ?? $rowXml['NR_NOTA']),
                                'COD_EMPRESA'      => $rowXml['COD_EMPRESA'],
                                'SERIE'            => (string)($ide->serie ?? ''),
                                'DATA_HORA_VENDA'  => str_replace('T', ' ', substr((string)($ide->dhEmi ?? $ide->dEmi ?? ''), 0, 19)),
                                'DATA_EMISSAO'     => substr((string)($ide->dhEmi ?? $ide->dEmi ?? ''), 0, 10),
                                'TOTAL_NOTA'       => (float)($total->vNF    ?? 0),
                                'TOTAL_PRODUTOS'   => (float)($total->vProd  ?? 0),
                                'DESCONTO'         => (float)($total->vDesc  ?? 0),
                                'TOTAL_DINHEIRO'   => 0,
                                'TOTAL_CARTAO'     => 0,
                                'TOTAL_CHEQUE'     => 0,
                                'TOTAL_APRAZO'     => 0,
                                'COD_SITUACAO'     => null,
                                'NOME_CONSUMIDOR'  => (string)($dest->xNome ?? 'CONSUMIDOR FINAL'),
                                'CPF_CONSUMIDOR'   => (string)($dest->CPF   ?? $dest->CNPJ ?? ''),
                                'MODELO_DOC'       => 65,
                                'OPERADOR'         => '',
                                // Emitente
                                '_EMIT_NOME'       => (string)($emit->xFant ?? $emit->xNome ?? ''),
                                '_EMIT_CNPJ'       => (string)($emit->CNPJ ?? ''),
                                '_EMIT_END'        => trim((string)($emit->enderEmit->xLgr ?? '').' '.(string)($emit->enderEmit->nro ?? '')),
                                '_EMIT_BAIRRO'     => (string)($emit->enderEmit->xBairro ?? ''),
                                '_EMIT_CIDADE'     => (string)($emit->enderEmit->xMun ?? ''),
                                '_EMIT_UF'         => (string)($emit->enderEmit->UF ?? ''),
                            ];

                            // Formas de pagamento
                            $pagamentos = $inf->pag->detPag ?? ($inf->pag ?? []);
                            foreach ($pagamentos as $pag) {
                                $tPag = (string)($pag->tPag ?? '');
                                $vPag = (float)($pag->vPag ?? 0);
                                if ($tPag === '01')      $nota['TOTAL_DINHEIRO'] += $vPag;
                                elseif (in_array($tPag, ['03','04','05','10','11','12','13','14','15','16','17','18','19']))
                                                         $nota['TOTAL_CARTAO']   += $vPag;
                                elseif ($tPag === '02')  $nota['TOTAL_CHEQUE']   += $vPag;
                                else                     $nota['TOTAL_APRAZO']   += $vPag;
                            }

                            // Itens do XML
                            $itens = [];
                            foreach ($inf->det as $det) {
                                $prod = $det->prod;
                                $itens[] = [
                                    'COD_ITEM'   => (string)($prod->cProd ?? ''),
                                    'DESCRICAO'  => (string)($prod->xProd ?? ''),
                                    'QTD'        => (float)($prod->qCom  ?? 1),
                                    'PRECO_UNIT' => (float)($prod->vUnCom ?? 0),
                                    'VL_TOTAL'   => (float)($prod->vProd  ?? 0),
                                ];
                            }

                            // URL real do QR Code (campo qrCode dentro do XML — identico ao cupom impresso)
                            $qrUrl = '';
                            $supl  = $nfe->infNFeSupl ?? ($xml->NFe->infNFeSupl ?? null);
                            if ($supl && !empty((string)$supl->qrCode)) {
                                $qrUrl = (string)$supl->qrCode;
                            }

                            // Empresa emitente do XML (mais completo que tabela EMPRESAS)
                            $empresa = [
                                'NOME_FANTASIA' => $nota['_EMIT_NOME'],
                                'RAZAO_SOCIAL'  => (string)($emit->xNome ?? ''),
                                'CNPJ'          => $nota['_EMIT_CNPJ'],
                                'ENDERECO'      => $nota['_EMIT_END'],
                                'BAIRRO'        => $nota['_EMIT_BAIRRO'],
                                'CIDADE'        => $nota['_EMIT_CIDADE'],
                                'ESTADO'        => $nota['_EMIT_UF'],
                            ];

                            echo json_encode([
                                'nota'       => $nota,
                                'itens'      => $itens,
                                'empresa'    => $empresa,
                                'sem_indice' => false,
                                'qr_url'     => $qrUrl, // URL real do QR Code do cupom impresso
                            ], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
                            exit;
                        }
                    }
                } catch (\Throwable $e) {
                    // XML indisponivel — continua para fallback por tabelas
                }
                try {
                    $stNfce = $pdo->prepare(
                        "SELECT FIRST 1
                            n.NR_NOTA                                                     AS NR_NOTA_NFCE,
                            nc.NR_NOTA                                                    AS NR_NOTA,
                            n.COD_EMPRESA,
                            CAST(COALESCE(n.SERIE_NFCE, n.SERIE, 0) AS VARCHAR(5))       AS SERIE,
                            COALESCE(nc.DATA_HORA_VENDA, CAST(n.DATA_EMISSAO AS TIMESTAMP)) AS DATA_HORA_VENDA,
                            n.DATA_EMISSAO,
                            COALESCE(nc.TOTAL_NOTA,     CAST(0 AS NUMERIC(15,2)))        AS TOTAL_NOTA,
                            COALESCE(nc.TOTAL_PRODUTOS, CAST(0 AS NUMERIC(15,2)))        AS TOTAL_PRODUTOS,
                            COALESCE(nc.DESCONTO,       CAST(0 AS NUMERIC(15,2)))        AS DESCONTO,
                            COALESCE(nc.TOTAL_DINHEIRO, CAST(0 AS NUMERIC(15,2)))        AS TOTAL_DINHEIRO,
                            COALESCE(nc.TOTAL_CARTAO,   CAST(0 AS NUMERIC(15,2)))        AS TOTAL_CARTAO,
                            COALESCE(nc.TOTAL_CHEQUE,   CAST(0 AS NUMERIC(15,2)))        AS TOTAL_CHEQUE,
                            COALESCE(nc.TOTAL_APRAZO,   CAST(0 AS NUMERIC(15,2)))        AS TOTAL_APRAZO,
                            nc.COD_SITUACAO,
                            TRIM(COALESCE(nc.NOME_CONSUMIDOR,''))                         AS NOME_CONSUMIDOR,
                            TRIM(COALESCE(nc.CPFCONSUMIDOR,''))                           AS CPF_CONSUMIDOR,
                            65                                                            AS MODELO_DOC,
                            TRIM(COALESCE(nc.USUARIO,''))                                 AS OPERADOR
                         FROM NFCE_CABN n
                         LEFT JOIN NOTAS_CAB nc ON nc.NR_NOTA     = n.NR_NOTA
                                                AND nc.COD_EMPRESA = n.COD_EMPRESA
                         WHERE n.IDNFE = ?"
                    );
                    $stNfce->execute([$chave]);
                    $nota = $stNfce->fetch(\PDO::FETCH_ASSOC) ?: null;

                    // Se nao achou (espacos no IDNFE), tenta com TRIM
                    if (!$nota) {
                        $stNfce->closeCursor();
                        $st2 = $pdo->prepare(str_replace('WHERE n.IDNFE = ?', 'WHERE TRIM(n.IDNFE) = ?',
                            "SELECT FIRST 1 n.NR_NOTA AS NR_NOTA_NFCE, nc.NR_NOTA,
                             n.COD_EMPRESA,
                             CAST(COALESCE(n.SERIE_NFCE,n.SERIE,0) AS VARCHAR(5)) AS SERIE,
                             COALESCE(nc.DATA_HORA_VENDA,CAST(n.DATA_EMISSAO AS TIMESTAMP)) AS DATA_HORA_VENDA,
                             n.DATA_EMISSAO,
                             COALESCE(nc.TOTAL_NOTA,CAST(0 AS NUMERIC(15,2))) AS TOTAL_NOTA,
                             COALESCE(nc.TOTAL_PRODUTOS,CAST(0 AS NUMERIC(15,2))) AS TOTAL_PRODUTOS,
                             COALESCE(nc.DESCONTO,CAST(0 AS NUMERIC(15,2))) AS DESCONTO,
                             COALESCE(nc.TOTAL_DINHEIRO,CAST(0 AS NUMERIC(15,2))) AS TOTAL_DINHEIRO,
                             COALESCE(nc.TOTAL_CARTAO,CAST(0 AS NUMERIC(15,2))) AS TOTAL_CARTAO,
                             COALESCE(nc.TOTAL_CHEQUE,CAST(0 AS NUMERIC(15,2))) AS TOTAL_CHEQUE,
                             COALESCE(nc.TOTAL_APRAZO,CAST(0 AS NUMERIC(15,2))) AS TOTAL_APRAZO,
                             nc.COD_SITUACAO,
                             TRIM(COALESCE(nc.NOME_CONSUMIDOR,'')) AS NOME_CONSUMIDOR,
                             TRIM(COALESCE(nc.CPFCONSUMIDOR,'')) AS CPF_CONSUMIDOR,
                             65 AS MODELO_DOC,
                             TRIM(COALESCE(nc.USUARIO,'')) AS OPERADOR
                             FROM NFCE_CABN n
                             LEFT JOIN NOTAS_CAB nc ON nc.NR_NOTA=n.NR_NOTA AND nc.COD_EMPRESA=n.COD_EMPRESA
                             WHERE n.IDNFE = ?"
                        ));
                        $st2->execute([$chave]);
                        $nota = $st2->fetch(\PDO::FETCH_ASSOC) ?: null;
                    }
                } catch (\Throwable) {}
            }

            // NF-e (modelo 55) ou fallback: busca em NOTAS_CAB por NR_NOTA + data
            if (!$nota) {
                $st = $pdo->prepare(
                    "SELECT FIRST 1
                        nc.NR_NOTA,
                        nc.COD_EMPRESA,
                        TRIM(COALESCE(nc.SERIE,'')) AS SERIE,
                        nc.DATA_HORA_VENDA,
                        nc.DATA_EMISSAO,
                        nc.TOTAL_NOTA,
                        nc.TOTAL_PRODUTOS,
                        nc.DESCONTO,
                        nc.TOTAL_DINHEIRO,
                        nc.TOTAL_CARTAO,
                        nc.TOTAL_CHEQUE,
                        nc.TOTAL_APRAZO,
                        nc.COD_SITUACAO,
                        TRIM(COALESCE(nc.NOME_CONSUMIDOR,'')) AS NOME_CONSUMIDOR,
                        TRIM(COALESCE(nc.CPFCONSUMIDOR,''))  AS CPF_CONSUMIDOR,
                        nc.MODELO_DOC,
                        TRIM(COALESCE(nc.USUARIO,''))        AS OPERADOR
                     FROM NOTAS_CAB nc
                     WHERE nc.NR_NOTA = ?
                       AND nc.DATA_EMISSAO BETWEEN ? AND ?"
                );
                $st->execute([$nrNota, $dataIni, $dataFim]);
                $nota = $st->fetch(\PDO::FETCH_ASSOC) ?: null;
            }

            if (!$nota) {
                http_response_code(404);
                echo json_encode(['erro' => 'Nota nao encontrada no sistema']);
                exit;
            }

            // Dados da empresa emitente (cabecalho do cupom)
            $empresa = [];
            try {
                $stEmp = $pdo->prepare(
                    "SELECT FIRST 1
                        TRIM(COALESCE(e.NOME_FANTASIA,'')) AS NOME_FANTASIA,
                        TRIM(COALESCE(e.RAZAO_SOCIAL,''))  AS RAZAO_SOCIAL,
                        TRIM(COALESCE(e.CNPJ,''))          AS CNPJ,
                        TRIM(COALESCE(e.ENDERECO,''))      AS ENDERECO,
                        TRIM(COALESCE(e.BAIRRO,''))        AS BAIRRO,
                        TRIM(COALESCE(e.CIDADE,''))        AS CIDADE,
                        TRIM(COALESCE(e.ESTADO,''))        AS ESTADO
                     FROM EMPRESAS e
                     WHERE e.COD_EMPRESA = ?"
                );
                $stEmp->execute([$nota['COD_EMPRESA']]);
                $empresa = $stEmp->fetch(\PDO::FETCH_ASSOC) ?: [];
            } catch (\Throwable) {
                $empresa = []; // tabela ou colunas inexistentes — sem cabecalho
            }

            // Verifica se o indice de NOTAS_ITENS existe (evita full scan de 60s)
            $stIdx = $pdo->query(
                "SELECT COUNT(*) AS CNT FROM RDB\$INDICES WHERE RDB\$INDEX_NAME = 'IDX_NOTASITENS_NRNOTA'"
            );
            $temIndice = (int)($stIdx->fetch()['CNT'] ?? 0) > 0;

            $itens     = [];
            $semIndice = false;
            // Usa NR_NOTA do NOTAS_CAB se disponivel (NFC-e pode ter NR_NOTA fiscal != interno)
            // NR_NOTA_NFCE = numero fiscal da NFC-e; NR_NOTA = numero interno do NOTAS_CAB
            $nrNotaItens = (int)($nota['NR_NOTA'] ?? $nrNota);
            if ($nrNotaItens <= 0) $nrNotaItens = $nrNota;

            if ($temIndice) {
                // Com indice: query rapida (~10ms)
                $stItens = $pdo->prepare(
                    "SELECT FIRST 500
                        ni.COD_ITEM,
                        TRIM(COALESCE(i.DESCRICAO, ni.COD_ITEM)) AS DESCRICAO,
                        ni.QTD,
                        ni.PRECO_UNIT,
                        (ni.QTD * ni.PRECO_UNIT) AS VL_TOTAL
                     FROM NOTAS_ITENS ni
                     LEFT JOIN ITENS i ON i.COD_ITEM = ni.COD_ITEM
                     WHERE ni.NR_NOTA = ?"
                );
                $stItens->execute([$nrNotaItens]);
                $itens = $stItens->fetchAll(\PDO::FETCH_ASSOC);
                // Se achou menos itens que esperado, tenta tb com NR_NOTA da chave
                if (count($itens) <= 1 && $nrNotaItens !== $nrNota) {
                    $stItens->execute([$nrNota]);
                    $itensAlt = $stItens->fetchAll(\PDO::FETCH_ASSOC);
                    if (count($itensAlt) > count($itens)) $itens = $itensAlt;
                }
            } else {
                $semIndice = true; // avisa o frontend para mostrar instrucao
            }

            echo json_encode([
                'nota'          => $nota,
                'itens'         => $itens,
                'sem_indice'    => $semIndice,
                'empresa'       => $empresa,
                '_nr_nota_itens'=> $nrNotaItens, // debug: NR_NOTA usado para buscar itens
                '_nr_nota_chave'=> $nrNota,       // debug: NR_NOTA extraido da chave
            ], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
        } catch (\Throwable $e) {
            http_response_code(500);
            echo json_encode(['erro' => $e->getMessage()], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
        }
        exit;
    }

    // ── GET /api/leitor/nfce-recentes ────────────────────────────────────────

    public function nfceRecentesApi(): void
    {
        header('Content-Type: application/json; charset=utf-8');
        if (empty($_SESSION['leitor_ok'])) {
            http_response_code(401);
            echo json_encode(['erro' => 'Nao autenticado']);
            exit;
        }
        session_write_close();
        try {
            $pdo = \App\Core\Database::conectar();
            $cfg = self::cfg();
            $emp = (int)($cfg['empresa'] ?? 0);
            $filtro = $emp > 0 ? "AND n.COD_EMPRESA = {$emp}" : '';

            // Tenta NFCE_CABN (tabela principal de NFC-e)
            $rows = [];
            try {
                $st = $pdo->query(
                    "SELECT FIRST 10
                        TRIM(n.IDNFE)             AS IDNFE,
                        n.NR_NOTA,
                        n.DATA_EMISSAO,
                        COALESCE(nc.TOTAL_NOTA, CAST(0 AS NUMERIC(15,2))) AS TOTAL_NOTA
                     FROM NFCE_CABN n
                     LEFT JOIN NOTAS_CAB nc ON nc.NR_NOTA=n.NR_NOTA AND nc.COD_EMPRESA=n.COD_EMPRESA
                     WHERE TRIM(COALESCE(n.IDNFE,'')) <> ''
                       AND n.DATA_CANCELA IS NULL
                       {$filtro}
                     ORDER BY n.DATA_EMISSAO DESC"
                );
                $rows = $st->fetchAll(\PDO::FETCH_ASSOC);
            } catch (\Throwable) {}

            // Fallback: NF-e de NOTAS_CAB
            if (empty($rows)) {
                $filtroNc = $emp > 0 ? "AND nc.COD_EMPRESA = {$emp}" : '';
                $st = $pdo->query(
                    "SELECT FIRST 10
                        TRIM(nc.IDNFE) AS IDNFE,
                        nc.NR_NOTA,
                        nc.DATA_EMISSAO,
                        nc.TOTAL_NOTA
                     FROM NOTAS_CAB nc
                     WHERE nc.NFE='S' AND TRIM(COALESCE(nc.IDNFE,''))<>''
                       {$filtroNc}
                     ORDER BY nc.DATA_HORA_VENDA DESC"
                );
                $rows = $st->fetchAll(\PDO::FETCH_ASSOC);
            }

            echo json_encode(['notas' => $rows], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
        } catch (\Throwable $e) {
            http_response_code(500);
            echo json_encode(['erro' => $e->getMessage()]);
        }
        exit;
    }

    // ── POST /api/leitor/manutencao ───────────────────────────────────────────

    public function manutencaoApi(): void
    {
        header('Content-Type: application/json; charset=utf-8');
        if (empty($_SESSION['leitor_config_ok'])) {
            http_response_code(403);
            echo json_encode(['erro' => 'Acesso negado']);
            exit;
        }
        $acao = trim($_POST['acao'] ?? '');
        if ($acao === 'criar_indice_itens' || $acao === 'criar_indice_nfce') {
            $indice = $acao === 'criar_indice_nfce'
                ? ['nome' => 'IDX_NFCECABN_IDNFE',   'tabela' => 'NFCE_CABN',   'coluna' => 'IDNFE']
                : ['nome' => 'IDX_NOTASITENS_NRNOTA', 'tabela' => 'NOTAS_ITENS', 'coluna' => 'NR_NOTA'];
            try {
                $pdo   = \App\Core\Database::conectar();
                $st    = $pdo->query("SELECT COUNT(*) AS CNT FROM RDB\$INDICES WHERE RDB\$INDEX_NAME = '{$indice['nome']}'");
                $existe = (int)($st->fetch()['CNT'] ?? 0) > 0;
                if ($existe) {
                    echo json_encode(['ok' => true, 'msg' => "Indice {$indice['nome']} ja existia — nenhuma acao necessaria."]);
                    exit;
                }
                $pdo->exec("CREATE INDEX {$indice['nome']} ON {$indice['tabela']} ({$indice['coluna']})");
                echo json_encode(['ok' => true, 'msg' => "Indice {$indice['nome']} criado! Consulta agora sera rapida."]);
            } catch (\Throwable $e) {
                echo json_encode(['ok' => false, 'msg' => $e->getMessage()]);
            }
            exit;
        }
        if ($acao === 'criar_indice_cupom') {
            // Indice descendente para a tela "Ultimos 10 Cupons" (ORDER BY DATA_EMISSAO DESC)
            $nome = 'IDX_NFCECABN_EMPDTEMI';
            try {
                $pdo    = \App\Core\Database::conectar();
                $st     = $pdo->query("SELECT COUNT(*) AS CNT FROM RDB\$INDICES WHERE RDB\$INDEX_NAME = '{$nome}'");
                $existe = (int)($st->fetch()['CNT'] ?? 0) > 0;
                if ($existe) {
                    echo json_encode(['ok' => true, 'msg' => "Indice {$nome} ja existia — busca de cupons ja esta otimizada."]);
                    exit;
                }
                $pdo->exec("CREATE DESCENDING INDEX {$nome} ON NFCE_CABN (COD_EMPRESA, DATA_EMISSAO)");
                echo json_encode(['ok' => true, 'msg' => "Indice {$nome} criado! A tela Ultimos 10 Cupons agora abre instantaneamente."]);
            } catch (\Throwable $e) {
                echo json_encode(['ok' => false, 'msg' => $e->getMessage()]);
            }
            exit;
        }
        http_response_code(400);
        echo json_encode(['erro' => 'Acao desconhecida']);
        exit;
    }

    // ── GET /api/leitor/audit?cod= ────────────────────────────────────────────

    public function auditApi(): void
    {
        if (empty($_SESSION['leitor_ok'])) {
            http_response_code(403);
            echo json_encode(['erro' => 'Não autorizado']);
            exit;
        }
        header('Content-Type: application/json; charset=utf-8');
        $cod  = trim($_GET['cod'] ?? '');
        $entries = self::lerAudit(50, $cod);
        echo json_encode(['entries' => $entries], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
        exit;
    }

    // ── GET /leitor/config ────────────────────────────────────────────────────

    public function config(): void
    {
        $cfg   = self::cfg();
        $erro  = '';
        $salvo = false;

        // Acesso pelo BI: admin autenticado abre a config do leitor (gate unificado com a Configuração).
        // Pelo app (sem sessão admin do BI), continua valendo a senha de config do leitor.
        if (\App\Core\Auth::estaLogado()) {
            $_SESSION['leitor_config_ok'] = true;
        }

        // Autenticação da tela de config via usuário ERP
        if (empty($_SESSION['leitor_config_ok'])) {
            if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['config_usuario'])) {
                $usuario = trim($_POST['config_usuario'] ?? '');
                $senha   = $_POST['config_senha'] ?? '';

                $erpUser = self::autenticarERP($usuario, $senha);

                if ($erpUser) {
                    // Verifica se é admin de config:
                    // Se 'config_admins' estiver vazio na config → qualquer usuário ERP válido entra
                    // Se tiver lista → só os listados entram
                    $admins = $cfg['config_admins'] ?? [];
                    $autorizado = empty($admins)
                        || in_array(strtoupper($erpUser['COD_USUARIO']), array_map('strtoupper', $admins), true);

                    if ($autorizado) {
                        $_SESSION['leitor_config_ok']         = true;
                        $_SESSION['leitor_config_usuario']    = $erpUser['COD_USUARIO'];
                        $_SESSION['leitor_config_nome']       = $erpUser['NOME'];
                        self::registrarAudit('config_acesso', '', [
                            'admin' => $erpUser['COD_USUARIO'],
                        ]);
                    } else {
                        $erro = 'Usuário sem permissão para acessar as configurações.';
                    }
                } else {
                    $erro = 'Usuário ou senha inválidos.';
                    if (!\App\Core\Database::estaConfigurado()) {
                        $erro = 'Banco de dados indisponível. Não é possível autenticar.';
                    }
                }
            }
            if (empty($_SESSION['leitor_config_ok'])) {
                require __DIR__ . '/../Views/leitor/config_login.php';
                return;
            }
        }

        // Salvamento
        if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['salvar'])) {
            // Processa lista de admins (vírgula separada → array limpo)
            $adminsRaw = trim($_POST['config_admins'] ?? '');
            $admins = $adminsRaw === ''
                ? []
                : array_values(array_filter(array_map('trim', explode(',', $adminsRaw))));

            $novo = [
                'pin'           => trim($_POST['pin']      ?? $cfg['pin']),
                'empresa'       => (int)($_POST['empresa'] ?? $cfg['empresa']),
                'ip'            => trim($_POST['ip']       ?? $cfg['ip']),
                'porta'         => trim($_POST['porta']    ?? $cfg['porta']),
                'senha_config'  => $cfg['senha_config'],   // não editável via form
                'permite_pin'   => !empty($_POST['permite_pin']),
                'config_admins' => $admins,
            ];
            if ($novo['pin'] === '') $novo['pin'] = $cfg['pin'];
            if (!preg_match('/^\d{1,5}$/', $novo['porta'])) $novo['porta'] = $cfg['porta'];
            if ($novo['empresa'] < 1) $novo['empresa'] = $cfg['empresa'];

            self::salvarCfg($novo);
            $cfg   = $novo;
            $salvo = true;
        }

        // Log de auditoria para tela de config
        $audit = self::lerAudit(100);

        require __DIR__ . '/../Views/leitor/config.php';
    }

    // ── GET /leitor/config/logout ─────────────────────────────────────────────

    public function configLogout(): void
    {
        unset($_SESSION['leitor_config_ok']);
        header('Location: /leitor/config');
        exit;
    }
}
