<?php
require_once(dirname(__DIR__) . '/config/db.php');
require_once(__DIR__ . '/home_service.php');

ini_set('display_errors', '0');
error_reporting(E_ALL);

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$cdempresa = $_SESSION['cdempresa'] ?? 1;

function json_out_var($arr, $code = 200) {
  http_response_code($code);
  header('Content-Type: application/json; charset=utf-8');
  echo json_encode($arr, JSON_UNESCAPED_UNICODE);
  exit;
}

set_error_handler(function($severity, $message, $file, $line) {
  $loc = basename($file) . ':' . $line;
  throw new ErrorException("$message @ $loc", 0, $severity, $file, $line);
});

set_exception_handler(function($e) {
  $loc = basename($e->getFile()) . ':' . $e->getLine();
  $msg = $e->getMessage();
  json_out_var(['ok'=>false,'msg'=>"Erro interno ($loc): $msg"], 500);
});

$ajax = $_GET['ajax'] ?? '';
if ($ajax === '') { http_response_code(204); exit; }

switch ($ajax) {
  case 'comparar_periodos':
    compararVariaveis();
    break;

  default:
    json_out_var(['ok' => false, 'msg' => 'Ação inválida'], 400);
}

/* ============================================================
 * COMPARAR VARIÁVEIS — Período A vs B ou Orçado vs Realizado
 * ============================================================ */
function compararVariaveis() {
  global $pdo, $cdempresa;

  $anoA   = (int)($_GET['ano_a'] ?? 0);
  $anoB   = (int)($_GET['ano_b'] ?? 0);
  $mesesA = array_filter(array_map('intval', (array)($_GET['meses_a'] ?? [])));
  $mesesB = array_filter(array_map('intval', (array)($_GET['meses_b'] ?? [])));
  $modo   = in_array($_GET['modo'] ?? '', ['periodo','orcreal']) ? $_GET['modo'] : 'periodo';
  $cdvariaveis = !empty($_GET['cdvariavel']) ? array_map('intval', (array)$_GET['cdvariavel']) : [];

  if (!$anoA || !$anoB || empty($mesesA) || empty($mesesB)) {
    json_out_var(['ok' => false, 'msg' => 'Informe os dois períodos para comparar.'], 400);
  }

  foreach (array_merge($mesesA, $mesesB) as $m) {
    if ($m < 1 || $m > 12) {
      json_out_var(['ok' => false, 'msg' => 'Mês inválido.'], 400);
    }
  }

  // ---------- Carregar variáveis + seções ----------
  $sqlVar = "
    SELECT v.cdvariavel, v.nome, v.tipo, 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 = 'Relatorio'
       AND v.flativo = 0
       AND s.flativo = 0
       AND EXISTS (SELECT 1 FROM back.variavel_empresa ve WHERE ve.cdvariavel = v.cdvariavel AND ve.cdempresa = :emp)
  ";
  $paramsVar = [':emp' => $cdempresa];
  if (!empty($cdvariaveis)) {
    $placeholders = [];
    foreach ($cdvariaveis as $i => $cv) {
      $key = ":cdvar{$i}";
      $placeholders[] = $key;
      $paramsVar[$key] = $cv;
    }
    $sqlVar .= " AND v.cdvariavel IN (" . implode(',', $placeholders) . ")";
  }
  $sqlVar .= " ORDER BY v.ordem NULLS LAST, v.cdvariavel, s.ordem";
  $stVar = $pdo->prepare($sqlVar);
  $stVar->execute($paramsVar);
  $rowsVar = $stVar->fetchAll(PDO::FETCH_ASSOC);

  if (empty($rowsVar)) {
    json_out_var(['ok' => true, 'variaveis' => [], 'modo' => $modo]);
  }

  // Agrupar por variável
  $variaveis = [];
  foreach ($rowsVar as $r) {
    $vid = (int)$r['cdvariavel'];
    if (!isset($variaveis[$vid])) {
      $variaveis[$vid] = [
        'cdvariavel' => $vid,
        'nome'       => $r['nome'],
        'tipo'       => $r['tipo'],
        'modelo'     => $r['modelo'],
        'secoes'     => [],
      ];
    }
    $variaveis[$vid]['secoes'][] = [
      'cdsecao'  => (int)$r['cdsecao'],
      'titulo'   => $r['titulo'],
      'mascara'  => $r['mascara'],
      'formula'  => $r['formula'],
    ];
  }

  // ---------- Coletar todas as contas referenciadas nas fórmulas ----------
  $contasUsadas = [];
  foreach ($variaveis as $v) {
    foreach ($v['secoes'] as $s) {
      preg_match_all('/\d+/', $s['formula'], $m);
      foreach ($m[0] as $cid) {
        $contasUsadas[(int)$cid] = true;
      }
    }
  }

  // ---------- Buscar valores para cada cenário (agregado + por mês) ----------
  if ($modo === 'orcreal') {
    $valoresA = buscarValoresMV($pdo, $cdempresa, 'v_orcado_conta_mes', $anoA, $mesesA, $contasUsadas);
    $valoresB = buscarValoresMV($pdo, $cdempresa, 'v_realizado_conta_mes', $anoA, $mesesA, $contasUsadas);
    $valoresAPorMes = buscarValoresMVPorMes($pdo, $cdempresa, 'v_orcado_conta_mes', $anoA, $mesesA, $contasUsadas);
    $valoresBPorMes = buscarValoresMVPorMes($pdo, $cdempresa, 'v_realizado_conta_mes', $anoA, $mesesA, $contasUsadas);
  } else {
    $valoresA = buscarValoresMV($pdo, $cdempresa, 'v_realizado_conta_mes', $anoA, $mesesA, $contasUsadas);
    $valoresB = buscarValoresMV($pdo, $cdempresa, 'v_realizado_conta_mes', $anoB, $mesesB, $contasUsadas);
    $valoresAPorMes = buscarValoresMVPorMes($pdo, $cdempresa, 'v_realizado_conta_mes', $anoA, $mesesA, $contasUsadas);
    $valoresBPorMes = buscarValoresMVPorMes($pdo, $cdempresa, 'v_realizado_conta_mes', $anoB, $mesesB, $contasUsadas);
  }

  // ---------- Calcular fórmulas ----------
  $calcVar = function(float $va, float $vb): array {
    // Usar valores absolutos para que a variação reflita o que o usuário vê (display é sempre positivo)
    $absA = abs($va);
    $absB = abs($vb);
    $varAbs = $absB - $absA;
    $varPct = ($absA != 0) ? (($absB - $absA) / $absA) * 100 : ($absB != 0 ? 100 : 0);
    return [round($varAbs, 4), round($varPct, 2)];
  };

  // Helper: calcular fórmula por mês individual
  $calcPorMes = function(string $formula, array $valsPorMes, array $meses) {
    $obj = new \stdClass();
    foreach ($meses as $m) {
      // Montar mapa {cdcontacontabil => valor} para este mês
      $mapMes = [];
      foreach ($valsPorMes as $cdconta => $mesesMap) {
        $mapMes[$cdconta] = (float)($mesesMap[$m] ?? 0);
      }
      $obj->{$m} = round(calcularFormulaDashboardInterno($formula, $mapMes), 4);
    }
    return $obj;
  };

  $resultado = [];
  foreach ($variaveis as $v) {
    $out = [
      'cdvariavel' => $v['cdvariavel'],
      'nome'       => $v['nome'],
      'secoes'     => [],
      'total_a'    => 0,
      'total_b'    => 0,
    ];

    $firstSecA = null;
    $firstSecB = null;
    $firstMascara = null;
    foreach ($v['secoes'] as $s) {
      $va = calcularFormulaDashboardInterno($s['formula'], $valoresA);
      $vb = calcularFormulaDashboardInterno($s['formula'], $valoresB);

      // Percentual: multiplicar por 100
      $isPct = ($s['mascara'] === 'percentual');
      if ($isPct) { $va *= 100; $vb *= 100; }

      // Quando cálculo vazio (ambos zero), pular
      if ($va == 0 && $vb == 0) continue;

      // Valores por mês (resultado da fórmula)
      $mesesAObj = $calcPorMes($s['formula'], $valoresAPorMes, $mesesA);
      $mesesBObj = $calcPorMes($s['formula'], $valoresBPorMes, $mesesB);
      if ($isPct) {
        foreach ($mesesA as $m) $mesesAObj->{$m} *= 100;
        foreach ($mesesB as $m) $mesesBObj->{$m} *= 100;
      }

      // Contas brutas por mês (para reagrupar no frontend respeitando ordem matemática)
      preg_match_all('/\d+/', $s['formula'], $fmContas);
      $contasFormula = array_unique(array_map('intval', $fmContas[0]));
      $contasMesesA = new \stdClass();
      $contasMesesB = new \stdClass();
      foreach ($contasFormula as $cid) {
        $contasMesesA->{$cid} = new \stdClass();
        $contasMesesB->{$cid} = new \stdClass();
        foreach ($mesesA as $m) {
          $contasMesesA->{$cid}->{$m} = (float)($valoresAPorMes[$cid][$m] ?? 0);
        }
        foreach ($mesesB as $m) {
          $contasMesesB->{$cid}->{$m} = (float)($valoresBPorMes[$cid][$m] ?? 0);
        }
      }

      [$varAbs, $varPct] = $calcVar($va, $vb);

      $out['secoes'][] = [
        'cdsecao'       => $s['cdsecao'],
        'titulo'        => $s['titulo'],
        'mascara'       => $s['mascara'],
        'formula'       => $s['formula'],
        'valor_a'       => round($va, 4),
        'valor_b'       => round($vb, 4),
        'meses_a'       => $mesesAObj,
        'meses_b'       => $mesesBObj,
        'contas_meses_a' => $contasMesesA,
        'contas_meses_b' => $contasMesesB,
        'variacao_abs'  => $varAbs,
        'variacao_pct' => $varPct,
      ];

      if ($firstSecA === null) {
        $firstSecA = $va; $firstSecB = $vb;
        $firstMascara = $s['mascara'];
      }
    }

    // Pular variável se todas as seções foram vazias
    if (empty($out['secoes'])) continue;

    $out['total_a'] = round($firstSecA ?? 0, 4);
    $out['total_b'] = round($firstSecB ?? 0, 4);
    $out['mascara'] = $firstMascara;
    [$vAbs, $vPct] = $calcVar($out['total_a'], $out['total_b']);
    $out['variacao_abs'] = $vAbs;
    $out['variacao_pct'] = $vPct;

    $resultado[] = $out;
  }

  json_out_var([
    'ok'        => true,
    'variaveis' => $resultado,
    'modo'      => $modo,
    'mesesA'    => array_values($mesesA),
    'mesesB'    => array_values($mesesB),
  ]);
}

/**
 * Buscar valores agregados por cdcontacontabil de uma MV (v_realizado_conta_mes ou v_orcado_conta_mes).
 * @return array<int, float> cdcontacontabil => soma
 */
function buscarValoresMV(PDO $pdo, int $cdempresa, string $view, int $ano, array $meses, array $contasUsadas): array {
  if (empty($meses) || empty($contasUsadas)) return [];

  $phM = implode(',', array_fill(0, count($meses), '?'));
  $contaIds = array_keys($contasUsadas);
  $phC = implode(',', array_fill(0, count($contaIds), '?'));

  // Whitelist view name for safety
  $allowedViews = ['v_realizado_conta_mes', 'v_orcado_conta_mes'];
  if (!in_array($view, $allowedViews, true)) {
    throw new InvalidArgumentException("View não permitida: $view");
  }

  $sql = "
    SELECT v.cdcontacontabil, COALESCE(SUM(v.valor_mes), 0) AS valor
      FROM back.{$view} v
     WHERE v.cdempresa = ?
       AND v.anoref = ?
       AND v.mesref IN ($phM)
       AND v.cdcontacontabil IN ($phC)
     GROUP BY v.cdcontacontabil
  ";

  $params = array_merge([$cdempresa, $ano], $meses, $contaIds);
  $st = $pdo->prepare($sql);
  $st->execute($params);

  $map = [];
  while ($r = $st->fetch(PDO::FETCH_ASSOC)) {
    $map[(int)$r['cdcontacontabil']] = (float)$r['valor'];
  }
  return $map;
}

/**
 * Buscar valores por mês individual por cdcontacontabil.
 * @return array<int, array<int, float>> cdcontacontabil => {mesref => valor}
 */
function buscarValoresMVPorMes(PDO $pdo, int $cdempresa, string $view, int $ano, array $meses, array $contasUsadas): array {
  if (empty($meses) || empty($contasUsadas)) return [];

  $phM = implode(',', array_fill(0, count($meses), '?'));
  $contaIds = array_keys($contasUsadas);
  $phC = implode(',', array_fill(0, count($contaIds), '?'));

  $allowedViews = ['v_realizado_conta_mes', 'v_orcado_conta_mes'];
  if (!in_array($view, $allowedViews, true)) {
    throw new InvalidArgumentException("View não permitida: $view");
  }

  $sql = "
    SELECT v.cdcontacontabil, v.mesref, COALESCE(SUM(v.valor_mes), 0) AS valor
      FROM back.{$view} v
     WHERE v.cdempresa = ?
       AND v.anoref = ?
       AND v.mesref IN ($phM)
       AND v.cdcontacontabil IN ($phC)
     GROUP BY v.cdcontacontabil, v.mesref
  ";

  $params = array_merge([$cdempresa, $ano], $meses, $contaIds);
  $st = $pdo->prepare($sql);
  $st->execute($params);

  $map = [];
  while ($r = $st->fetch(PDO::FETCH_ASSOC)) {
    $cid = (int)$r['cdcontacontabil'];
    $mes = (int)$r['mesref'];
    if (!isset($map[$cid])) $map[$cid] = [];
    $map[$cid][$mes] = (float)$r['valor'];
  }
  return $map;
}
