<?php
namespace App\Controllers;

use App\Core\Database;
use App\Core\View;

class PrevisaoController
{
    private function cfg(): array
    {
        $cfgFile = dirname(__DIR__, 2) . '/config/database.php';
        if (!file_exists($cfgFile)) return [];
        return require $cfgFile;
    }

    /**
     * @deprecated v1.4.17 — use VendasRepository::filtroEmpresaSql() em vez disto.
     * Lê cod_empresa direto do config/database.php; não respeita troca de loja
     * em runtime (Lojas::codEmpresaAtiva). Mantido por compatibilidade.
     */
    private function filtroEmpresa(array $cfg, string $alias = 'nc'): string
    {
        $cod = isset($cfg['cod_empresa']) && $cfg['cod_empresa'] ? (int)$cfg['cod_empresa'] : null;
        return $cod ? "AND {$alias}.COD_EMPRESA = {$cod}" : '';
    }

    private function jsonOk(mixed $dados): void
    {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(['ok' => true, 'dados' => $dados], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    private function jsonErro(string $msg, int $status = 500): void
    {
        http_response_code($status);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(['ok' => false, 'erro' => $msg], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
        exit;
    }

    // Regressao Linear Simples
    private function calcRegressaoLinear(array $valores): array
    {
        $n = count($valores);
        if ($n < 2) return ['a' => $valores[0] ?? 0, 'b' => 0, 'r2' => 0];

        $xs = range(0, $n - 1);
        $sumX = array_sum($xs);
        $sumY = array_sum($valores);
        $sumXY = 0; $sumX2 = 0; $sumY2 = 0;
        $mediaY = $sumY / $n;

        foreach ($xs as $i => $x) {
            $sumXY += $x * $valores[$i];
            $sumX2 += $x * $x;
            $sumY2 += pow($valores[$i] - $mediaY, 2);
        }

        $denom = ($n * $sumX2 - $sumX * $sumX);
        if ($denom == 0) return ['a' => $mediaY, 'b' => 0, 'r2' => 0];

        $b = ($n * $sumXY - $sumX * $sumY) / $denom;
        $a = ($sumY - $b * $sumX) / $n;

        $ssRes = 0;
        foreach ($xs as $i => $x) {
            $ssRes += pow($valores[$i] - ($a + $b * $x), 2);
        }
        $r2 = $sumY2 > 0 ? 1 - ($ssRes / $sumY2) : 0;

        return ['a' => $a, 'b' => $b, 'r2' => max(0, round($r2, 4))];
    }

    // Fatores Sazonais por mes (1-12)
    private function calcFatoresSazonais(array $historico): array
    {
        $fatores = array_fill(1, 12, 1.0);
        if (empty($historico)) return $fatores;

        $totalGeral = 0.0;
        $count = 0;
        foreach ($historico as $h) { $totalGeral += (float)$h['valor']; $count++; }
        if ($count === 0 || $totalGeral <= 0) return $fatores;

        $mediaGeral = $totalGeral / $count;
        $porMes = [];
        foreach ($historico as $h) {
            $m = (int)$h['mes'];
            $porMes[$m][] = (float)$h['valor'];
        }
        foreach ($porMes as $mes => $vals) {
            if (!empty($vals)) {
                $fatores[$mes] = (array_sum($vals) / count($vals)) / $mediaGeral;
            }
        }
        return $fatores;
    }

    // Desvio Padrao dos Residuos
    private function calcStdResiduos(array $valores, float $a, float $b): float
    {
        $n = count($valores);
        if ($n < 2) return 0.0;
        $ssRes = 0.0;
        foreach ($valores as $i => $v) {
            $ssRes += pow($v - ($a + $b * $i), 2);
        }
        return sqrt($ssRes / $n);
    }

    // GET /previsao
    public function index(): void
    {
        $cfg = $this->cfg();
        if (empty($cfg['host'])) {
            header('Location: /configurar');
            exit;
        }
        View::render('previsao/index', [
            'titulo'   => 'Previsao de Vendas',
            'config'   => $cfg,
            'ano'      => (int)date('Y'),
            'dataHoje' => date('d/m/Y'),
        ]);
    }

    // GET /api/previsao/data
    public function api(): void
    {
        $cfg = $this->cfg();
        if (empty($cfg['host'])) $this->jsonErro('Banco nao configurado', 503);

        try {
            $pdo = Database::conectar();
        } catch (\Throwable $e) {
            $this->jsonErro('Falha na conexao: ' . $e->getMessage());
        }

        // v1.4.17 fix: delega o filtro de venda + empresa ao VendasRepository.
        // Antes a query embutia TIPO_NATUREZA='V' + MOV_INTERNA='N' + ETRANSF='N'
        // + DEVOLUCAO='N' direto no SQL, o que ignorava o override
        // 'tipos_venda_extras' do config/database.php — cliente Real Prime tinha
        // op 115 com MOV_INTERNA='S' e a previsao zerava. Agora respeita o
        // mesmo IN(...) usado em /vendas, /dashboard e /comparativo.
        $vendasRepo = new \App\Repositories\VendasRepository($pdo);
        $filtroV    = $vendasRepo->filtroVendaSql('nc');
        $fe         = $vendasRepo->filtroEmpresaSql('nc');
        $anoIni     = (int)date('Y') - 3;
        $hoje       = date('Y-m-d');

        // Buscar historico mensal dos ultimos 36 meses
        // Sem JOIN com TIPO_OPERACAO: filtroVendaSql ja resolve a lista de ops.
        $rows = [];
        try {
            $rows = $pdo->query("
                SELECT
                    EXTRACT(YEAR  FROM nc.DATA_EMISSAO) AS ANO,
                    EXTRACT(MONTH FROM nc.DATA_EMISSAO) AS MES,
                    COALESCE(SUM(nc.TOTAL_NOTA), 0)     AS TOTAL
                FROM NOTAS_CAB nc
                WHERE nc.DATA_EMISSAO >= '{$anoIni}-01-01'
                  AND nc.DATA_EMISSAO < DATEADD(DAY, 1, CAST('{$hoje}' AS DATE))
                  AND nc.DATA_CANC IS NULL
                  AND nc.TOTAL_NOTA > 0
                  {$filtroV}
                  {$fe}
                GROUP BY EXTRACT(YEAR FROM nc.DATA_EMISSAO), EXTRACT(MONTH FROM nc.DATA_EMISSAO)
                ORDER BY ANO, MES
            ")->fetchAll(\PDO::FETCH_ASSOC);
        } catch (\Throwable $e) {
            $this->jsonErro('Erro ao buscar historico: ' . $e->getMessage());
        }

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

        $historico = [];
        foreach ($rows as $r) {
            $ano = (int)$r['ANO'];
            $mes = (int)$r['MES'];
            $historico[] = [
                'label' => $meses[$mes] . '/' . $ano,
                'ano'   => $ano,
                'mes'   => $mes,
                'valor' => round((float)$r['TOTAL'], 2),
            ];
        }

        if (count($historico) < 3) {
            $this->jsonErro('Historico insuficiente para calcular previsao (minimo 3 meses de dados)');
        }

        // Ultimos 24 meses como base
        $base    = array_slice($historico, -24);
        $valores = array_column($base, 'valor');

        $reg    = $this->calcRegressaoLinear($valores);
        $a      = $reg['a'];
        $b      = $reg['b'];
        $r2     = $reg['r2'];
        $stdDev = $this->calcStdResiduos($valores, $a, $b);
        $fatores = $this->calcFatoresSazonais($historico);

        // Projetar proximos 12 meses
        $anoAtual = (int)date('Y');
        $mesAtual = (int)date('m');
        $n        = count($valores);
        $previsao = [];

        for ($i = 1; $i <= 12; $i++) {
            $mesPrev = $mesAtual + $i;
            $anoPrev = $anoAtual;
            while ($mesPrev > 12) { $mesPrev -= 12; $anoPrev++; }

            $idx   = $n - 1 + $i;
            $proj  = max(0.0, ($a + $b * $idx) * ($fatores[$mesPrev] ?? 1.0));
            $previsao[] = [
                'label'    => $meses[$mesPrev] . '/' . $anoPrev,
                'ano'      => $anoPrev,
                'mes'      => $mesPrev,
                'valor'    => round($proj, 2),
                'inferior' => round(max(0.0, $proj - $stdDev), 2),
                'superior' => round($proj + $stdDev, 2),
            ];
        }

        $mediaHist    = count($valores) > 0 ? array_sum($valores) / count($valores) : 0.0;
        $proximos     = array_column($previsao, 'valor');

        $this->jsonOk([
            'historico' => $historico,
            'previsao'  => $previsao,
            'kpis'      => [
                'proximo_mes'      => $proximos[0] ?? 0.0,
                'proximos_3m'      => round(array_sum(array_slice($proximos, 0, 3)), 2),
                'proximos_6m'      => round(array_sum(array_slice($proximos, 0, 6)), 2),
                'proximos_12m'     => round(array_sum($proximos), 2),
                'tendencia_mensal' => round($b, 2),
                'tendencia_pct'    => $mediaHist > 0 ? round(($b / $mediaHist) * 100, 2) : 0.0,
                'r2'               => $r2,
                'media_historico'  => round($mediaHist, 2),
                'meses_base'       => count($base),
            ],
        ]);
    }
}
