<?php
/**
 * Depto de Engenharia e Automação - Master Automação - Autor: Derlivon Silva
 * (parte do Premium Express BI - Fire Sistemas)
 */

namespace App\Controllers;

use App\Core\Database;
use App\Core\View;
use App\Repositories\VendasRepository;

class VendasController
{
    private function repo(): VendasRepository
    {
        return new VendasRepository(Database::conectar());
    }

    private function config(): array
    {
        $path = dirname(__DIR__, 2) . '/config/database.php';
        return file_exists($path) ? (require $path) : [];
    }

    private function guardaConexao(): void
    {
        if (!Database::estaConfigurado()) {
            header('Location: /configurar'); exit;
        }
    }

    private function tentar(callable $fn, mixed $padrao, array &$erros): mixed
    {
        try {
            return $fn();
        } catch (\Throwable $e) {
            $erros[] = $e->getMessage();
            return $padrao;
        }
    }

    // ── Filtros SEM consulta ao banco — só session + GET ─────────────────────
    // Usado para render do shell: TTFB < 15ms, sem conexão Firebird.
    private function filtrosSemDB(): array
    {
        $hoje    = date('Y-m-d');
        $sessAno = isset($_SESSION['filtro_ano']) ? (int)$_SESSION['filtro_ano'] : null;
        $sessMes = isset($_SESSION['filtro_mes']) && (int)$_SESSION['filtro_mes'] > 0
                   ? (int)$_SESSION['filtro_mes'] : null;

        // v1.4.9: módulo Vendas SEMPRE inicia em HOJE quando entra sem filtro explícito.
        // Sessão de mês/ano só é respeitada se houver ?data_ini= ou ?data_fim= explícito
        // na URL (sinal de navegação intencional pra esse período).
        $temGetData = isset($_GET['data_ini']) || isset($_GET['data_fim']);

        if ($temGetData && $sessAno && $sessMes) {
            $defaultIni = sprintf('%04d-%02d-01', $sessAno, $sessMes);
            $fimDoMes   = date('Y-m-t', strtotime($defaultIni));
            $defaultFim = min($fimDoMes, $hoje);
            if ($defaultIni > $hoje) { $defaultIni = $defaultFim = $hoje; }
        } elseif ($temGetData && $sessAno) {
            $defaultIni = sprintf('%04d-01-01', $sessAno);
            $defaultFim = min(sprintf('%04d-12-31', $sessAno), $hoje);
        } else {
            // Default novo (v1.4.9): HOJE → HOJE (era 'Y-m-01' → hoje, pegava o mês inteiro)
            $defaultIni = $hoje;
            $defaultFim = $hoje;
        }

        $dataIni = $_GET['data_ini'] ?? $defaultIni;
        $dataFim = $_GET['data_fim'] ?? $defaultFim;
        if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataIni)) $dataIni = $defaultIni;
        if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataFim))  $dataFim = $defaultFim;
        if ($dataFim < $dataIni) $dataFim = $dataIni;

        $anoSel   = (int)substr($dataIni, 0, 4);
        $anoAtual = (int)date('Y');
        // Anos estáticos (últimos 5) — API retorna anos reais e JS atualiza as abas
        $anos = range($anoAtual, max(2020, $anoAtual - 4));

        // Return snake_case keys to match vendas/_filtro.php expectations
        return [
            'data_ini' => $dataIni,
            'data_fim' => $dataFim,
            'dataIni'  => $dataIni,  // alias for JS injection
            'dataFim'  => $dataFim,  // alias for JS injection
            'anos'     => $anos,
            'anoSel'   => $anoSel,
            'vendedor' => null,
            'vendedores' => [],
            'usuario'    => null,
            'usuarios'   => [],
        ];
    }

    // ── API helper: parse e valida datas do GET ───────────────────────────────
    private function parseDatas(string $ultimaFallback): array
    {
        $dataIni = $_GET['data_ini'] ?? date('Y-m-01', strtotime($ultimaFallback));
        $dataFim = $_GET['data_fim'] ?? $ultimaFallback;
        if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataIni)) $dataIni = date('Y-m-01', strtotime($ultimaFallback));
        if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataFim))  $dataFim = $ultimaFallback;
        if ($dataFim < $dataIni) $dataFim = $dataIni;
        return [$dataIni, $dataFim];
    }

    // ════════════════════════════════════════════════════════════════════════
    //  PÁGINAS — shell instantâneo, sem queries no render
    // ════════════════════════════════════════════════════════════════════════

    public function gestao(): void
    {
        $this->guardaConexao();
        $config  = $this->config();
        $filtros = $this->filtrosSemDB();

        View::render('vendas/gestao', [
            'titulo'   => 'Gestão de Vendas — Premium Express Gestão',
            'config'   => $config,
            'conexao'  => ['success' => true, 'mensagem' => ''],
            'segmento' => \App\Core\Lojas::resolverSegmento($config),
            'filtros'  => $filtros,
            'erros'    => [],
            'ano'      => date('Y'),
        ]);
    }

    public function diarias(): void
    {
        $this->guardaConexao();
        $config  = $this->config();
        $filtros = $this->filtrosSemDB();

        View::render('vendas/diarias', [
            'titulo'   => 'Vendas Diárias — Premium Express Gestão',
            'config'   => $config,
            'conexao'  => ['success' => true, 'mensagem' => ''],
            'segmento' => \App\Core\Lojas::resolverSegmento($config),
            'filtros'  => $filtros,
            'erros'    => [],
            'ano'      => date('Y'),
        ]);
    }

    public function mensais(): void
    {
        $this->guardaConexao();
        $config  = $this->config();
        $filtros = $this->filtrosSemDB();

        $anoSel = isset($_GET['ano']) && ctype_digit($_GET['ano'])
                  ? (int)$_GET['ano']
                  : (int)substr($filtros['data_fim'] ?? date('Y-m-d'), 0, 4);

        View::render('vendas/mensais', [
            'titulo'   => 'Vendas Mensais — Premium Express Gestão',
            'config'   => $config,
            'conexao'  => ['success' => true, 'mensagem' => ''],
            'segmento' => \App\Core\Lojas::resolverSegmento($config),
            'filtros'  => $filtros,
            'anoSel'   => $anoSel,
            'erros'    => [],
            'ano'      => date('Y'),
        ]);
    }

    public function porVendedor(): void
    {
        $this->guardaConexao();
        $config  = $this->config();
        $filtros = $this->filtrosSemDB();

        View::render('vendas/por-vendedor', [
            'titulo'   => 'Vendas por Vendedor — Premium Express Gestão',
            'config'   => $config,
            'conexao'  => ['success' => true, 'mensagem' => ''],
            'segmento' => \App\Core\Lojas::resolverSegmento($config),
            'filtros'  => $filtros,
            'erros'    => [],
            'ano'      => date('Y'),
        ]);
    }

    public function porGrupo(): void
    {
        $this->guardaConexao();
        $config  = $this->config();
        $filtros = $this->filtrosSemDB();

        View::render('vendas/por-grupo', [
            'titulo'   => 'Vendas por Grupo — Premium Express Gestão',
            'config'   => $config,
            'conexao'  => ['success' => true, 'mensagem' => ''],
            'segmento' => \App\Core\Lojas::resolverSegmento($config),
            'filtros'  => $filtros,
            'erros'    => [],
            'ano'      => date('Y'),
        ]);
    }

    public function porMarca(): void
    {
        $this->guardaConexao();
        $config  = $this->config();
        $filtros = $this->filtrosSemDB();

        View::render('vendas/por-marca', [
            'titulo'   => 'Vendas por Marca — Premium Express Gestão',
            'config'   => $config,
            'conexao'  => ['success' => true, 'mensagem' => ''],
            'segmento' => \App\Core\Lojas::resolverSegmento($config),
            'filtros'  => $filtros,
            'erros'    => [],
            'ano'      => date('Y'),
        ]);
    }

    public function notasSaida(): void
    {
        $this->guardaConexao();
        $config  = $this->config();
        $filtros = $this->filtrosSemDB();

        View::render('vendas/notas-saida', [
            'titulo'   => 'Notas de Saída — Premium Express Gestão',
            'config'   => $config,
            'conexao'  => ['success' => true, 'mensagem' => ''],
            'segmento' => \App\Core\Lojas::resolverSegmento($config),
            'filtros'  => $filtros,
            'erros'    => [],
            'ano'      => date('Y'),
        ]);
    }

    public function preNotas(): void
    {
        $this->guardaConexao();
        $config  = $this->config();
        $filtros = $this->filtrosSemDB();

        View::render('vendas/pre-notas', [
            'titulo'   => 'Pré-Notas — Premium Express Gestão',
            'config'   => $config,
            'conexao'  => ['success' => true, 'mensagem' => ''],
            'segmento' => \App\Core\Lojas::resolverSegmento($config),
            'filtros'  => $filtros,
            'erros'    => [],
            'ano'      => date('Y'),
        ]);
    }

    // ════════════════════════════════════════════════════════════════════════
    //  APIs JSON — dados carregados pelo browser após o shell aparecer
    // ════════════════════════════════════════════════════════════════════════

    /** GET /api/vendas/kpis?data_ini=&data_fim= */
    public function kpisGestaoApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo   = $this->repo();
            $ultima = $repo->ultimaData();
            [$dataIni, $dataFim] = $this->parseDatas($ultima);
            $kpis = $repo->kpisGestao($dataIni, $dataFim);
            $anos = $repo->anosDisponiveis();
            echo json_encode([
                'kpis'       => $kpis,
                'anos'       => $anos,
                'ultimaData' => $ultima,
                'dataIni'    => $dataIni,
                'dataFim'    => $dataFim,
            ], 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/vendas/diarias?data_ini=&data_fim= */
    public function diariasApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo   = $this->repo();
            $ultima = $repo->ultimaData();
            // Default: últimos 30 dias
            $dataFim = $_GET['data_fim'] ?? $ultima;
            $dataIni = $_GET['data_ini'] ?? date('Y-m-d', strtotime($dataFim . ' -29 days'));
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataIni)) $dataIni = date('Y-m-d', strtotime($dataFim . ' -29 days'));
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataFim)) $dataFim = $ultima;
            if ($dataFim < $dataIni) $dataFim = $dataIni;

            $linhas = $repo->vendasDiarias($dataIni, $dataFim);
            $anos   = $repo->anosDisponiveis();
            echo json_encode([
                'linhas'  => $linhas,
                'anos'    => $anos,
                'dataIni' => $dataIni,
                'dataFim' => $dataFim,
            ], 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/vendas/mensais?ano= */
    public function mensaisApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo   = $this->repo();
            $anoSel = isset($_GET['ano']) && ctype_digit($_GET['ano'])
                      ? (int)$_GET['ano']
                      : (int)date('Y');
            $linhas = $repo->vendasMensais($anoSel);
            $anos   = $repo->anosDisponiveis();

            // Enriquecimentos rápidos (cada um tolerante a falha — nunca derruba a tela).
            // O lucro/margem (pesado) vai por endpoint separado: /api/vendas/mensais-margem
            $formasMes = []; $metas = [];
            try { $formasMes = $repo->tiposPagamentoMensal($anoSel); } catch (\Throwable) {}
            try {
                $todas = (new \App\Controllers\MetasController())->lerMetas();
                $metas = $todas[$anoSel] ?? [];
            } catch (\Throwable) {}

            echo json_encode([
                'linhas'    => $linhas,
                'anos'      => $anos,
                'anoSel'    => $anoSel,
                'formasMes' => $formasMes,
                'metas'     => $metas,
            ], 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/vendas/mensais-margem?ano= — CMV/lucro/margem por mês (pesado, cacheado, carregado em 2º plano). */
    public function mensaisMargemApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $anoSel = isset($_GET['ano']) && ctype_digit($_GET['ano']) ? (int)$_GET['ano'] : (int)date('Y');
            $repo   = $this->repo();
            $cmv    = $repo->cmvMensal($anoSel);
            // Faturamento "oficial" (vendasMensais) é a fonte da verdade pra comparar com CMV.
            $vendas    = [];
            try { $vendas = $repo->vendasMensais($anoSel); } catch (\Throwable) {}
            $confiavel = $repo->cmvConfiavel($cmv, $vendas);
            echo json_encode([
                'anoSel'    => $anoSel,
                'cmv'       => $cmv,
                'confiavel' => $confiavel,
            ], 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/vendas/por-vendedor?data_ini=&data_fim= */
    public function porVendedorApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo    = $this->repo();
            $ultima  = $repo->ultimaData();
            $dataFim = $_GET['data_fim'] ?? $ultima;
            $dataIni = $_GET['data_ini'] ?? date('Y-m-d', strtotime($dataFim . ' -29 days'));
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataIni)) $dataIni = date('Y-m-d', strtotime($dataFim . ' -29 days'));
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataFim)) $dataFim = $ultima;
            if ($dataFim < $dataIni) $dataFim = $dataIni;

            $linhas = $repo->vendasPorVendedor($dataIni, $dataFim);
            $anos   = $repo->anosDisponiveis();
            echo json_encode([
                'linhas'  => $linhas,
                'anos'    => $anos,
                'dataIni' => $dataIni,
                'dataFim' => $dataFim,
            ], 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/vendas/por-vendedor/extras?data_ini=&data_fim=
     * Métricas adicionais pra página Vendas por Vendedor (sem impactar endpoint principal).
     * Resposta degradada (campo retorna null/array vazio) se alguma query falhar.
     */
    public function porVendedorExtrasApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo    = $this->repo();
            $ultima  = $repo->ultimaData();
            $dataFim = $_GET['data_fim'] ?? $ultima;
            $dataIni = $_GET['data_ini'] ?? date('Y-m-d', strtotime($dataFim . ' -29 days'));
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataIni)) $dataIni = date('Y-m-d', strtotime($dataFim . ' -29 days'));
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataFim)) $dataFim = $ultima;
            if ($dataFim < $dataIni) $dataFim = $dataIni;

            $metricas    = null;
            $evolucao    = null;
            $diaSemana   = null;
            $aprofundado = null;
            $erros       = [];

            try { $metricas    = $repo->vendedorMetricasGerais($dataIni, $dataFim); } catch (\Throwable $e) { $erros[] = 'metricas: '.$e->getMessage(); }
            try { $evolucao    = $repo->vendedorEvolucaoTopN($dataIni, $dataFim, 5); } catch (\Throwable $e) { $erros[] = 'evolucao: '.$e->getMessage(); }
            try { $diaSemana   = $repo->vendedorDiaSemana($dataIni, $dataFim, 8); } catch (\Throwable $e) { $erros[] = 'dsem: '.$e->getMessage(); }
            try { $aprofundado = $repo->vendedorAprofundado($dataIni, $dataFim, 10); } catch (\Throwable $e) { $erros[] = 'aprof: '.$e->getMessage(); }

            echo json_encode([
                'metricas'    => $metricas,
                'evolucao'    => $evolucao,
                'dia_semana'  => $diaSemana,
                'aprofundado' => $aprofundado,
                'dataIni'     => $dataIni,
                'dataFim'     => $dataFim,
                'erros'       => $erros,
            ], 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;
    }

    /**
     * Helper: lê e sanitiza ?limite= (1..500, default 50) na request atual.
     * Mantém comportamento previsível em todos os endpoints de drill-down.
     */
    private function lerLimite(int $default = 50, int $max = 500): int
    {
        if (!isset($_GET['limite']) || !ctype_digit((string)$_GET['limite'])) return $default;
        $v = (int)$_GET['limite'];
        if ($v < 1)   return 1;
        if ($v > $max) return $max;
        return $v;
    }

    /**
     * Helper: deriva ($de, $ate, $deAnt, $ateAnt). Se ?de_ant/?ate_ant não vierem,
     * calcula o "período imediatamente anterior" (mesma duração, deslocando pra trás).
     */
    private function periodoAtualEAnterior(): array
    {
        $repo   = $this->repo();
        $ultima = $repo->ultimaData();

        $de  = $_GET['data_ini'] ?? $_GET['de']  ?? null;
        $ate = $_GET['data_fim'] ?? $_GET['ate'] ?? null;
        if (!is_string($de)  || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $de))  $de  = date('Y-m-d', strtotime($ultima . ' -29 days'));
        if (!is_string($ate) || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $ate)) $ate = $ultima;
        if ($ate < $de) $ate = $de;

        $deAnt  = $_GET['de_ant']  ?? null;
        $ateAnt = $_GET['ate_ant'] ?? null;
        if (!is_string($deAnt)  || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $deAnt) ||
            !is_string($ateAnt) || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $ateAnt)) {
            $tsDe  = strtotime($de);
            $tsAte = strtotime($ate);
            $diasMaisUm = max(1, (int)(($tsAte - $tsDe) / 86400) + 1);
            $ateAnt = date('Y-m-d', strtotime($de . ' -1 day'));
            $deAnt  = date('Y-m-d', strtotime($ateAnt . ' -' . ($diasMaisUm - 1) . ' days'));
        }
        if ($ateAnt < $deAnt) $ateAnt = $deAnt;

        return [$de, $ate, $deAnt, $ateAnt];
    }

    /** GET /api/vendas/por-grupo?data_ini=&data_fim=&limite= */
    public function porGrupoApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo    = $this->repo();
            $ultima  = $repo->ultimaData();
            $dataFim = $_GET['data_fim'] ?? $ultima;
            $dataIni = $_GET['data_ini'] ?? date('Y-m-d', strtotime($dataFim . ' -29 days'));
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataIni)) $dataIni = date('Y-m-d', strtotime($dataFim . ' -29 days'));
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataFim)) $dataFim = $ultima;
            if ($dataFim < $dataIni) $dataFim = $dataIni;
            $limite = $this->lerLimite(50, 500);

            $bloco  = $repo->vendasPorGrupo($dataIni, $dataFim, $limite);
            $anos   = $repo->anosDisponiveis();
            echo json_encode([
                'linhas'     => $bloco['linhas']     ?? [],
                'outros'     => $bloco['outros']     ?? null,
                'total_real' => $bloco['total_real'] ?? null,
                'limite'     => $limite,
                'anos'       => $anos,
                'dataIni'    => $dataIni,
                'dataFim'    => $dataFim,
            ], 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/vendas/por-marca?data_ini=&data_fim=&limite= */
    public function porMarcaApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo    = $this->repo();
            $ultima  = $repo->ultimaData();
            $dataFim = $_GET['data_fim'] ?? $ultima;
            $dataIni = $_GET['data_ini'] ?? date('Y-m-d', strtotime($dataFim . ' -29 days'));
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataIni)) $dataIni = date('Y-m-d', strtotime($dataFim . ' -29 days'));
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataFim)) $dataFim = $ultima;
            if ($dataFim < $dataIni) $dataFim = $dataIni;
            $limite = $this->lerLimite(50, 500);

            $bloco  = $repo->vendasPorMarca($dataIni, $dataFim, $limite);
            $anos   = $repo->anosDisponiveis();
            echo json_encode([
                'linhas'     => $bloco['linhas']     ?? [],
                'outros'     => $bloco['outros']     ?? null,
                'total_real' => $bloco['total_real'] ?? null,
                'limite'     => $limite,
                'anos'       => $anos,
                'dataIni'    => $dataIni,
                'dataFim'    => $dataFim,
            ], 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/vendas/por-grupo/comparativo?data_ini=&data_fim=&de_ant=&ate_ant=&limite=
     * Retorna ranking de grupos do período atual com variação % vs o anterior.
     * Se de_ant/ate_ant não vierem, calcula o período imediatamente anterior.
     */
    public function porGrupoComparativoApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo = $this->repo();
            [$de, $ate, $deAnt, $ateAnt] = $this->periodoAtualEAnterior();
            $limite = $this->lerLimite(50, 500);

            $bloco = $repo->vendasPorGrupoComparativo($de, $ate, $deAnt, $ateAnt, $limite);
            echo json_encode([
                'linhas_com_var'      => $bloco['linhas_com_var']      ?? [],
                'top_crescimento'     => $bloco['top_crescimento']     ?? null,
                'top_queda'           => $bloco['top_queda']           ?? null,
                'total_real_atual'    => $bloco['total_real_atual']    ?? null,
                'total_real_anterior' => $bloco['total_real_anterior'] ?? null,
                'limite'              => $limite,
                'periodo'             => ['de' => $de, 'ate' => $ate],
                'periodo_anterior'    => ['de' => $deAnt, 'ate' => $ateAnt],
            ], 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/vendas/por-marca/comparativo?data_ini=&data_fim=&de_ant=&ate_ant=&limite=
     * Ranking de marcas do período atual com variação % vs o anterior.
     */
    public function porMarcaComparativoApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo = $this->repo();
            [$de, $ate, $deAnt, $ateAnt] = $this->periodoAtualEAnterior();
            $limite = $this->lerLimite(50, 500);

            $bloco = $repo->vendasPorMarcaComparativo($de, $ate, $deAnt, $ateAnt, $limite);
            echo json_encode([
                'linhas_com_var'      => $bloco['linhas_com_var']      ?? [],
                'top_crescimento'     => $bloco['top_crescimento']     ?? null,
                'top_queda'           => $bloco['top_queda']           ?? null,
                'total_real_atual'    => $bloco['total_real_atual']    ?? null,
                'total_real_anterior' => $bloco['total_real_anterior'] ?? null,
                'limite'              => $limite,
                'periodo'             => ['de' => $de, 'ate' => $ate],
                'periodo_anterior'    => ['de' => $deAnt, 'ate' => $ateAnt],
            ], 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/vendas/por-grupo/produtos?cod_grupo=N&data_ini=&data_fim=&limite=
     * Drill-down: top N produtos vendidos NO grupo informado durante o período.
     */
    public function produtosPorGrupoApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo    = $this->repo();
            $ultima  = $repo->ultimaData();
            $codGrupo = isset($_GET['cod_grupo']) && ctype_digit((string)$_GET['cod_grupo']) ? (int)$_GET['cod_grupo'] : 0;
            if ($codGrupo <= 0) {
                http_response_code(400);
                echo json_encode(['erro' => 'cod_grupo inválido'], JSON_UNESCAPED_UNICODE);
                exit;
            }
            $dataFim = $_GET['data_fim'] ?? $ultima;
            $dataIni = $_GET['data_ini'] ?? date('Y-m-d', strtotime($dataFim . ' -29 days'));
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataIni)) $dataIni = date('Y-m-d', strtotime($dataFim . ' -29 days'));
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataFim)) $dataFim = $ultima;
            if ($dataFim < $dataIni) $dataFim = $dataIni;
            $limite = $this->lerLimite(20, 500);

            $produtos = $repo->produtosPorGrupo($codGrupo, $dataIni, $dataFim, $limite);
            echo json_encode([
                'cod_grupo' => $codGrupo,
                'produtos'  => $produtos,
                'limite'    => $limite,
                'dataIni'   => $dataIni,
                'dataFim'   => $dataFim,
            ], 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/vendas/por-marca/produtos?cod_marca=N&data_ini=&data_fim=&limite=
     * Drill-down: top N produtos vendidos da marca informada durante o período.
     */
    public function produtosPorMarcaApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo    = $this->repo();
            $ultima  = $repo->ultimaData();
            $codMarca = isset($_GET['cod_marca']) && ctype_digit((string)$_GET['cod_marca']) ? (int)$_GET['cod_marca'] : 0;
            if ($codMarca <= 0) {
                http_response_code(400);
                echo json_encode(['erro' => 'cod_marca inválido'], JSON_UNESCAPED_UNICODE);
                exit;
            }
            $dataFim = $_GET['data_fim'] ?? $ultima;
            $dataIni = $_GET['data_ini'] ?? date('Y-m-d', strtotime($dataFim . ' -29 days'));
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataIni)) $dataIni = date('Y-m-d', strtotime($dataFim . ' -29 days'));
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dataFim)) $dataFim = $ultima;
            if ($dataFim < $dataIni) $dataFim = $dataIni;
            $limite = $this->lerLimite(20, 500);

            $produtos = $repo->produtosPorMarca($codMarca, $dataIni, $dataFim, $limite);
            echo json_encode([
                'cod_marca' => $codMarca,
                'produtos'  => $produtos,
                'limite'    => $limite,
                'dataIni'   => $dataIni,
                'dataFim'   => $dataFim,
            ], 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/vendas/notas-saida?data_ini=&data_fim= */
    public function notasSaidaApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo   = $this->repo();
            $ultima = $repo->ultimaData();
            [$dataIni, $dataFim] = $this->parseDatas($ultima);
            $codVendedor = isset($_GET['vendedor']) && ctype_digit($_GET['vendedor'])
                           ? (int)$_GET['vendedor'] : null;

            // ?v=2 → listagem nova (v1.4.4+) com vendedor real, operação, cliente
            // sem o ?v=2 → mantém compatibilidade total com o endpoint antigo
            if (($_GET['v'] ?? '') === '2') {
                $limite = isset($_GET['limite']) ? (int)$_GET['limite'] : 1000;
                $notas  = $repo->notasSaidaV2($dataIni, $dataFim, $limite);
            } else {
                $notas  = $repo->notasSaida($dataIni, $dataFim, $codVendedor);
            }
            $anos  = $repo->anosDisponiveis();
            echo json_encode([
                'notas'   => $notas,
                'anos'    => $anos,
                'dataIni' => $dataIni,
                'dataFim' => $dataFim,
            ], 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/vendas/notas-saida/resumo?data_ini=&data_fim=  (v1.4.4+) */
    public function notasSaidaResumoApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo   = $this->repo();
            $ultima = $repo->ultimaData();
            [$dataIni, $dataFim] = $this->parseDatas($ultima);
            $resumo = $repo->notasSaidaResumo($dataIni, $dataFim);
            echo json_encode([
                'resumo'  => $resumo,
                'dataIni' => $dataIni,
                'dataFim' => $dataFim,
            ], 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/vendas/pre-notas?data_ini=&data_fim= */
    public function preNotasApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo   = $this->repo();
            $ultima = $repo->ultimaData();
            [$dataIni, $dataFim] = $this->parseDatas($ultima);
            $limite = isset($_GET['limite']) ? (int)$_GET['limite'] : 1000;
            $notas  = $repo->preNotas($dataIni, $dataFim, $limite);
            $anos   = $repo->anosDisponiveis();
            echo json_encode([
                'notas'   => $notas,
                'anos'    => $anos,
                'dataIni' => $dataIni,
                'dataFim' => $dataFim,
            ], 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/vendas/pre-notas/resumo?data_ini=&data_fim= */
    public function preNotasResumoApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo   = $this->repo();
            $ultima = $repo->ultimaData();
            [$dataIni, $dataFim] = $this->parseDatas($ultima);
            $resumo = $repo->preNotasResumo($dataIni, $dataFim);
            echo json_encode([
                'resumo'  => $resumo,
                'dataIni' => $dataIni,
                'dataFim' => $dataFim,
            ], 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/vendas/gestao-dash?data_ini=&data_fim= */
    public function gestaoDashApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo   = $this->repo();
            $ultima = $repo->ultimaData();
            [$dataIni, $dataFim] = $this->parseDatas($ultima);

            $kpis = $repo->kpisGestao($dataIni, $dataFim);
            $anos = $repo->anosDisponiveis();

            $diarias = [];
            try { $diarias = $repo->vendasDiarias($dataIni, $dataFim); } catch (\Throwable $e) { $diarias = []; }

            $tiposPgto = [];
            try { $tiposPgto = $repo->tiposPagamento($dataIni, $dataFim); } catch (\Throwable $e) { $tiposPgto = []; }

            $fiscal = [];
            try { $fiscal = $repo->faturamentoFiscal($dataIni, $dataFim); } catch (\Throwable $e) { $fiscal = []; }

            $porHora = [];
            try { $porHora = $repo->faturamentoPorHora($dataIni, $dataFim); } catch (\Throwable $e) { $porHora = []; }

            $diaSemana = [];
            try { $diaSemana = $repo->vendasDiaSemanaExtendido($dataIni, $dataFim); } catch (\Throwable $e) { $diaSemana = []; }

            $vendedores = [];
            try {
                $todos = $repo->vendasPorVendedor($dataIni, $dataFim);
                $vendedores = array_slice($todos, 0, 10);
            } catch (\Throwable $e) { $vendedores = []; }

            echo json_encode([
                'kpis'       => $kpis,
                'anos'       => $anos,
                'diarias'    => $diarias,
                'tipos_pgto' => $tiposPgto,
                'fiscal'     => $fiscal,
                'por_hora'   => $porHora,
                'dia_semana' => $diaSemana,
                'vendedores' => $vendedores,
                'data_ini'   => $dataIni,
                'data_fim'   => $dataFim,
            ], 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/vendas/diarias-dash?data_ini=&data_fim= */
    public function diariasDashApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo   = $this->repo();
            $ultima = $repo->ultimaData();
            [$dataIni, $dataFim] = $this->parseDatas($ultima);

            $anos      = $repo->anosDisponiveis();
            $linhas    = [];
            $porHora   = [];
            $diaSemana = [];
            $tiposPgto = [];

            try { $linhas    = $repo->vendasDiarias($dataIni, $dataFim); } catch (\Throwable) {}
            try { $porHora   = $repo->faturamentoPorHora($dataIni, $dataFim); } catch (\Throwable) {}
            try { $diaSemana = $repo->vendasDiaSemanaExtendido($dataIni, $dataFim); } catch (\Throwable) {}
            try { $tiposPgto = $repo->tiposPagamento($dataIni, $dataFim); } catch (\Throwable) {}

            echo json_encode([
                'linhas'     => $linhas,
                'por_hora'   => $porHora,
                'dia_semana' => $diaSemana,
                'tipos_pgto' => $tiposPgto,
                'anos'       => $anos,
                'data_ini'   => $dataIni,
                'data_fim'   => $dataFim,
            ], 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;
    }

    // ════════════════════════════════════════════════════════════════════════
    //  CUSTO MÉDIO POR VENDA (cupom) — Σ custo dos itens ÷ total de itens
    // ════════════════════════════════════════════════════════════════════════

    public function custoMedioVenda(): void
    {
        $this->guardaConexao();
        $config  = $this->config();
        $filtros = $this->filtrosSemDB();
        View::render('vendas/custo-medio-venda', [
            'titulo'   => 'Custo Médio por Venda — Premium Express Gestão',
            'config'   => $config,
            'conexao'  => ['success' => true, 'mensagem' => ''],
            'segmento' => \App\Core\Lojas::resolverSegmento($config),
            'filtros'  => $filtros,
            'erros'    => [],
            'ano'      => date('Y'),
        ]);
    }

    /** GET /api/vendas/custo-medio?data_ini=&data_fim=&ordenar=&apenas=&busca=&limite= */
    public function custoMedioVendaApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $repo   = $this->repo();
            $ultima = $repo->ultimaDataComVendas();
            $g = $_GET;
            $ini = (isset($g['data_ini']) && preg_match('/^\d{4}-\d{2}-\d{2}$/', $g['data_ini'])) ? $g['data_ini'] : $ultima;
            $fim = (isset($g['data_fim']) && preg_match('/^\d{4}-\d{2}-\d{2}$/', $g['data_fim'])) ? $g['data_fim'] : $ini;
            if ($fim < $ini) $fim = $ini;
            $out = $repo->custoMedioVenda([
                'data_ini' => $ini, 'data_fim' => $fim,
                'ordenar'  => $g['ordenar'] ?? 'custo',
                'apenas'   => $g['apenas']  ?? '',
                'busca'    => $g['busca']   ?? '',
                'limite'   => $g['limite']  ?? 1000,
            ]);
            echo json_encode($out, 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/vendas/custo-medio/itens?nota=&op= — drill de um cupom */
    public function itensCupomApi(): void
    {
        $this->guardaConexao();
        header('Content-Type: application/json; charset=utf-8');
        try {
            $nr = (int)($_GET['nota'] ?? 0);
            $op = (int)($_GET['op'] ?? 0);
            $itens = ($nr > 0 && $op > 0) ? $this->repo()->itensCupom($nr, $op) : [];
            echo json_encode(['itens' => $itens], 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;
    }
}
