<?php

namespace App\Services;

use App\Core\Database;
use App\Repositories\DashboardRepository;
use App\Repositories\FinanceiroRepository;
use App\Repositories\EstoqueRepository;
use App\Repositories\ClientesRepository;

class DashboardService
{
    private DashboardRepository $repo;
    private ?int $filtroAno;
    private ?int $filtroMes;

    public function __construct(?int $ano = null, ?int $mes = null)
    {
        $this->filtroAno = $ano;
        $this->filtroMes = $mes;

        // COD_EMPRESA da LOJA ATIVA (multi-loja) ou do config/database.php (single-loja)
        $codEmpresa = \App\Core\Lojas::codEmpresaAtiva();

        // Determina a data de referência a partir do filtro
        $dataOverride = null;
        if ($ano) {
            $repoTemp = new DashboardRepository(Database::conectar(), null, $codEmpresa);
            if ($mes) {
                // Mês específico: 1 query
                $dataOverride = $repoTemp->ultimoDiaDoMes($ano, $mes);
            } else {
                // Sem mês: 1 query para o último dia do ano inteiro
                $ultimo = $repoTemp->ultimoDiaDoAno($ano);
                $dataOverride = $ultimo['data'] ?? null;
            }
        }

        $this->repo = new DashboardRepository(Database::conectar(), $dataOverride, $codEmpresa);
    }

    // -------------------------------------------------------------------------
    // Meta — período de referência
    // -------------------------------------------------------------------------

    public function meta(): array
    {
        $range   = $this->seguro(fn() => $this->repo->rangeDatasBanco(), []);
        $dataRef = $this->repo->dataReferencia();
        $eHoje   = $this->repo->eHoje();

        return [
            'data_referencia'  => $dataRef,
            'data_ref_fmt'     => $this->formatarData($dataRef),
            'ano_ref'          => $this->repo->anoReferencia(),
            'mes_ref'          => $this->repo->mesReferencia(),
            'e_hoje'           => $eHoje,
            'banco_inicio'     => $this->formatarData($range['INICIO'] ?? null),
            'banco_fim'        => $this->formatarData($range['FIM']    ?? null),
            'total_notas'      => (int)($range['TOTAL_NOTAS'] ?? 0),
            'filtro_ano'       => $this->filtroAno,
            'filtro_mes'       => $this->filtroMes,
            'anos_disponiveis' => $this->seguro(fn() => $this->repo->anosDisponiveis(), []),
            'meses_disponiveis'=> $this->filtroAno
                ? $this->seguro(fn() => $this->repo->mesesDisponiveis($this->filtroAno), [])
                : [],
        ];
    }

    // -------------------------------------------------------------------------
    // Indicadores do período de referência
    // -------------------------------------------------------------------------

    public function indicadores(): array
    {
        $hoje       = $this->seguro(fn() => $this->repo->faturamentoHoje(),   0.0);
        $ontem      = $this->seguro(fn() => $this->repo->faturamentoOntem(),  0.0);
        $mes        = $this->seguro(fn() => $this->repo->faturamentoMes(),    0.0);
        $mesAnt     = $this->seguro(fn() => $this->repo->faturamentoMesAnterior(), 0.0);
        $mesAAnt    = $this->seguro(fn() => $this->repo->faturamentoMesmoMesAnoAnterior(), 0.0);

        // Projeção do mês (extrapolação linear pelo dia de referência)
        $dataRef  = $this->repo->dataReferencia();
        $diaRef   = (int) substr($dataRef, 8, 2);
        $diasMes  = cal_days_in_month(CAL_GREGORIAN, $this->repo->mesReferencia(), $this->repo->anoReferencia());
        $projecao = $diaRef > 0 ? round($mes / $diaRef * $diasMes, 2) : 0.0;
        $mediaDia = $diaRef > 0 ? round($mes / $diaRef, 2) : 0.0;

        return [
            // — Hoje —
            'faturamento_hoje'        => $hoje,
            'total_vendas_hoje'       => $this->seguro(fn() => $this->repo->totalVendasHoje(), 0),
            'ticket_medio'            => $this->seguro(fn() => $this->repo->ticketMedioHoje(), 0.0),
            'delta_hoje_ontem'        => $ontem > 0 ? round(($hoje - $ontem) / $ontem * 100, 1) : null,

            // — Mês —
            'faturamento_mes'         => $mes,
            'total_vendas_mes'        => $this->seguro(fn() => $this->repo->totalVendasMes(), 0),
            'media_diaria'            => $mediaDia,
            'projecao_mes'            => $projecao,
            'delta_mes_anterior'      => $mesAnt  > 0 ? round(($mes - $mesAnt)  / $mesAnt  * 100, 1) : null,
            'delta_mes_ano_anterior'  => $mesAAnt > 0 ? round(($mes - $mesAAnt) / $mesAAnt * 100, 1) : null,

            // — Financeiro —
            'mesas_abertas'           => $this->seguro(fn() => $this->repo->mesasAbertas(), 0),
            'contas_receber'          => $this->seguro(fn() => $this->repo->contasReceberVencer(), 0.0),
            'contas_pagar'            => $this->seguro(fn() => $this->repo->contasPagarVencer(), 0.0),

            // — Operacional —
            'clientes_ativos_mes'     => $this->seguro(fn() => $this->repo->clientesAtivosMes(), 0),
            'melhor_vendedor'         => $this->seguro(fn() => $this->repo->melhorVendedorMes(), ['NOME'=>'—','TOTAL'=>0,'QTD'=>0]),
        ];
    }

    public function listaMesas(): array
    {
        return $this->seguro(fn() => $this->repo->listaMesasAbertas(), []);
    }

    public function topProdutos(string $periodo = 'hoje'): array
    {
        if ($periodo === 'semana') {
            return $this->seguro(fn() => $this->repo->topProdutosSemana(), []);
        }
        return $this->seguro(fn() => $this->repo->topProdutosHoje(), []);
    }

    // -------------------------------------------------------------------------
    // Histórico
    // -------------------------------------------------------------------------

    public function historicoAnual(): array
    {
        return $this->seguro(fn() => $this->repo->faturamentoAnualHistorico(), []);
    }

    public function historicoMensal(): array
    {
        return $this->seguro(fn() => $this->repo->faturamentoMensalHistorico(), []);
    }

    public function topProdutosHistorico(): array
    {
        return $this->seguro(fn() => $this->repo->topProdutosHistorico(15), []);
    }

    public function topDiasHistorico(): array
    {
        return $this->seguro(fn() => $this->repo->topDiasHistorico(10), []);
    }

    // -------------------------------------------------------------------------
    // Gráficos
    // -------------------------------------------------------------------------

    public function graficoFaturamentoDiario(int $dias = 30): array
    {
        $rows    = $this->seguro(fn() => $this->repo->faturamentoDiario($dias), []);
        $labels  = [];
        $valores = [];
        foreach ($rows as $row) {
            $labels[]  = $this->dataParaDDMM($row['DIA']);
            $valores[] = round((float)$row['TOTAL'], 2);
        }
        return ['labels' => $labels, 'valores' => $valores];
    }

    public function graficoHorasPico(): array
    {
        $rows    = $this->seguro(fn() => $this->repo->faturamentoPorHoraHoje(), []);
        $labels  = [];
        $valores = [];
        foreach ($rows as $row) {
            $labels[]  = str_pad((string)(int)$row['HORA'], 2, '0', STR_PAD_LEFT) . 'h';
            $valores[] = round((float)$row['TOTAL'], 2);
        }
        return ['labels' => $labels, 'valores' => $valores];
    }

    public function graficoHistoricoMensal(): array
    {
        $rows    = $this->historicoMensal();
        $labels  = [];
        $valores = [];
        $meses   = ['','Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'];
        foreach ($rows as $row) {
            $labels[]  = $meses[(int)$row['MES']] . '/' . $row['ANO'];
            $valores[] = round((float)$row['TOTAL'], 2);
        }
        return ['labels' => $labels, 'valores' => $valores];
    }

    public function graficoHistoricoAnual(): array
    {
        $rows    = $this->historicoAnual();
        $labels  = [];
        $valores = [];
        $tickets = [];
        foreach ($rows as $row) {
            $labels[]  = (string)$row['ANO'];
            $valores[] = round((float)$row['TOTAL'], 2);
            $tickets[] = round((float)$row['TICKET_MEDIO'], 2);
        }
        return ['labels' => $labels, 'valores' => $valores, 'tickets' => $tickets];
    }

    /**
     * Últimos 12 meses + média móvel de 3 meses (suaviza sazonalidade).
     * Ideal para identificar tendência de crescimento ou queda.
     */
    public function graficoRolling12m(): array
    {
        $rows  = $this->historicoMensal();
        $rows  = array_slice($rows, -12);   // garante máx. 12 meses

        $meses   = ['', 'Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun',
                        'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'];
        $labels  = [];
        $valores = [];

        foreach ($rows as $row) {
            $labels[]  = $meses[(int)$row['MES']] . '/' . substr((string)$row['ANO'], -2);
            $valores[] = round((float)$row['TOTAL'], 2);
        }

        // Média móvel simples 3 meses (começa no 3.º ponto)
        $n   = count($valores);
        $ma3 = [];
        for ($i = 0; $i < $n; $i++) {
            $ma3[] = $i >= 2
                ? round(($valores[$i - 2] + $valores[$i - 1] + $valores[$i]) / 3, 2)
                : null;
        }

        return ['labels' => $labels, 'valores' => $valores, 'ma3' => $ma3];
    }

    // -------------------------------------------------------------------------
    // Helpers
    // -------------------------------------------------------------------------

    private function formatarData(mixed $data): string
    {
        if (!$data) return '';
        if ($data instanceof \DateTime) return $data->format('d/m/Y');
        $s = trim((string)$data);
        if (strlen($s) >= 10) {
            [$a, $m, $d] = explode('-', substr($s, 0, 10));
            return "{$d}/{$m}/{$a}";
        }
        return $s;
    }

    private function dataParaDDMM(mixed $data): string
    {
        if ($data instanceof \DateTime) return $data->format('d/m');
        $s = trim((string)$data);
        if (strlen($s) >= 10) return substr($s, 8, 2) . '/' . substr($s, 5, 2);
        return $s;
    }

    // -------------------------------------------------------------------------
    // Meta vs Realizado + Projeção
    // -------------------------------------------------------------------------

    public function metaVsRealizado(): array
    {
        // Metas DA LOJA ATIVA (multi-loja = arquivo por loja; fallback no global).
        $metas = \App\Core\Lojas::lerMetas();

        $ano   = $this->repo->anoReferencia();
        $mes   = $this->repo->mesReferencia();
        $dataRef = $this->repo->dataReferencia();

        $meta      = (float)($metas[$ano][$mes] ?? 0);
        $realizado = $this->seguro(fn() => $this->repo->faturamentoMes(), 0.0);

        $diaRef    = (int)substr($dataRef, 8, 2);
        $diasMes   = cal_days_in_month(CAL_GREGORIAN, $mes, $ano);
        $diasRest  = $diasMes - $diaRef;

        $projecao   = $diaRef > 0 ? $realizado / $diaRef * $diasMes : 0;
        $percMeta   = $meta > 0 ? min($realizado / $meta * 100, 200) : 0;
        $percProj   = $meta > 0 ? min($projecao  / $meta * 100, 200) : 0;
        $gap        = $meta - $realizado;
        $mediaDia   = $diaRef > 0 ? $realizado / $diaRef : 0;

        return [
            'tem_meta'    => $meta > 0,
            'meta'        => $meta,
            'realizado'   => $realizado,
            'projecao'    => $projecao,
            'gap'         => $gap,
            'perc_meta'   => round($percMeta, 1),
            'perc_proj'   => round($percProj, 1),
            'dia_ref'     => $diaRef,
            'dias_mes'    => $diasMes,
            'dias_rest'   => $diasRest,
            'media_dia'   => $mediaDia,
            'ano'         => $ano,
            'mes'         => $mes,
        ];
    }

    // -------------------------------------------------------------------------
    // Alertas gerenciais
    // -------------------------------------------------------------------------

    public function alertas(): array
    {
        $pdo = Database::conectar();

        $finKpis  = $this->seguro(fn() => (new FinanceiroRepository($pdo))->kpis(), []);
        $estKpis  = $this->seguro(fn() => (new EstoqueRepository($pdo))->kpisMinimos(), []);
        $cliRepo  = new ClientesRepository($pdo);

        return [
            'inadimplencia_valor'  => (float)($finKpis['VENCIDOS_RECEBER'] ?? 0),
            'a_receber_valor'      => (float)($finKpis['A_RECEBER']        ?? 0),
            'pagar_vencido_valor'  => (float)($finKpis['VENCIDOS_PAGAR']   ?? 0),
            'a_pagar_valor'        => (float)($finKpis['A_PAGAR']          ?? 0),
            'estoque_abaixo_qtd'   => (int)($estKpis['TOTAL_ABAIXO']       ?? 0),
            'estoque_deficit'      => (float)($estKpis['DEFICIT_TOTAL']    ?? 0),
            'clientes_inativos_60' => $this->seguro(fn() => $cliRepo->countInativos(60), 0),
            'clientes_inativos_90' => $this->seguro(fn() => $cliRepo->countInativos(90), 0),
        ];
    }

    private function seguro(callable $fn, mixed $padrao = 0): mixed
    {
        try {
            return $fn();
        } catch (\Throwable) {
            return $padrao;
        }
    }
}
