<?php

declare(strict_types=1);

/**
 * Serviço de cálculo do Dashboard (Orçado x Realizado).
 * Não faz saída HTML/JSON: apenas retorna um array com os dados calculados.
 */

function calcularFormulaDashboardInterno(string $formula, array $valores): float {
    $formula = trim($formula);
    if ($formula === '') return 0.0;

    preg_match_all('/\d+|[\+\-\*\/\(\)]/', $formula, $m);
    $tokens = $m[0] ?? [];
    if (empty($tokens)) return 0.0;

    $prec   = ['+' => 1, '-' => 1, '*' => 2, '/' => 2];
    $output = [];
    $ops    = [];

    foreach ($tokens as $t) {
        if (ctype_digit($t)) {
            $output[] = $t;
        } elseif (isset($prec[$t])) {
            while (!empty($ops)) {
                $top = end($ops);
                if ($top === '(') break;
                if ($prec[$top] >= $prec[$t]) {
                    $output[] = array_pop($ops);
                } else {
                    break;
                }
            }
            $ops[] = $t;
        } elseif ($t === '(') {
            $ops[] = $t;
        } elseif ($t === ')') {
            while (!empty($ops) && end($ops) !== '(') {
                $output[] = array_pop($ops);
            }
            array_pop($ops); // remove "("
        }
    }

    while (!empty($ops)) {
        $output[] = array_pop($ops);
    }

    $stack = [];
    foreach ($output as $t) {
        if (ctype_digit($t)) {
            $cid      = (int)$t;
            $stack[]  = $valores[$cid] ?? 0.0;
        } elseif (isset($prec[$t])) {
            $b = array_pop($stack) ?? 0.0;
            $a = array_pop($stack) ?? 0.0;
            switch ($t) {
                case '+': $stack[] = $a + $b; break;
                case '-': $stack[] = $a - $b; break;
                case '*': $stack[] = $a * $b; break;
                case '/': $stack[] = ($b == 0.0 ? 0.0 : $a / $b); break;
            }
        }
    }

    return (float)($stack[0] ?? 0.0);
}

function obterDadosDashboard(PDO $pdo): array {
    // Empresa atual
    $cdempresa = (int)($_SESSION['cdempresa'] ?? 0);
    if ($cdempresa <= 0) {
        return [
            'ok'  => false,
            'msg' => 'Empresa não encontrada na sessão.',
        ];
    }

    // Exercício base (último ativo)
    $stmt = $pdo->prepare("
        SELECT anoref
          FROM back.exercicio
         WHERE cdempresa = :emp
           AND flativo   = 0
         ORDER BY anoref DESC
         LIMIT 1
    ");
    $stmt->execute([':emp' => $cdempresa]);
    $anoref = (int)($stmt->fetchColumn() ?: date('Y'));

    // Moeda da empresa
    $stmt = $pdo->prepare("
        SELECT m.simbolo, m.formato
          FROM back.empresa e
     LEFT JOIN back.moeda   m ON m.cdmoeda = e.cdmoeda
         WHERE e.cdempresa = :emp
    ");
    $stmt->execute([':emp' => $cdempresa]);
    $moeda = $stmt->fetch(PDO::FETCH_ASSOC) ?: ['simbolo' => 'R$', 'formato' => 'decimal'];

    // Variáveis do tipo Dashboard + seções ativas
    $sql = "
        SELECT v.cdvariavel, v.nome, v.ordem AS ordem_variavel, v.modelo,
               s.cdsecao, s.titulo, s.mascara, s.formula, s.ordem
          FROM back.variavel v
          JOIN back.variavel_secao s ON s.cdvariavel = v.cdvariavel
         WHERE v.tipo   = 'Dashboard'
           AND v.flativo = 0
           AND s.flativo = 0
      ORDER BY v.ordem NULLS LAST, v.cdvariavel, s.ordem
    ";

    $rows = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
    if (!$rows) {
        return [
            'ok'  => false,
            'msg' => 'Nenhuma variável de Dashboard ativa configurada.',
        ];
    }

    $dashboards   = [];
    $contasUsadas = [];

    foreach ($rows as $r) {
        $vid = (int)$r['cdvariavel'];
        if (!isset($dashboards[$vid])) {
            $dashboards[$vid] = [
                'cdvariavel' => $vid,
                'nome'       => $r['nome'],
                'modelo'     => $r['modelo'] ?? 'orcamento',
                'secoes'     => [],
            ];
        }
        $dashboards[$vid]['secoes'][] = $r;

        if (!empty($r['formula'])) {
            preg_match_all('/\d+/', $r['formula'], $m);
            foreach ($m[0] as $cd) {
                $contasUsadas[(int)$cd] = true;
            }
        }
    }

    $contasFiltradas = array_keys($contasUsadas);
    if (empty($contasFiltradas)) {
        return [
            'ok'  => false,
            'msg' => 'As fórmulas de Dashboard não possuem contas contábeis vinculadas.',
        ];
    }

    // Mapas de valores por conta (totais)
    $valoresRealizadosAno      = array_fill_keys($contasFiltradas, 0.0);
    $valoresRealizadosMes      = array_fill_keys($contasFiltradas, 0.0);
    $valoresOrcadosAno         = array_fill_keys($contasFiltradas, 0.0);
    $valoresOrcadosMes         = array_fill_keys($contasFiltradas, 0.0);
    $valoresRealizadosAnoAnt   = array_fill_keys($contasFiltradas, 0.0);
    $valoresRealizadosMesAnt   = array_fill_keys($contasFiltradas, 0.0);

    // Mapas mensais para série YTD (ano atual x anterior)
    $valoresRealizadosMeses = [];
    foreach ([$anoref, $anoref - 1] as $anoMap) {
        $valoresRealizadosMeses[$anoMap] = [];
        for ($m = 1; $m <= 12; $m++) {
            $valoresRealizadosMeses[$anoMap][$m] = array_fill_keys($contasFiltradas, 0.0);
        }
    }

    $placeholders = implode(',', array_fill(0, count($contasFiltradas), '?'));

    // Mês atual e ano anterior
    $mesAtual       = (int)date('n');
    $anorefAnterior = $anoref - 1;

    // ============================
    // Realizado (ANO, MÊS, ANO ANT., MÊS ANT.)
    // Uma única consulta para os dois anos e todos os meses
    // ============================
    $sqlReal = "
        SELECT cdcontacontabil,
               anoref,
               mesref,
               COALESCE(SUM(valor_mes),0) AS total
          FROM back.v_realizado_conta_mes
         WHERE cdempresa       = ?
           AND anoref          IN (?, ?)
           AND cdcontacontabil IN ($placeholders)
      GROUP BY cdcontacontabil, anoref, mesref
    ";

    $paramsReal = array_merge([$cdempresa, $anoref, $anorefAnterior], $contasFiltradas);
    $stmt = $pdo->prepare($sqlReal);
    $stmt->execute($paramsReal);
    while ($r = $stmt->fetch(PDO::FETCH_ASSOC)) {
        $cid    = (int)$r['cdcontacontabil'];
        $anoRow = (int)$r['anoref'];
        $mesRow = (int)$r['mesref'];
        $total  = (float)$r['total'];

        if (!isset($valoresRealizadosMeses[$anoRow][$mesRow][$cid])) {
            // Segurança, embora os arrays já tenham sido inicializados
            $valoresRealizadosMeses[$anoRow][$mesRow][$cid] = 0.0;
        }
        $valoresRealizadosMeses[$anoRow][$mesRow][$cid] += $total;

        if ($anoRow === $anoref) {
            // Ano corrente
            $valoresRealizadosAno[$cid] += $total;
            if ($mesRow === $mesAtual) {
                $valoresRealizadosMes[$cid] += $total;
            }
        } elseif ($anoRow === $anorefAnterior) {
            // Ano anterior
            $valoresRealizadosAnoAnt[$cid] += $total;
            if ($mesRow === $mesAtual) {
                $valoresRealizadosMesAnt[$cid] += $total;
            }
        }
    }

    // ============================
    // Orçado (ANO, MÊS)
    // Única consulta para o ano inteiro; mês atual calculado no loop
    // ============================
    $sqlOrcado = "
        SELECT cdcontacontabil,
               anoref,
               mesref,
               COALESCE(SUM(valor_mes),0) AS total
          FROM back.v_orcado_conta_mes
         WHERE cdempresa       = ?
           AND anoref          = ?
           AND cdcontacontabil IN ($placeholders)
      GROUP BY cdcontacontabil, anoref, mesref
    ";

    $paramsOrcado = array_merge([$cdempresa, $anoref], $contasFiltradas);
    $stmt = $pdo->prepare($sqlOrcado);
    $stmt->execute($paramsOrcado);
    while ($r = $stmt->fetch(PDO::FETCH_ASSOC)) {
        $cid    = (int)$r['cdcontacontabil'];
        $mesRow = (int)$r['mesref'];
        $total  = (float)$r['total'];

        $valoresOrcadosAno[$cid] += $total;
        if ($mesRow === $mesAtual) {
            $valoresOrcadosMes[$cid] += $total;
        }
    }

    // Datas para projeções
    $diaAtual  = (int)date('j');      // dia do mês (1-31)
    $diasNoMes = (int)date('t');      // dias do mês atual
    $diaAno    = (int)date('z') + 1;  // dia do ano
    $diasNoAno = date('L') ? 366 : 365;

    // Montar estrutura final por variável/seção
    $saidaDash = [];
    foreach ($dashboards as $vid => $dash) {
        $outSecoes = [];
        foreach ($dash['secoes'] as $sec) {
            $formula = $sec['formula'];

            $orcAno     = calcularFormulaDashboardInterno($formula, $valoresOrcadosAno);
            $realAno    = calcularFormulaDashboardInterno($formula, $valoresRealizadosAno);
            $realAnoAnt = calcularFormulaDashboardInterno($formula, $valoresRealizadosAnoAnt);
            $orcMes     = calcularFormulaDashboardInterno($formula, $valoresOrcadosMes);
            $realMes    = calcularFormulaDashboardInterno($formula, $valoresRealizadosMes);
            $realMesAnt = calcularFormulaDashboardInterno($formula, $valoresRealizadosMesAnt);

            $atingAno = ($orcAno != 0.0) ? ($realAno / $orcAno) : 0.0;
            $restAno  = max(0.0, $orcAno - $realAno);
            $atingMes = ($orcMes != 0.0) ? ($realMes / $orcMes) : 0.0;
            $restMes  = max(0.0, $orcMes - $realMes);

            $varAnoVsAnt = ($realAnoAnt != 0.0) ? (($realAno - $realAnoAnt) / abs($realAnoAnt)) : null;
            $varMesVsAnt = ($realMesAnt != 0.0) ? (($realMes - $realMesAnt) / abs($realMesAnt)) : null;

            $projMesValor = ($diaAtual > 0) ? ($realMes / $diaAtual * $diasNoMes) : 0.0;
            $projMesPerc  = ($orcMes != 0.0) ? ($projMesValor / $orcMes) : 0.0;

            $projAnoValor = ($diaAno > 0) ? ($realAno / $diaAno * $diasNoAno) : 0.0;
            $projAnoPerc  = ($orcAno != 0.0) ? ($projAnoValor / $orcAno) : 0.0;

            $outSecoes[] = [
                'cdsecao'  => (int)$sec['cdsecao'],
                'titulo'   => $sec['titulo'],
                'mascara'  => $sec['mascara'],
                'formula'  => $formula,
                'ano'      => [
                    'orcado'           => $orcAno,
                    'realizado'        => $realAno,
                    'atingidoPerc'     => $atingAno,
                    'restanteValor'    => $restAno,
                    'restantePerc'     => max(0.0, 1.0 - $atingAno),
                    'realizadoAnterior'=> $realAnoAnt,
                    'varAnteriorPerc'  => $varAnoVsAnt,
                    'projValor'        => $projAnoValor,
                    'projPerc'         => $projAnoPerc,
                ],
                'mes'      => [
                    'orcado'           => $orcMes,
                    'realizado'        => $realMes,
                    'atingidoPerc'     => $atingMes,
                    'restanteValor'    => $restMes,
                    'restantePerc'     => max(0.0, 1.0 - $atingMes),
                    'realizadoAnterior'=> $realMesAnt,
                    'varAnteriorPerc'  => $varMesVsAnt,
                    'projValor'        => $projMesValor,
                    'projPerc'         => $projMesPerc,
                ],
            ];
        }

        $saidaDash[] = [
            'cdvariavel' => $vid,
            'nome'       => $dash['nome'],
            'modelo'     => $dash['modelo'],
            'secoes'     => $outSecoes,
        ];
    }

    // ============================
    // Série YTD (Year-to-Date) para o primeiro dashboard
    // Compara realizado acumulado mês a mês: ano atual x ano anterior
    // Até o mês corrente (corrida ao longo do ano)
    // ============================
    $serieYtd = null;
    if (!empty($dashboards)) {
        $firstId   = array_key_first($dashboards);
        $dashMain  = $dashboards[$firstId];
        $labelsMesBase = ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'];

        $ultimoMes = $mesAtual > 0 && $mesAtual <= 12 ? $mesAtual : 12;
        $labelsMes = array_slice($labelsMesBase, 0, $ultimoMes);

        $acumAtual    = [];
        $acumAnterior = [];
        $somaAtual    = 0.0;
        $somaAnterior = 0.0;

        for ($m = 1; $m <= $ultimoMes; $m++) {
            $mapAtual    = $valoresRealizadosMeses[$anoref][$m]       ?? [];
            $mapAnterior = $valoresRealizadosMeses[$anorefAnterior][$m] ?? [];

            $valorMesAtual    = 0.0;
            $valorMesAnterior = 0.0;
            foreach ($dashMain['secoes'] as $secMain) {
                $f = $secMain['formula'];
                if ($f === null || $f === '') {
                    continue;
                }
                $valorMesAtual    += calcularFormulaDashboardInterno($f, $mapAtual);
                $valorMesAnterior += calcularFormulaDashboardInterno($f, $mapAnterior);
            }

            $somaAtual    += $valorMesAtual;
            $somaAnterior += $valorMesAnterior;
            $acumAtual[]    = $somaAtual;
            $acumAnterior[] = $somaAnterior;
        }

        $serieYtd = [
            'nome'         => $dashMain['nome'],
            'labels'       => $labelsMes,
            'anoAtual'     => $acumAtual,
            'anoAnterior'  => $acumAnterior,
        ];
    }

    return [
        'ok'       => true,
        'empresa'  => $cdempresa,
        'anoref'   => $anoref,
        'mesAtual' => $mesAtual,
        'moeda'    => $moeda,
        'dashboards'=> $saidaDash,
        'serieYtd' => $serieYtd,
    ];
}
