<?php
// =============================================================================
// Depto de Engenharia e Automacao - Master Automacao - Autor: Derlivon Silva
// =============================================================================

namespace App\Controllers;

use App\Core\Lojas;
use App\Core\Database;
use App\Core\View;
use App\Repositories\DashboardRepository;

/**
 * Visão CONSOLIDADA — "Todas as lojas".
 * Faturamento / vendas / ticket de cada loja (do período de referência) + totais.
 * Conecta em cada loja (Database::conectarLoja) e usa DashboardRepository por loja.
 */
class ConsolidadoController
{
    public function index(): void
    {
        // Tela faz queries em N lojas (algumas via VPN) — quando o cache é
        // invalidado, a primeira request precisa rebuildar tudo. 30s padrao
        // do PHP nao da. Bump pra 180s + memoria 512M.
        @set_time_limit(180);
        @ini_set('memory_limit', '512M');

        if (!Lojas::multi()) {
            header('Location: /');
            exit;
        }
        // Só faz sentido com o modo "Todas as lojas" ligado. Se uma loja específica
        // está ativa (usuário trocou no seletor), volta pro dashboard daquela loja —
        // senão a tela continuaria mostrando o consolidado de todas.
        if (!Lojas::consolidado()) {
            header('Location: /');
            exit;
        }

        $hoje = date('Y-m-d');

        // Data custom (date picker) tem prioridade sobre o atalho de período
        $iniCustom = $_GET['data_ini'] ?? null;
        $fimCustom = $_GET['data_fim'] ?? null;
        $ehCustom  = false;
        if ($iniCustom && preg_match('/^\d{4}-\d{2}-\d{2}$/', $iniCustom)
            && $fimCustom && preg_match('/^\d{4}-\d{2}-\d{2}$/', $fimCustom)) {
            if ($fimCustom < $iniCustom) $fimCustom = $iniCustom;
            $ini = $iniCustom; $fim = $fimCustom;
            $periodo = 'custom';
            $ehCustom = true;
            $_SESSION['cons_periodo']  = 'custom';
            $_SESSION['cons_data_ini'] = $ini;
            $_SESSION['cons_data_fim'] = $fim;
        } else {
            // Período rápido: hoje | semana | mês | ano (botões na tela). Persiste na sessão.
            $periodo = $_GET['p'] ?? ($_SESSION['cons_periodo'] ?? 'mes');
            if (!in_array($periodo, ['hoje', 'semana', 'mes', 'ano', 'custom'], true)) $periodo = 'mes';
            // Se sessão tinha custom mas usuário não passou GET, recupera as datas salvas
            if ($periodo === 'custom') {
                $ini = $_SESSION['cons_data_ini'] ?? date('Y-m-01');
                $fim = $_SESSION['cons_data_fim'] ?? $hoje;
            } else {
                $_SESSION['cons_periodo'] = $periodo;
                switch ($periodo) {
                    case 'hoje':   $ini = $hoje; break;
                    case 'semana': $ini = date('Y-m-d', strtotime('-6 days')); break;
                    case 'ano':    $ini = date('Y-01-01'); break;
                    default:       $periodo = 'mes'; $ini = date('Y-m-01');
                }
                $fim = $hoje;
            }
        }

        $linhas       = [];
        $totFat       = 0.0;
        $totVendas    = 0;
        // Agregados Com Nota / Sem Nota (soma de todas as lojas no período)
        $totValCom    = 0.0;
        $totValSem    = 0.0;
        $totQtdCom    = 0;
        $totQtdSem    = 0;

        // Acumuladores das 5 features novas (somam contribuição de cada loja)
        $aggGrupos       = []; // [cod => ['nome','faturamento','qtd_notas','qtd_itens']]
        $aggMarcas       = []; // idem
        $aggFormas       = []; // [grupo => ['VALOR','QTD','FORMAS'=>[]]]
        // Totais consolidados das formas de pagamento (somam contribuição de cada loja).
        // - formas_total_geral_cons  = espelha relatório fiscal ERP (todas as ops)
        // - formas_total_vendas_cons = bate com faturamento (filtroVenda aplicado)
        // - formas_total_vendas_disp = false se ALGUMA loja não conseguiu calcular
        //   (qualquer falha invalida o total agregado — view esconde linha)
        $formasTotalGeralCons  = 0.0;
        $formasTotalVendasCons = 0.0;
        $formasTotalVendasDisp = true;
        // Drill-down consolidado da "diferença" do card de Formas de Pagamento.
        // Agrega por (operacao + forma) somando valor e qtd das lojas.
        //   chave canônica = UPPER(operacao) . '|' . UPPER(forma)
        $formasDetalheForaVendaCons = []; // [chave => ['operacao','forma','qtd','valor']]
        // OBS: data_ini_ant/data_fim_ant ficam IDENTICAS pra todas as lojas
        // (sao funcao do periodo $ini..$fim, nao da loja). Por isso so atribuimos
        // uma vez (na 1a loja que conseguir calcular) e ja vale pro consolidado.
        $compSemana      = ['atual' => 0.0, 'anterior' => 0.0, 'vendas_atual' => 0, 'vendas_anterior' => 0,
                            'data_ini_ant' => null, 'data_fim_ant' => null];
        $compMes         = ['atual' => 0.0, 'anterior' => 0.0, 'vendas_atual' => 0, 'vendas_anterior' => 0,
                            'data_ini_ant' => null, 'data_fim_ant' => null];
        $compAno         = ['atual' => 0.0, 'anterior' => 0.0, 'vendas_atual' => 0, 'vendas_anterior' => 0,
                            'data_ini_ant' => null, 'data_fim_ant' => null];
        $compMesmoDataAA = ['atual' => 0.0, 'anterior' => 0.0, 'vendas_atual' => 0, 'vendas_anterior' => 0,
                            'data_ini_ant' => null, 'data_fim_ant' => null];

        $linhaCSNzero = [
            'com_nota' => ['qtd' => 0, 'valor' => 0.0],
            'sem_nota' => ['qtd' => 0, 'valor' => 0.0],
        ];

        foreach (Lojas::todas() as $id => $l) {
            $id  = (string) $id;
            $cod = (int) ($l['cod_empresa'] ?? 0);
            $row = [
                'id'       => $id,
                'nome'     => $l['nome'] ?? $id,
                'fat'      => 0.0,
                'vendas'   => 0,
                'ticket'   => 0.0,
                'erro'     => null,
                'ultima'   => null,
                'com_nota' => ['qtd' => 0, 'valor' => 0.0],
                'sem_nota' => ['qtd' => 0, 'valor' => 0.0],
                // dados por loja (top 5/10 + comparativos)
                'grupos_loja'           => [],
                'marcas_loja'           => [],
                'formas_pgto_loja'      => [],
                // 2 totais "ricos" da forma de pagamento desta loja:
                //   total_geral  = espelha relatório fiscal (inclui crediário/cortesia/troca)
                //   total_vendas = bate com card de Faturamento (filtroVenda aplicado)
                'formas_total_geral'    => 0.0,
                'formas_total_vendas'   => null,
                // Drill-down da diferença (1 linha por TIPO_OPERACAO × FORMA fora do filtroVenda).
                // [] se a query falhou ou loja não tem filtroVenda → view esconde toggle.
                'formas_detalhe_fora_venda' => [],
                'comp_semana_loja'      => ['atual' => 0.0, 'anterior' => 0.0, 'pct' => 0.0, 'abs' => 0.0,
                                            'data_ini_ant' => null, 'data_fim_ant' => null],
                'comp_mes_loja'         => ['atual' => 0.0, 'anterior' => 0.0, 'pct' => 0.0, 'abs' => 0.0,
                                            'data_ini_ant' => null, 'data_fim_ant' => null],
                'comp_ano_loja'         => ['atual' => 0.0, 'anterior' => 0.0, 'pct' => 0.0, 'abs' => 0.0,
                                            'data_ini_ant' => null, 'data_fim_ant' => null],
                'comp_mesma_data_aa_loja' => ['atual' => 0.0, 'anterior' => 0.0, 'pct' => 0.0, 'abs' => 0.0,
                                              'data_ini_ant' => null, 'data_fim_ant' => null],
                // 4 indicadores extras por loja (v1.4.32+) — dia da semana, hora,
                // top produtos, top clientes. Cada lista pode ficar vazia se a
                // loja não devolveu dado (banco antigo, schema diferente etc.).
                'dia_semana_loja' => [],
                'hora_loja'       => [],
                'produtos_loja'   => [],
                'clientes_loja'   => [],
            ];

            try {
                Lojas::setFoco($id); // alinha o escopo de cache à loja consultada
                $pdo  = Database::conectarLoja($id);
                // MESMO intervalo [ini,fim] para TODAS as lojas. dataOverride=fim evita
                // a query de detecção da última data no construtor.
                $repo = new DashboardRepository($pdo, $fim, $cod);
                $row['fat']    = (float) $repo->faturamentoIntervalo($ini, $fim);
                $row['vendas'] = (int)   $repo->totalVendasIntervalo($ini, $fim);
                $row['ticket'] = $row['vendas'] > 0 ? $row['fat'] / $row['vendas'] : 0.0;
                // Sem venda no período? mostra até quando a base tem dado (avisa cópia desatualizada).
                if ($row['vendas'] === 0) $row['ultima'] = $repo->ultimaVenda();

                // Com Nota / Sem Nota — try/catch isolado: se a query nova falhar (banco
                // mais antigo, schema diferente), mantemos os zeros e seguimos sem
                // derrubar o resto da linha da loja.
                try {
                    $csn = $repo->faturamentoComSemNota($ini, $fim);
                    if (is_array($csn) && isset($csn['com_nota'], $csn['sem_nota'])) {
                        $row['com_nota'] = $csn['com_nota'];
                        $row['sem_nota'] = $csn['sem_nota'];
                    }
                } catch (\Throwable) {
                    // segue com os zeros já definidos em $row
                }

                $totFat    += $row['fat'];
                $totVendas += $row['vendas'];
                $totValCom += (float) ($row['com_nota']['valor'] ?? 0);
                $totValSem += (float) ($row['sem_nota']['valor'] ?? 0);
                $totQtdCom += (int)   ($row['com_nota']['qtd']   ?? 0);
                $totQtdSem += (int)   ($row['sem_nota']['qtd']   ?? 0);

                // helper local: calcula delta (atual vs anterior) por loja
                $deltaLoja = function (float $a, float $b): array {
                    $abs = $a - $b;
                    $pct = $b > 0 ? ($a - $b) / $b * 100 : ($a > 0 ? 100.0 : 0.0);
                    return ['atual' => $a, 'anterior' => $b, 'pct' => $pct, 'abs' => $abs];
                };

                // === FEATURE 1: Vendas por Grupo (top 20 por loja + agregado) ===
                try {
                    $vg = $repo->vendasPorGrupoConsolidado($ini, $fim, 20);
                    $linhasGrupoLoja = [];
                    foreach (($vg['linhas'] ?? []) as $g) {
                        // Agregar por NOME normalizado (uppercase+trim), nao por
                        // cod_grupo: cada loja tem cadastro independente e o
                        // mesmo nome de grupo pode ter codigos diferentes em
                        // bancos diferentes. Sem isso, "UTILIDADES DOMESTICAS"
                        // de 2 lojas apareceria como 2 itens separados no top.
                        $nomeKey = mb_strtoupper(trim((string)$g['nome']));
                        $k = $nomeKey !== '' ? 'n:'.$nomeKey : 'c:'.($g['cod_grupo'] ?? 'null');
                        if (!isset($aggGrupos[$k])) {
                            $aggGrupos[$k] = ['cod_grupo' => $g['cod_grupo'], 'nome' => $g['nome'],
                                              'faturamento' => 0.0, 'qtd_notas' => 0, 'qtd_itens' => 0.0,
                                              'por_loja' => []];
                        }
                        $aggGrupos[$k]['faturamento'] += (float)$g['faturamento'];
                        $aggGrupos[$k]['qtd_notas']   += (int)$g['qtd_notas'];
                        $aggGrupos[$k]['qtd_itens']   += (float)$g['qtd_itens'];
                        // Stacked bar por loja: acumula faturamento desta loja ($id) no grupo $k
                        $aggGrupos[$k]['por_loja'][$id] = ($aggGrupos[$k]['por_loja'][$id] ?? 0.0) + (float)$g['faturamento'];
                        $linhasGrupoLoja[] = [
                            'cod_grupo'   => $g['cod_grupo'],
                            'nome'        => $g['nome'],
                            'faturamento' => (float)$g['faturamento'],
                            'qtd_notas'   => (int)$g['qtd_notas'],
                            'qtd_itens'   => (float)$g['qtd_itens'],
                        ];
                    }
                    // top 20 por faturamento (a query ja deve devolver ordenado, mas garantimos)
                    usort($linhasGrupoLoja, fn($a, $b) => $b['faturamento'] <=> $a['faturamento']);
                    $row['grupos_loja'] = array_slice($linhasGrupoLoja, 0, 20);
                } catch (\Throwable) {}

                // === FEATURE 2: Vendas por Marca (top 20 por loja + agregado) ===
                try {
                    $vm = $repo->vendasPorMarcaConsolidado($ini, $fim, 20);
                    $linhasMarcaLoja = [];
                    foreach (($vm['linhas'] ?? []) as $m) {
                        // Mesma logica: agregar por NOME normalizado em vez de
                        // cod_marca, ja que cada loja tem cadastro independente.
                        $nomeKey = mb_strtoupper(trim((string)$m['nome']));
                        $k = $nomeKey !== '' ? 'n:'.$nomeKey : 'c:'.($m['cod_marca'] ?? 'null');
                        if (!isset($aggMarcas[$k])) {
                            $aggMarcas[$k] = ['cod_marca' => $m['cod_marca'], 'nome' => $m['nome'],
                                              'faturamento' => 0.0, 'qtd_notas' => 0, 'qtd_itens' => 0.0,
                                              'por_loja' => []];
                        }
                        $aggMarcas[$k]['faturamento'] += (float)$m['faturamento'];
                        $aggMarcas[$k]['qtd_notas']   += (int)$m['qtd_notas'];
                        $aggMarcas[$k]['qtd_itens']   += (float)$m['qtd_itens'];
                        // Stacked bar por loja: acumula faturamento desta loja ($id) na marca $k
                        $aggMarcas[$k]['por_loja'][$id] = ($aggMarcas[$k]['por_loja'][$id] ?? 0.0) + (float)$m['faturamento'];
                        $linhasMarcaLoja[] = [
                            'cod_marca'   => $m['cod_marca'],
                            'nome'        => $m['nome'],
                            'faturamento' => (float)$m['faturamento'],
                            'qtd_notas'   => (int)$m['qtd_notas'],
                            'qtd_itens'   => (float)$m['qtd_itens'],
                        ];
                    }
                    usort($linhasMarcaLoja, fn($a, $b) => $b['faturamento'] <=> $a['faturamento']);
                    $row['marcas_loja'] = array_slice($linhasMarcaLoja, 0, 20);
                } catch (\Throwable) {}

                // === FEATURE 3: Formas de Pagamento — TODAS individualmente (sem "Outros") ===
                // Usa a versão "ComTotal" que devolve grupos + total_geral (espelha
                // o relatório fiscal do ERP Fire) + total_vendas (bate com o card
                // de Faturamento ao lado). A diferença entre os dois = crediário,
                // cortesia, devolução com troca etc.
                try {
                    $fp = $repo->formasPagamentoIntervaloComTotal($ini, $fim);
                    $row['formas_total_geral']  = (float) ($fp['total_geral']  ?? 0);
                    $row['formas_total_vendas'] = $fp['total_vendas'] !== null ? (float)$fp['total_vendas'] : null;
                    $formasLoja = [];
                    foreach (($fp['grupos'] ?? []) as $grp) {
                        $nomeForma = trim((string)($grp['GRUPO'] ?? ''));
                        if ($nomeForma === '') continue; // descarta forma sem nome (não cria bucket "Outros")
                        // chave canônica = nome em UPPER+TRIM (junta variantes de capitalização entre lojas)
                        $k = mb_strtoupper($nomeForma, 'UTF-8');
                        if (!isset($aggFormas[$k])) {
                            $aggFormas[$k] = ['GRUPO' => $nomeForma, 'VALOR' => 0.0, 'QTD' => 0, 'por_loja' => []];
                        }
                        $aggFormas[$k]['VALOR'] += (float)($grp['VALOR'] ?? 0);
                        $aggFormas[$k]['QTD']   += (int)($grp['QTD'] ?? 0);
                        $aggFormas[$k]['por_loja'][$id] = ($aggFormas[$k]['por_loja'][$id] ?? 0.0) + (float)($grp['VALOR'] ?? 0);
                        $formasLoja[] = [
                            'GRUPO' => $nomeForma,
                            'VALOR' => (float)($grp['VALOR'] ?? 0),
                            'QTD'   => (int)($grp['QTD'] ?? 0),
                        ];
                    }
                    // ordena e calcula PCT por loja
                    usort($formasLoja, fn($a, $b) => $b['VALOR'] <=> $a['VALOR']);
                    $sumValLoja = 0.0;
                    foreach ($formasLoja as $f) { $sumValLoja += (float)$f['VALOR']; }
                    foreach ($formasLoja as &$f) {
                        $f['PCT'] = $sumValLoja > 0 ? (float)$f['VALOR'] / $sumValLoja * 100 : 0.0;
                    }
                    unset($f);
                    $row['formas_pgto_loja'] = $formasLoja;
                    // Drill-down da diferença: 1 linha por (TIPO_OPERACAO × FORMA) fora do filtroVenda.
                    // Vai pra view por loja E acumula no agregado consolidado (somando entre lojas).
                    $detalheLoja = (array) ($fp['detalhe_fora_venda'] ?? []);
                    $row['formas_detalhe_fora_venda'] = $detalheLoja;
                    foreach ($detalheLoja as $d) {
                        $opNome = (string)($d['operacao'] ?? '');
                        $fmNome = (string)($d['forma'] ?? '');
                        if ($opNome === '' && $fmNome === '') continue;
                        $k = mb_strtoupper($opNome, 'UTF-8') . '|' . mb_strtoupper($fmNome, 'UTF-8');
                        if (!isset($formasDetalheForaVendaCons[$k])) {
                            $formasDetalheForaVendaCons[$k] = [
                                'operacao' => $opNome,
                                'forma'    => $fmNome,
                                'qtd'      => 0,
                                'valor'    => 0.0,
                            ];
                        }
                        $formasDetalheForaVendaCons[$k]['qtd']   += (int)($d['qtd'] ?? 0);
                        $formasDetalheForaVendaCons[$k]['valor'] += (float)($d['valor'] ?? 0);
                    }
                    // Acumula nos totais consolidados
                    $formasTotalGeralCons += (float)$row['formas_total_geral'];
                    if ($row['formas_total_vendas'] === null) {
                        // Loja não conseguiu calcular → invalida agregado consolidado
                        $formasTotalVendasDisp = false;
                    } else {
                        $formasTotalVendasCons += (float)$row['formas_total_vendas'];
                    }
                } catch (\Throwable) {}

                // === FEATURE 4: Comparativos (semana / mês / ano + mesma data ano anterior) ===
                try {
                    $semAnt = $repo->faturamentoPeriodoAnterior($ini, $fim, 'semana');
                    $compSemana['atual']           += $row['fat'];
                    $compSemana['vendas_atual']    += $row['vendas'];
                    $compSemana['anterior']        += (float) ($semAnt['faturamento'] ?? 0);
                    $compSemana['vendas_anterior'] += (int)   ($semAnt['qtd_vendas']  ?? 0);
                    // Datas do periodo anterior: identicas pra todas as lojas (so
                    // dependem de $ini/$fim). 1a loja que conseguir calcular ja preenche.
                    if ($compSemana['data_ini_ant'] === null) {
                        $compSemana['data_ini_ant'] = $semAnt['data_ini'] ?? null;
                        $compSemana['data_fim_ant'] = $semAnt['data_fim'] ?? null;
                    }
                    $row['comp_semana_loja']       = array_merge(
                        $deltaLoja($row['fat'], (float)($semAnt['faturamento'] ?? 0)),
                        [
                            'data_ini_ant'    => $semAnt['data_ini'] ?? null,
                            'data_fim_ant'    => $semAnt['data_fim'] ?? null,
                            // Qtd de vendas pra paridade com "Qtd.Vendas: N" do relatorio fiscal Fire
                            'vendas_atual'    => (int)$row['vendas'],
                            'vendas_anterior' => (int)($semAnt['qtd_vendas'] ?? 0),
                        ]
                    );
                } catch (\Throwable) {}
                try {
                    $mesAnt = $repo->faturamentoPeriodoAnterior($ini, $fim, 'mes');
                    $compMes['atual']           += $row['fat'];
                    $compMes['vendas_atual']    += $row['vendas'];
                    $compMes['anterior']        += (float) ($mesAnt['faturamento'] ?? 0);
                    $compMes['vendas_anterior'] += (int)   ($mesAnt['qtd_vendas']  ?? 0);
                    if ($compMes['data_ini_ant'] === null) {
                        $compMes['data_ini_ant'] = $mesAnt['data_ini'] ?? null;
                        $compMes['data_fim_ant'] = $mesAnt['data_fim'] ?? null;
                    }
                    $row['comp_mes_loja']       = array_merge(
                        $deltaLoja($row['fat'], (float)($mesAnt['faturamento'] ?? 0)),
                        [
                            'data_ini_ant'    => $mesAnt['data_ini'] ?? null,
                            'data_fim_ant'    => $mesAnt['data_fim'] ?? null,
                            'vendas_atual'    => (int)$row['vendas'],
                            'vendas_anterior' => (int)($mesAnt['qtd_vendas'] ?? 0),
                        ]
                    );
                } catch (\Throwable) {}
                try {
                    // YTD: acumulado do ano atual ate a data vs mesmo acumulado
                    // do ano anterior. NAO usa $row['fat'] (que e o filtro), e sim
                    // o YTD calculado direto pra ficar apples-to-apples.
                    $ytd = $repo->faturamentoYTD($ini, $fim);
                    $compAno['atual']           += (float) ($ytd['atual']           ?? 0);
                    $compAno['vendas_atual']    += (int)   ($ytd['vendas_atual']    ?? 0);
                    $compAno['anterior']        += (float) ($ytd['anterior']        ?? 0);
                    $compAno['vendas_anterior'] += (int)   ($ytd['vendas_anterior'] ?? 0);
                    // YTD anterior = 01/01 do ano passado ate $fim do ano passado.
                    // O repo devolve anterior_ini/anterior_fim, normalizamos pra
                    // data_ini_ant/data_fim_ant (mesma convencao dos outros cards).
                    if ($compAno['data_ini_ant'] === null) {
                        $compAno['data_ini_ant'] = $ytd['anterior_ini'] ?? null;
                        $compAno['data_fim_ant'] = $ytd['anterior_fim'] ?? null;
                    }
                    $row['comp_ano_loja']       = array_merge(
                        $deltaLoja(
                            (float)($ytd['atual']    ?? 0),
                            (float)($ytd['anterior'] ?? 0)
                        ),
                        [
                            'data_ini_ant'    => $ytd['anterior_ini'] ?? null,
                            'data_fim_ant'    => $ytd['anterior_fim'] ?? null,
                            'vendas_atual'    => (int)($ytd['vendas_atual']    ?? 0),
                            'vendas_anterior' => (int)($ytd['vendas_anterior'] ?? 0),
                        ]
                    );
                } catch (\Throwable) {}
                try {
                    $mda = $repo->faturamentoMesmaDataAnoAnterior($ini, $fim);
                    $compMesmoDataAA['atual']           += $row['fat'];
                    $compMesmoDataAA['vendas_atual']    += $row['vendas'];
                    $compMesmoDataAA['anterior']        += (float) ($mda['faturamento'] ?? 0);
                    $compMesmoDataAA['vendas_anterior'] += (int)   ($mda['qtd_vendas']  ?? 0);
                    $compMesmoDataAA['data_ini_ant']    = $mda['data_ini'] ?? null;
                    $compMesmoDataAA['data_fim_ant']    = $mda['data_fim'] ?? null;
                    $row['comp_mesma_data_aa_loja']     = array_merge(
                        $deltaLoja($row['fat'], (float)($mda['faturamento'] ?? 0)),
                        [
                            'data_ini_ant'    => $mda['data_ini'] ?? null,
                            'data_fim_ant'    => $mda['data_fim'] ?? null,
                            'vendas_atual'    => (int)$row['vendas'],
                            'vendas_anterior' => (int)($mda['qtd_vendas'] ?? 0),
                        ]
                    );
                } catch (\Throwable) {}

                // === 4 indicadores extras por loja (cada um isolado em try/catch) ===
                // Cada chamada cai em [] se a loja não tiver schema/coluna esperada,
                // mantendo o resto do painel funcional.
                try { $row['dia_semana_loja'] = $repo->vendasPorDiaSemanaIntervalo($ini, $fim); } catch (\Throwable) {}
                try { $row['hora_loja']       = $repo->vendasPorHoraIntervalo($ini, $fim);     } catch (\Throwable) {}
                try { $row['produtos_loja']   = $repo->topProdutosIntervalo($ini, $fim, 10);   } catch (\Throwable) {}
                try { $row['clientes_loja']   = $repo->topClientesIntervalo($ini, $fim, 10);   } catch (\Throwable) {}
            } catch (\Throwable $e) {
                $row['erro'] = $e->getMessage();
            } finally {
                Lojas::setFoco(null);
            }

            $linhas[] = $row;
        }

        // Ranking por faturamento (desc)
        usort($linhas, fn($a, $b) => $b['fat'] <=> $a['fat']);

        $totValCSN      = $totValCom + $totValSem;
        $pctComNotaGlob = $totValCSN > 0 ? ($totValCom / $totValCSN) * 100.0 : 0.0;

        // === Pós-processamento das 5 features ===
        // Top 20 grupos por faturamento somado entre as lojas
        usort($aggGrupos, fn($a, $b) => $b['faturamento'] <=> $a['faturamento']);
        $gruposConsolidado = array_slice(array_values($aggGrupos), 0, 20);
        $sumGruposFat = 0.0;
        foreach ($gruposConsolidado as $g) { $sumGruposFat += $g['faturamento']; }
        foreach ($gruposConsolidado as &$g) {
            $g['participacao_pct'] = $sumGruposFat > 0 ? $g['faturamento'] / $sumGruposFat * 100 : 0.0;
            $g['ticket_medio']     = $g['qtd_notas'] > 0 ? $g['faturamento'] / $g['qtd_notas'] : 0.0;
        }
        unset($g);

        // Top 20 marcas por faturamento somado entre as lojas
        usort($aggMarcas, fn($a, $b) => $b['faturamento'] <=> $a['faturamento']);
        $marcasConsolidado = array_slice(array_values($aggMarcas), 0, 20);
        $sumMarcasFat = 0.0;
        foreach ($marcasConsolidado as $m) { $sumMarcasFat += $m['faturamento']; }
        foreach ($marcasConsolidado as &$m) {
            $m['participacao_pct'] = $sumMarcasFat > 0 ? $m['faturamento'] / $sumMarcasFat * 100 : 0.0;
            $m['ticket_medio']     = $m['qtd_notas'] > 0 ? $m['faturamento'] / $m['qtd_notas'] : 0.0;
        }
        unset($m);

        // Formas de pagamento — soma valor+qtd por categoria, ordena desc, calcula %
        usort($aggFormas, fn($a, $b) => $b['VALOR'] <=> $a['VALOR']);
        $formasPagamentoConsolidado = array_values($aggFormas);
        $sumFormasVal = 0.0;
        foreach ($formasPagamentoConsolidado as $f) { $sumFormasVal += $f['VALOR']; }
        foreach ($formasPagamentoConsolidado as &$f) {
            $f['PCT'] = $sumFormasVal > 0 ? $f['VALOR'] / $sumFormasVal * 100 : 0.0;
        }
        unset($f);

        // Drill-down da diferença consolidado: ordena por valor desc
        usort($formasDetalheForaVendaCons, fn($a, $b) => $b['valor'] <=> $a['valor']);
        $formasDetalheForaVendaCons = array_values($formasDetalheForaVendaCons);

        // Deltas dos comparativos
        $calcDelta = function (array $c): array {
            $a = (float)$c['atual']; $b = (float)$c['anterior'];
            $c['delta_abs'] = $a - $b;
            $c['delta_pct'] = $b > 0 ? ($a - $b) / $b * 100 : ($a > 0 ? 100.0 : 0.0);
            return $c;
        };
        $compSemana      = $calcDelta($compSemana);
        $compMes         = $calcDelta($compMes);
        $compAno         = $calcDelta($compAno);
        $compMesmoDataAA = $calcDelta($compMesmoDataAA);

        View::render('consolidado/index', [
            'titulo'         => 'Consolidado — Todas as Lojas',
            'linhas'         => $linhas,
            'totFat'         => $totFat,
            'totVendas'      => $totVendas,
            'totTicket'      => $totVendas > 0 ? $totFat / $totVendas : 0.0,
            'totValCom'      => $totValCom,
            'totValSem'      => $totValSem,
            'totQtdCom'      => $totQtdCom,
            'totQtdSem'      => $totQtdSem,
            'pctComNota'     => $pctComNotaGlob,
            'pctSemNota'     => $totValCSN > 0 ? 100.0 - $pctComNotaGlob : 0.0,
            'periodo'        => $periodo,
            'dataIni'        => $ini,
            'dataFim'        => $fim,
            // 5 features novas
            'gruposConsolidado'           => $gruposConsolidado,
            'marcasConsolidado'           => $marcasConsolidado,
            'formasPagamentoConsolidado'  => $formasPagamentoConsolidado,
            // Totais "ricos" do card de Formas de Pagamento (resolve divergência visual com Faturamento)
            'formasTotalGeralCons'        => $formasTotalGeralCons,
            'formasTotalVendasCons'       => $formasTotalVendasDisp ? $formasTotalVendasCons : null,
            // Drill-down da diferença (1 linha por TIPO_OPERACAO × FORMA fora do filtroVenda)
            'formasDetalheForaVendaCons'  => $formasDetalheForaVendaCons,
            'compSemana'                  => $compSemana,
            'compMes'                     => $compMes,
            'compAno'                     => $compAno,
            'compMesmoDataAA'             => $compMesmoDataAA,
        ]);
    }

    /**
     * Último mês COM vendas da loja principal (1ª da lista) — usado como período
     * padrão do consolidado quando o usuário ainda não escolheu. Evita abrir num
     * mês vazio (ex.: mês corrente sem dado numa base de cópia desatualizada).
     */
    private function mesPadrao(): array
    {
        foreach (Lojas::todas() as $id => $l) {
            try {
                Lojas::setFoco((string) $id);
                $pdo  = Database::conectarLoja((string) $id);
                $repo = new DashboardRepository($pdo, null, (int) ($l['cod_empresa'] ?? 0));
                $d    = $repo->dataReferencia(); // 'YYYY-MM-DD' (última data com venda)
                Lojas::setFoco(null);
                if ($d && preg_match('/^\d{4}-\d{2}-\d{2}$/', $d)) {
                    return ['ano' => (int) substr($d, 0, 4), 'mes' => (int) substr($d, 5, 2)];
                }
            } catch (\Throwable) {
                Lojas::setFoco(null);
            }
        }
        return ['ano' => (int) date('Y'), 'mes' => (int) date('n')];
    }
}
