<?php
// Depto de Engenharia e Automação - Master Automação - Autor: Derlivon Silva

namespace App\Repositories;

class CozinhaRepository
{
    /** Default: cozinha quente do Fire Sistemas (COD_IMPRESSAO=6) */
    private const DEFAULT_COD_IMPRESSAO_COZINHA = 6;

    /** Janela de tempo pra fila ativa (horas pra trás a partir de agora) */
    private const JANELA_HORAS_FILA = 8;

    public function __construct(private \PDO $pdo) {}

    // ════════════════════════════════════════════════════════════════════════
    //  PERSISTENCIA LOCAL (arquivo JSON em storage/) — NUNCA escreve no ERP
    // ════════════════════════════════════════════════════════════════════════

    private function arquivoFinalizacoes(): string
    {
        return dirname(__DIR__, 2) . '/storage/kds_finalizacoes.json';
    }

    private function arquivoConfig(): string
    {
        return dirname(__DIR__, 2) . '/storage/kds_config.json';
    }

    private function carregarFinalizacoes(): array
    {
        $f = $this->arquivoFinalizacoes();
        if (!file_exists($f)) return [];
        $raw = @file_get_contents($f);
        if ($raw === false || $raw === '') return [];
        $data = json_decode($raw, true);
        return is_array($data['finalizacoes'] ?? null) ? $data['finalizacoes'] : [];
    }

    private function salvarFinalizacoes(array $finalizacoes): void
    {
        $f = $this->arquivoFinalizacoes();
        $dir = dirname($f);
        if (!is_dir($dir)) @mkdir($dir, 0775, true);
        $payload = json_encode([
            'finalizacoes' => $finalizacoes,
            'salvo_em'     => date('Y-m-d H:i:s'),
        ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);

        $tmp = $f . '.tmp';
        file_put_contents($tmp, $payload, LOCK_EX);
        rename($tmp, $f);
    }

    public function carregarConfig(): array
    {
        $defaults = [
            'grupos_ativos'      => [],
            'polling_segundos'   => 10,
            'amarelo_min'        => 5,
            'vermelho_min'       => 15,
            'esquecido_min'      => 30,
            'cod_impressoes'     => [6, 7, 8],   // cozinha quente + sushi + a la carte (cobre maioria)
            'usa_whitelist'      => false,
        ];
        $f = $this->arquivoConfig();
        if (!file_exists($f)) return $defaults;
        $data = json_decode((string)@file_get_contents($f), true);
        if (!is_array($data)) return $defaults;
        // Compat: migra config antiga cod_impressao (int) -> cod_impressoes (array)
        if (isset($data['cod_impressao']) && !isset($data['cod_impressoes'])) {
            $data['cod_impressoes'] = [(int)$data['cod_impressao']];
            unset($data['cod_impressao']);
        }
        return array_merge($defaults, $data);
    }

    public function salvarConfig(array $config): void
    {
        $f = $this->arquivoConfig();
        $dir = dirname($f);
        if (!is_dir($dir)) @mkdir($dir, 0775, true);
        file_put_contents($f, json_encode($config, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), LOCK_EX);
    }

    // ════════════════════════════════════════════════════════════════════════
    //  FILA ATIVA — lê LANC_MESA do ERP (somente leitura)
    // ════════════════════════════════════════════════════════════════════════

    /**
     * Pedidos lançados nas últimas N horas que ainda não foram finalizados
     * (nem no ERP nativo via PRONTO='S', nem no BI via kds_finalizacoes.json).
     */
    public function fila(?int $codEmpresa = null): array
    {
        $cfg = $this->carregarConfig();
        $codEmpresa = $codEmpresa ?: $this->codEmpresaConfig();

        // Filtro de cozinha: whitelist (se ativada) OU multipos COD_IMPRESSAO
        $filtroCozinha = '';
        $params = [];
        if (!empty($cfg['usa_whitelist']) && !empty($cfg['grupos_ativos'])) {
            $placeholders = implode(',', array_fill(0, count($cfg['grupos_ativos']), '?'));
            $filtroCozinha = " AND i.COD_ESTRUTURA IN ($placeholders) ";
            $params = array_map('strval', $cfg['grupos_ativos']);
        } else {
            $impressoes = $cfg['cod_impressoes'] ?? [6, 7, 8];
            $impressoes = array_values(array_filter(array_map('intval', $impressoes)));
            if (!$impressoes) $impressoes = [6];
            $placeholders = implode(',', array_fill(0, count($impressoes), '?'));
            $filtroCozinha = " AND e.COD_IMPRESSAO IN ($placeholders) ";
            $params = $impressoes;
        }

        $janela = self::JANELA_HORAS_FILA;
        $sql = "SELECT
                  lm.COD_LANC,
                  lm.COD_ABERTURA,
                  lm.COD_MESA,
                  lm.COD_EMPRESA,
                  lm.COD_PRODUTO,
                  COALESCE(lm.DESCRICAO, i.DESCRICAO) AS PRODUTO,
                  CAST(lm.QTD AS NUMERIC(18,4)) AS QTD,
                  lm.OBSERVACAO,
                  lm.OBS2,
                  lm.COD_GARCOM,
                  COALESCE(p.NOME, '') AS GARCOM,
                  lm.DATAHORAMOV,
                  lm.IMPRESSO,
                  i.COD_ESTRUTURA,
                  e.DESCRICAO AS GRUPO
                FROM LANC_MESA lm
                JOIN ITENS     i ON i.COD_ITEM       = lm.COD_PRODUTO
                LEFT JOIN ESTRUTURA e ON e.COD_ESTRUTURA = i.COD_ESTRUTURA
                LEFT JOIN GARCOM  g ON g.COD_GARCOM = lm.COD_GARCOM
                LEFT JOIN PESSOAS p ON p.COD_PESSOA = g.COD_PESSOA
                WHERE lm.COD_EMPRESA = ?
                  AND lm.DATAHORAMOV >= DATEADD(-{$janela} HOUR TO CURRENT_TIMESTAMP)
                  AND (lm.PRONTO IS NULL OR lm.PRONTO <> 'S')
                  $filtroCozinha
                ORDER BY lm.DATAHORAMOV ASC";

        $st = $this->pdo->prepare($sql);
        $st->execute(array_merge([$codEmpresa], $params));
        $linhas = $st->fetchAll(\PDO::FETCH_ASSOC) ?: [];

        // Remove os já finalizados no BI
        $finalizadas = $this->carregarFinalizacoes();
        $chavesFinalizadas = [];
        foreach ($finalizadas as $fin) {
            $k = $fin['cod_empresa'] . '|' . $fin['cod_abertura'] . '|' . $fin['cod_lanc'];
            $chavesFinalizadas[$k] = true;
        }

        $agora = time();
        $out = [];
        foreach ($linhas as $r) {
            $k = $r['COD_EMPRESA'] . '|' . $r['COD_ABERTURA'] . '|' . $r['COD_LANC'];
            if (isset($chavesFinalizadas[$k])) continue;

            $tLanc = strtotime((string)$r['DATAHORAMOV']);
            $minNaFila = $tLanc ? (int)floor(($agora - $tLanc) / 60) : 0;

            $r['MIN_NA_FILA'] = $minNaFila;
            $r['SEMAFORO']    = $this->semaforo($minNaFila, $cfg);
            $r['PRODUTO']     = $this->toUtf8($r['PRODUTO']);
            $r['OBSERVACAO']  = $this->toUtf8($r['OBSERVACAO']);
            $r['OBS2']        = $this->toUtf8($r['OBS2']);
            $r['GARCOM']      = $this->toUtf8($r['GARCOM']);
            $r['GRUPO']       = $this->toUtf8($r['GRUPO']);
            $out[] = $r;
        }
        return $out;
    }

    private function semaforo(int $minutos, array $cfg): string
    {
        if ($minutos >= (int)$cfg['esquecido_min']) return 'esquecido';
        if ($minutos >= (int)$cfg['vermelho_min']) return 'vermelho';
        if ($minutos >= (int)$cfg['amarelo_min'])  return 'amarelo';
        return 'verde';
    }

    /**
     * Grava finalização em DOIS lugares (autorizado pelo Derlivon 2026-06-16):
     *  1) ERP Fire Sistemas — preenche campos nativos PRONTOFIM/PRONTO/PRONTOQUEM/PRODUINI
     *     (LANC_MESA. Os campos JÁ existem no schema da Fire — só não estavam
     *      sendo usados. Permanente, consultável fora do BI.)
     *  2) Arquivo JSON local do BI — backup/fallback se VPN cair na hora do clique.
     *
     * EXCEÇÃO À REGRA READ-ONLY: única função que escreve no banco do cliente.
     * Foco: gravar o tempo do prato (DATAHORAMOV → PRONTOFIM) de forma permanente.
     */
    public function finalizar(int $codEmpresa, int $codAbertura, int $codLanc): array
    {
        // Snapshot do lançamento no ERP — pega DATAHORAMOV pra calcular tempo no relatório
        $st = $this->pdo->prepare("
            SELECT FIRST 1
                   lm.COD_EMPRESA, lm.COD_ABERTURA, lm.COD_LANC,
                   lm.COD_MESA, lm.COD_PRODUTO,
                   COALESCE(lm.DESCRICAO, i.DESCRICAO) AS PRODUTO,
                   CAST(lm.QTD AS NUMERIC(18,4)) AS QTD,
                   lm.OBSERVACAO, lm.OBS2,
                   COALESCE(p.NOME, '') AS GARCOM,
                   lm.DATAHORAMOV,
                   i.COD_ESTRUTURA,
                   COALESCE(e.DESCRICAO, '') AS GRUPO
              FROM LANC_MESA lm
              LEFT JOIN ITENS     i ON i.COD_ITEM       = lm.COD_PRODUTO
              LEFT JOIN ESTRUTURA e ON e.COD_ESTRUTURA  = i.COD_ESTRUTURA
              LEFT JOIN GARCOM    g ON g.COD_GARCOM    = lm.COD_GARCOM
              LEFT JOIN PESSOAS   p ON p.COD_PESSOA    = g.COD_PESSOA
             WHERE lm.COD_EMPRESA  = ?
               AND lm.COD_ABERTURA = ?
               AND lm.COD_LANC     = ?");
        $st->execute([$codEmpresa, $codAbertura, $codLanc]);
        $snap = $st->fetch(\PDO::FETCH_ASSOC);

        if (!$snap) {
            return ['ok' => false, 'erro' => 'Lançamento não encontrado no ERP'];
        }

        $dataLanc = (string)$snap['DATAHORAMOV'];
        $tLanc    = strtotime($dataLanc) ?: time();
        $agora    = time();
        $tempoMin = (int)floor(($agora - $tLanc) / 60);

        $registro = [
            'cod_empresa'  => (int)$codEmpresa,
            'cod_abertura' => (int)$codAbertura,
            'cod_lanc'     => (int)$codLanc,
            'cod_mesa'     => (string)$snap['COD_MESA'],
            'cod_produto'  => (int)$snap['COD_PRODUTO'],
            'produto'      => $this->toUtf8($snap['PRODUTO']),
            'qtd'          => (float)$snap['QTD'],
            'observacao'   => $this->toUtf8($snap['OBSERVACAO']),
            'garcom'       => $this->toUtf8($snap['GARCOM']),
            'grupo'        => $this->toUtf8($snap['GRUPO']),
            'cod_estrutura'=> (string)$snap['COD_ESTRUTURA'],
            'data_lanc'    => $dataLanc,
            'data_pronto'  => date('Y-m-d H:i:s', $agora),
            'tempo_min'    => $tempoMin,
            'retirado_em'  => null,   // garçom marca quando passa no balcão
        ];

        $finalizadas = $this->carregarFinalizacoes();

        // Idempotente: se já tem essa chave, não duplica
        foreach ($finalizadas as $fin) {
            if ($fin['cod_empresa'] === $registro['cod_empresa']
                && $fin['cod_abertura'] === $registro['cod_abertura']
                && $fin['cod_lanc'] === $registro['cod_lanc']) {
                return ['ok' => true, 'duplicado' => true, 'registro' => $fin];
            }
        }

        $finalizadas[] = $registro;
        $this->salvarFinalizacoes($finalizadas);

        // ── ESCRITA NO ERP FIRE SISTEMAS ─────────────────────────────────────
        // Preenche os campos KDS nativos da LANC_MESA. Esses campos já existem
        // no schema da Fire (são da própria Fire — só não estavam sendo usados).
        // Autorizado por Derlivon em 2026-06-16 como única exceção à regra read-only.
        // Try/catch: se UPDATE falhar (VPN caiu, lock, etc), JSON local já foi
        // salvo e a operação NÃO é desfeita — pode reconciliar depois.
        $erpOk = false;
        $erpErro = null;
        try {
            $up = $this->pdo->prepare("
                UPDATE LANC_MESA
                   SET PRONTOFIM = CURRENT_TIMESTAMP,
                       PRONTO     = 'S',
                       PRONTOQUEM = ?,
                       PRODUINI   = COALESCE(PRODUINI, DATAHORAMOV),
                       PRODUZINDO = 'N'
                 WHERE COD_EMPRESA  = ?
                   AND COD_ABERTURA = ?
                   AND COD_LANC     = ?");
            $up->execute(['Cozinha BI', $codEmpresa, $codAbertura, $codLanc]);
            $erpOk = $up->rowCount() > 0;
            if (!$erpOk) $erpErro = 'UPDATE não afetou nenhuma linha';
        } catch (\Throwable $e) {
            $erpErro = $e->getMessage();
            error_log('[cozinha] UPDATE LANC_MESA falhou: ' . $erpErro);
        }

        $registro['erp_ok']  = $erpOk;
        if ($erpErro) $registro['erp_erro'] = $erpErro;

        return ['ok' => true, 'registro' => $registro, 'erp_ok' => $erpOk];
    }

    /**
     * Lista pratos prontos pendentes de retirada pelo garçom.
     * Limita a últimas N minutos pra evitar acúmulo se o display ficou off.
     */
    public function avisosPendentes(int $janelaMin = 10): array
    {
        $finalizadas = $this->carregarFinalizacoes();
        $limite = time() - ($janelaMin * 60);
        $out = [];
        $agora = time();
        foreach ($finalizadas as $f) {
            if (!empty($f['retirado_em'])) continue;
            $t = strtotime($f['data_pronto'] ?? '');
            if (!$t || $t < $limite) continue;
            $seg = max(0, $agora - $t);
            $f['seg_pronto'] = $seg;
            $out[] = $f;
        }
        // Mais antigos primeiro (já esperando há mais tempo)
        usort($out, fn($a, $b) => strcmp((string)($a['data_pronto'] ?? ''), (string)($b['data_pronto'] ?? '')));
        return $out;
    }

    /** Garçom marca que retirou o prato — aviso some do display. */
    public function marcarRetirado(int $codEmpresa, int $codAbertura, int $codLanc, string $retiradoPor = ''): array
    {
        $finalizadas = $this->carregarFinalizacoes();
        $achou = false;
        $retiradoPor = trim($retiradoPor);
        foreach ($finalizadas as &$f) {
            if ((int)$f['cod_empresa']  === $codEmpresa
                && (int)$f['cod_abertura'] === $codAbertura
                && (int)$f['cod_lanc']     === $codLanc
                && empty($f['retirado_em'])) {
                $f['retirado_em']  = date('Y-m-d H:i:s');
                $f['retirado_por'] = $retiradoPor ?: 'Balcao';
                $achou = true;
                break;
            }
        }
        unset($f);
        if ($achou) {
            $this->salvarFinalizacoes($finalizadas);
            return ['ok' => true];
        }
        return ['ok' => false, 'erro' => 'Aviso não encontrado ou já retirado'];
    }

    /** Lista garçons ativos do ERP (pra modal Retirei). */
    public function listarGarcons(): array
    {
        $sql = "SELECT g.COD_GARCOM, COALESCE(TRIM(p.NOME), '') AS NOME
                  FROM GARCOM g
                  LEFT JOIN PESSOAS p ON p.COD_PESSOA = g.COD_PESSOA
                 WHERE COALESCE(p.ATIVO,'S') <> 'N'
                   AND COALESCE(p.EGARCOM,'S') = 'S'
                 ORDER BY p.NOME";
        try {
            $rows = $this->pdo->query($sql)->fetchAll(\PDO::FETCH_ASSOC) ?: [];
        } catch (\Throwable $e) {
            // Fallback: listar todos da GARCOM mesmo sem JOIN com PESSOAS
            $rows = $this->pdo->query("SELECT FIRST 50 g.COD_GARCOM, COALESCE(TRIM(p.NOME),'') AS NOME
                                         FROM GARCOM g
                                         LEFT JOIN PESSOAS p ON p.COD_PESSOA = g.COD_PESSOA
                                        ORDER BY p.NOME")->fetchAll(\PDO::FETCH_ASSOC) ?: [];
        }
        foreach ($rows as &$r) {
            $r['NOME'] = $this->toUtf8($r['NOME']) ?: ('Garcom #' . $r['COD_GARCOM']);
        }
        return $rows;
    }

    /**
     * Desfaz a última finalização — usado pra corrigir clique acidental.
     */
    public function desfazer(int $codEmpresa, int $codAbertura, int $codLanc): array
    {
        $finalizadas = $this->carregarFinalizacoes();
        $antes = count($finalizadas);
        $finalizadas = array_values(array_filter($finalizadas, function ($f) use ($codEmpresa, $codAbertura, $codLanc) {
            return !($f['cod_empresa'] === $codEmpresa
                && $f['cod_abertura'] === $codAbertura
                && $f['cod_lanc'] === $codLanc);
        }));
        $depois = count($finalizadas);
        if ($depois < $antes) {
            $this->salvarFinalizacoes($finalizadas);
            return ['ok' => true, 'removidos' => $antes - $depois];
        }
        return ['ok' => false, 'erro' => 'Não havia finalização para esse lançamento'];
    }

    // ════════════════════════════════════════════════════════════════════════
    //  RELATÓRIO — tempo de preparo agregado por dia/produto/cozinheiro
    // ════════════════════════════════════════════════════════════════════════

    public function relatorio(string $dataIni, string $dataFim): array
    {
        $finalizadas = $this->carregarFinalizacoes();

        // Filtra pelo range de data_pronto
        $iniTs = strtotime($dataIni . ' 00:00:00');
        $fimTs = strtotime($dataFim . ' 23:59:59');
        $finalizadas = array_filter($finalizadas, function ($f) use ($iniTs, $fimTs) {
            $t = strtotime($f['data_pronto'] ?? '');
            return $t && $t >= $iniTs && $t <= $fimTs;
        });

        $tempos = array_map(fn($f) => (int)$f['tempo_min'], $finalizadas);

        $kpis = [
            'qtd_finalizados'  => count($finalizadas),
            'media_min'        => $tempos ? array_sum($tempos) / count($tempos) : 0,
            'p50_min'          => $this->percentil($tempos, 50),
            'p90_min'          => $this->percentil($tempos, 90),
            'maior_min'        => $tempos ? max($tempos) : 0,
            'menor_min'        => $tempos ? min($tempos) : 0,
        ];

        // Top produtos por tempo médio (mín 3 ocorrências pra estabilidade)
        $porProduto = [];
        foreach ($finalizadas as $f) {
            $p = $f['produto'] ?: '(sem nome)';
            $porProduto[$p][] = (int)$f['tempo_min'];
        }
        $rankingProdutos = [];
        foreach ($porProduto as $produto => $arr) {
            if (count($arr) < 1) continue;
            $rankingProdutos[] = [
                'produto'    => $produto,
                'qtd'        => count($arr),
                'media_min'  => array_sum($arr) / count($arr),
                'p50_min'    => $this->percentil($arr, 50),
                'maior_min'  => max($arr),
            ];
        }
        usort($rankingProdutos, fn($a, $b) => $b['media_min'] <=> $a['media_min']);
        $rankingProdutos = array_slice($rankingProdutos, 0, 30);

        // Por grupo (categoria do prato)
        $porGrupo = [];
        foreach ($finalizadas as $f) {
            $g = $f['grupo'] ?: '(sem grupo)';
            $porGrupo[$g][] = (int)$f['tempo_min'];
        }
        $rankingGrupos = [];
        foreach ($porGrupo as $grupo => $arr) {
            $rankingGrupos[] = [
                'grupo'     => $grupo,
                'qtd'       => count($arr),
                'media_min' => array_sum($arr) / count($arr),
                'p50_min'   => $this->percentil($arr, 50),
            ];
        }
        usort($rankingGrupos, fn($a, $b) => $b['media_min'] <=> $a['media_min']);

        // Por garçom (quem lança mais e o tempo médio dos pedidos dele)
        $porGarcom = [];
        foreach ($finalizadas as $f) {
            $g = trim((string)($f['garcom'] ?? '')) ?: '(sem garçom)';
            $porGarcom[$g][] = (int)$f['tempo_min'];
        }
        $rankingGarcons = [];
        foreach ($porGarcom as $garcom => $arr) {
            $rankingGarcons[] = [
                'garcom'    => $garcom,
                'qtd'       => count($arr),
                'media_min' => array_sum($arr) / count($arr),
            ];
        }
        usort($rankingGarcons, fn($a, $b) => $b['qtd'] <=> $a['qtd']);

        // Por quem retirou (passou no balcão pra levar até a mesa)
        $porRetirante = [];
        foreach ($finalizadas as $f) {
            if (empty($f['retirado_em']) || empty($f['retirado_por'])) continue;
            $g = trim((string)$f['retirado_por']);
            if ($g === '' || $g === 'Balcao') continue;
            // tempo de balcão = retirado_em - data_pronto (minutos)
            $tProto = strtotime((string)$f['data_pronto']);
            $tRet   = strtotime((string)$f['retirado_em']);
            $espera = ($tProto && $tRet) ? max(0, (int)floor(($tRet - $tProto) / 60)) : 0;
            if (!isset($porRetirante[$g])) {
                $porRetirante[$g] = ['qtd' => 0, 'soma_espera' => 0];
            }
            $porRetirante[$g]['qtd']++;
            $porRetirante[$g]['soma_espera'] += $espera;
        }
        $rankingRetirantes = [];
        foreach ($porRetirante as $garcom => $info) {
            $rankingRetirantes[] = [
                'garcom'         => $garcom,
                'qtd'            => $info['qtd'],
                'espera_media_min' => $info['qtd'] > 0 ? $info['soma_espera'] / $info['qtd'] : 0,
            ];
        }
        usort($rankingRetirantes, fn($a, $b) => $b['qtd'] <=> $a['qtd']);

        // Por hora do dia
        $porHora = array_fill(0, 24, ['qtd' => 0, 'soma' => 0]);
        foreach ($finalizadas as $f) {
            $t = strtotime($f['data_lanc'] ?? '');
            if (!$t) continue;
            $h = (int)date('G', $t);
            $porHora[$h]['qtd']++;
            $porHora[$h]['soma'] += (int)$f['tempo_min'];
        }
        $horas = [];
        foreach ($porHora as $h => $info) {
            $horas[] = [
                'hora'      => sprintf('%02dh', $h),
                'qtd'       => $info['qtd'],
                'media_min' => $info['qtd'] > 0 ? $info['soma'] / $info['qtd'] : 0,
            ];
        }

        // Distribuição em faixas (histograma)
        $faixas = [
            ['label' => '0-5 min',   'min' => 0,  'max' => 5,   'qtd' => 0],
            ['label' => '5-10 min',  'min' => 5,  'max' => 10,  'qtd' => 0],
            ['label' => '10-15 min', 'min' => 10, 'max' => 15,  'qtd' => 0],
            ['label' => '15-20 min', 'min' => 15, 'max' => 20,  'qtd' => 0],
            ['label' => '20-30 min', 'min' => 20, 'max' => 30,  'qtd' => 0],
            ['label' => '30-45 min', 'min' => 30, 'max' => 45,  'qtd' => 0],
            ['label' => '45+ min',   'min' => 45, 'max' => 9999,'qtd' => 0],
        ];
        foreach ($tempos as $t) {
            foreach ($faixas as $i => $f) {
                if ($t >= $f['min'] && $t < $f['max']) { $faixas[$i]['qtd']++; break; }
            }
        }

        // Lista detalhada (pra drill-down)
        $detalhe = array_values($finalizadas);
        usort($detalhe, fn($a, $b) => strcmp((string)$b['data_pronto'], (string)$a['data_pronto']));

        return [
            'kpis'               => $kpis,
            'ranking_produtos'   => $rankingProdutos,
            'ranking_grupos'     => $rankingGrupos,
            'ranking_garcons'    => $rankingGarcons,
            'ranking_retirantes' => $rankingRetirantes,
            'por_hora'           => $horas,
            'faixas'             => $faixas,
            'detalhe'            => array_slice($detalhe, 0, 200),
            'data_ini'           => $dataIni,
            'data_fim'           => $dataFim,
        ];
    }

    private function percentil(array $valores, int $p): float
    {
        if (!$valores) return 0;
        sort($valores);
        $idx = ($p / 100) * (count($valores) - 1);
        $lo = (int)floor($idx);
        $hi = (int)ceil($idx);
        if ($lo === $hi) return (float)$valores[$lo];
        $frac = $idx - $lo;
        return $valores[$lo] + ($valores[$hi] - $valores[$lo]) * $frac;
    }

    // ════════════════════════════════════════════════════════════════════════
    //  CONFIG — lista de grupos pra tela /cozinha/config (whitelist)
    // ════════════════════════════════════════════════════════════════════════

    /**
     * Lista os destinos de impressão (COD_IMPRESSAO) usados no ERP do cliente
     * com quantos lançamentos cada um teve nos últimos N dias. Pra tela de
     * config: cliente vê visualmente onde estão as porções, sushi, A LA CARTE,
     * bar etc. e marca quais vão pra fila da cozinha.
     */
    public function listarDestinos(int $codEmpresa, int $diasJanela = 30): array
    {
        $sql = "SELECT e.COD_IMPRESSAO,
                       COUNT(DISTINCT e.COD_ESTRUTURA) AS QTD_GRUPOS,
                       LIST(DISTINCT TRIM(e.DESCRICAO), ', ')      AS GRUPOS,
                       COALESCE(SUM(c.QTD_LANC), 0)               AS QTD_LANC
                FROM ESTRUTURA e
                LEFT JOIN (
                  SELECT i.COD_ESTRUTURA, COUNT(lm.COD_LANC) AS QTD_LANC
                    FROM LANC_MESA lm
                    JOIN ITENS i ON i.COD_ITEM = lm.COD_PRODUTO
                   WHERE lm.COD_EMPRESA = ?
                     AND lm.DATAHORAMOV >= DATEADD(-{$diasJanela} DAY TO CURRENT_TIMESTAMP)
                   GROUP BY i.COD_ESTRUTURA
                ) c ON c.COD_ESTRUTURA = e.COD_ESTRUTURA
                WHERE e.COD_IMPRESSAO IS NOT NULL
                GROUP BY e.COD_IMPRESSAO
                ORDER BY 4 DESC";
        $st = $this->pdo->prepare($sql);
        $st->execute([$codEmpresa]);
        $rows = $st->fetchAll(\PDO::FETCH_ASSOC) ?: [];
        foreach ($rows as &$r) {
            $r['GRUPOS'] = $this->toUtf8($r['GRUPOS']);
        }
        return $rows;
    }

    public function listarGrupos(int $codEmpresa, int $diasJanela = 30): array
    {
        $sql = "SELECT
                  e.COD_ESTRUTURA,
                  e.DESCRICAO,
                  e.COD_IMPRESSAO,
                  COUNT(lm.COD_LANC) AS QTD_LANC
                FROM ESTRUTURA e
                LEFT JOIN ITENS     i ON i.COD_ESTRUTURA = e.COD_ESTRUTURA
                LEFT JOIN LANC_MESA lm ON lm.COD_PRODUTO = i.COD_ITEM
                     AND lm.COD_EMPRESA = ?
                     AND lm.DATAHORAMOV >= DATEADD(-{$diasJanela} DAY TO CURRENT_TIMESTAMP)
                GROUP BY e.COD_ESTRUTURA, e.DESCRICAO, e.COD_IMPRESSAO
                ORDER BY QTD_LANC DESC, e.DESCRICAO";
        $st = $this->pdo->prepare($sql);
        $st->execute([$codEmpresa]);
        $rows = $st->fetchAll(\PDO::FETCH_ASSOC) ?: [];
        foreach ($rows as &$r) {
            $r['DESCRICAO'] = $this->toUtf8($r['DESCRICAO']);
        }
        return $rows;
    }

    // ════════════════════════════════════════════════════════════════════════
    //  HELPERS
    // ════════════════════════════════════════════════════════════════════════

    private function codEmpresaConfig(): int
    {
        $path = dirname(__DIR__, 2) . '/config/database.php';
        if (file_exists($path)) {
            $cfg = require $path;
            if (!empty($cfg['cod_empresa'])) return (int)$cfg['cod_empresa'];
        }
        return 1;
    }

    private function toUtf8(?string $s): string
    {
        if ($s === null) return '';
        if (mb_detect_encoding($s, 'UTF-8', true)) return trim($s);
        return trim(mb_convert_encoding($s, 'UTF-8', 'Windows-1252'));
    }
}
