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

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

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

function json_out_comp($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_comp(['ok'=>false,'msg'=>"Erro interno ($loc): $msg"], 500);
});

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

switch ($ajax) {
  case 'periodos_disponiveis':
    periodosDisponiveis();
    break;

  case 'comparar_periodos':
    compararPeriodos();
    break;

  case 'export_comparativo_csv':
    exportComparativoCSV();
    break;

  case 'detalhe_lancamentos':
    detalheLancamentos();
    break;

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

/* ============================================================
 * PERÍODOS DISPONÍVEIS (anos e meses com dados na MV)
 * ============================================================ */
function periodosDisponiveis() {
  global $pdo, $cdempresa;

  $st = $pdo->prepare("
    SELECT DISTINCT anoref, mesref
      FROM back.v_realizado_conta_mes
     WHERE cdempresa = :emp
     ORDER BY anoref DESC, mesref ASC
  ");
  $st->execute([':emp' => $cdempresa]);
  $rows = $st->fetchAll(PDO::FETCH_ASSOC);

  $anos = [];
  foreach ($rows as $r) {
    $ano = (int)$r['anoref'];
    $mes = (int)$r['mesref'];
    if (!isset($anos[$ano])) $anos[$ano] = [];
    $anos[$ano][] = $mes;
  }

  json_out_comp(['ok' => true, 'periodos' => $anos]);
}

/* ============================================================
 * COMPARAR PERÍODOS (A vs B) usando MV v_realizado_conta_mes
 * ============================================================ */
function compararPeriodos() {
  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'] ?? [])));

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

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

  // Construir placeholders para meses
  $phA = implode(',', array_fill(0, count($mesesA), '?'));
  $phB = implode(',', array_fill(0, count($mesesB), '?'));

  // Filtros opcionais
  $cdcentrocusto = !empty($_GET['cdcentrocusto']) ? (int)$_GET['cdcentrocusto'] : null;
  $tpmovimento = (!empty($_GET['tpmovimento']) && in_array($_GET['tpmovimento'], ['C','D'])) ? $_GET['tpmovimento'] : null;

  // Se filtro de CC ou tpmovimento, precisamos query direta (MV não tem essas colunas)
  $usarQueryDireta = ($cdcentrocusto || $tpmovimento);

  if ($usarQueryDireta) {
    $sql = buildQueryDireta($phA, $phB, $cdcentrocusto, $tpmovimento);
  } else {
    // Query na MV (mais performática) — retorna por mês para detalhamento
    $sql = "
      SELECT 'A' AS periodo, v.cdcontacontabil, v.mesref, COALESCE(SUM(v.valor_mes), 0) AS valor
        FROM back.v_realizado_conta_mes v
       WHERE v.cdempresa = ?
         AND v.anoref = ?
         AND v.mesref IN ($phA)
       GROUP BY v.cdcontacontabil, v.mesref

      UNION ALL

      SELECT 'B' AS periodo, v.cdcontacontabil, v.mesref, COALESCE(SUM(v.valor_mes), 0) AS valor
        FROM back.v_realizado_conta_mes v
       WHERE v.cdempresa = ?
         AND v.anoref = ?
         AND v.mesref IN ($phB)
       GROUP BY v.cdcontacontabil, v.mesref
    ";
  }

  // Montar parâmetros
  if ($usarQueryDireta) {
    $params = buildParamsDireta($cdempresa, $cdcentrocusto, $tpmovimento, $anoA, $mesesA, $anoB, $mesesB);
  } else {
    $params = array_merge(
      [$cdempresa, $anoA], $mesesA,
      [$cdempresa, $anoB], $mesesB
    );
  }

  $st = $pdo->prepare($sql);
  $st->execute($params);

  // Agrupar valores por conta, período E mês
  $mapA = []; // mapA[cdcontacontabil][mesref] = valor
  $mapB = [];
  while ($row = $st->fetch(PDO::FETCH_ASSOC)) {
    $cd   = (int)$row['cdcontacontabil'];
    $mes  = (int)$row['mesref'];
    $val  = (float)$row['valor'];
    if ($row['periodo'] === 'A') {
      if (!isset($mapA[$cd])) $mapA[$cd] = [];
      $mapA[$cd][$mes] = ($mapA[$cd][$mes] ?? 0) + $val;
    } else {
      if (!isset($mapB[$cd])) $mapB[$cd] = [];
      $mapB[$cd][$mes] = ($mapB[$cd][$mes] ?? 0) + $val;
    }
  }

  // Carregar plano de contas ativo
  $stPlano = $pdo->query("
    SELECT cdcontacontabil, cdconta, cdcontapai, dsconta, fltipo, flnatureza
      FROM back.planocontabil
     WHERE flativo = 0
     ORDER BY cdconta ASC
  ");
  $contas = $stPlano->fetchAll(PDO::FETCH_ASSOC);

  // Helper: soma de todos os meses de um map
  $somaMap = function(array $mesesMap): float {
    return array_sum($mesesMap);
  };

  // Calcular totais apenas das contas raiz (sem cdcontapai) para percentuais
  $totalA = 0;
  $totalB = 0;
  foreach ($contas as $c) {
    if (empty($c['cdcontapai'])) {
      $cd = (int)$c['cdcontacontabil'];
      $totalA += abs($somaMap($mapA[$cd] ?? []));
      $totalB += abs($somaMap($mapB[$cd] ?? []));
    }
  }

  // Helper: calcular variação
  $calcVar = function(float $va, float $vb): array {
    $varAbs = $vb - $va;
    $varPct = ($va != 0) ? (($vb - $va) / abs($va)) * 100 : ($vb != 0 ? 100 : 0);
    return [round($varAbs, 2), round($varPct, 2)];
  };

  // Montar dados para cada conta com detalhamento por mês
  $dados = [];
  foreach ($contas as $c) {
    $cd = (int)$c['cdcontacontabil'];
    $mA = $mapA[$cd] ?? [];
    $mB = $mapB[$cd] ?? [];

    $va = $somaMap($mA);
    $vb = $somaMap($mB);

    // Só incluir contas que têm valor em ao menos um período
    if ($va == 0 && $vb == 0) continue;

    [$varAbs, $varPct] = $calcVar($va, $vb);
    $pctA = ($totalA != 0) ? (abs($va) / $totalA) * 100 : 0;
    $pctB = ($totalB != 0) ? (abs($vb) / $totalB) * 100 : 0;

    // Converter maps de meses para objetos com chave string (para JSON)
    $mesesAOut = new \stdClass();
    $mesesBOut = new \stdClass();
    foreach ($mA as $m => $v) $mesesAOut->{$m} = round($v, 2);
    foreach ($mB as $m => $v) $mesesBOut->{$m} = round($v, 2);

    $dados[] = [
      'cdcontacontabil' => $cd,
      'cdconta'         => $c['cdconta'],
      'cdcontapai'      => $c['cdcontapai'] ?: null,
      'dsconta'         => $c['dsconta'],
      'fltipo'          => $c['fltipo'],
      'flnatureza'      => $c['flnatureza'] ?? null,
      'meses_a'         => $mesesAOut,
      'meses_b'         => $mesesBOut,
      'valor_a'         => round($va, 2),
      'valor_b'         => round($vb, 2),
      'variacao_abs'    => round($varAbs, 2),
      'variacao_pct'    => round($varPct, 2),
      'pct_a'           => round($pctA, 2),
      'pct_b'           => round($pctB, 2),
    ];
  }

  // Totalização geral (soma de contas raiz com sinal) + por mês
  $totalGeralA = 0;
  $totalGeralB = 0;
  $recTotalA = 0;
  $recTotalB = 0;
  $recMesesA = [];
  $recMesesB = [];
  $totaisMesesA = [];
  $totaisMesesB = [];
  foreach ($contas as $c) {
    if (empty($c['cdcontapai'])) {
      $cd = (int)$c['cdcontacontabil'];
      $mA = $mapA[$cd] ?? [];
      $mB = $mapB[$cd] ?? [];
      $totalGeralA += $somaMap($mA);
      $totalGeralB += $somaMap($mB);
      foreach ($mA as $m => $v) $totaisMesesA[$m] = ($totaisMesesA[$m] ?? 0) + $v;
      foreach ($mB as $m => $v) $totaisMesesB[$m] = ($totaisMesesB[$m] ?? 0) + $v;
      if (($c['flnatureza'] ?? '') === 'C') {
        $recTotalA += $somaMap($mA);
        $recTotalB += $somaMap($mB);
        foreach ($mA as $m => $v) $recMesesA[$m] = ($recMesesA[$m] ?? 0) + $v;
        foreach ($mB as $m => $v) $recMesesB[$m] = ($recMesesB[$m] ?? 0) + $v;
      }
    }
  }

  [$totalVarAbs, $totalVarPct] = $calcVar($totalGeralA, $totalGeralB);

  // Converter totais por mês
  $totMesesAOut = new \stdClass();
  $totMesesBOut = new \stdClass();
  $recMesesAOut = new \stdClass();
  $recMesesBOut = new \stdClass();
  foreach ($totaisMesesA as $m => $v) $totMesesAOut->{$m} = round($v, 2);
  foreach ($totaisMesesB as $m => $v) $totMesesBOut->{$m} = round($v, 2);
  foreach ($recMesesA as $m => $v) $recMesesAOut->{$m} = round($v, 2);
  foreach ($recMesesB as $m => $v) $recMesesBOut->{$m} = round($v, 2);

  json_out_comp([
    'ok'     => true,
    'dados'  => $dados,
    'totais' => [
      'meses_a'      => $totMesesAOut,
      'meses_b'      => $totMesesBOut,
      'rec_meses_a'  => $recMesesAOut,
      'rec_meses_b'  => $recMesesBOut,
      'total_a'      => round($totalGeralA, 2),
      'total_b'      => round($totalGeralB, 2),
      'rec_a'        => round($recTotalA, 2),
      'rec_b'        => round($recTotalB, 2),
      'variacao_abs' => round($totalVarAbs, 2),
      'variacao_pct' => round($totalVarPct, 2),
    ],
  ]);
}

/**
 * Query direta com filtros não disponíveis na MV (CC e/ou tipo movimento).
 * Usa recursive CTE para hierarquia de contas, igual v_realizado_conta_mes.
 */
function buildQueryDireta(string $phA, string $phB, ?int $cdcentrocusto, ?string $tpmovimento): string {
  $extraWhere = '';
  if ($cdcentrocusto) $extraWhere .= ' AND r.cdcentrocusto = ?';
  if ($tpmovimento)   $extraWhere .= ' AND r.tpmovimento = ?';

  // Manter convenção de sinal C/D: créditos positivos, débitos negativos
  // Mesmo filtrando por tipo, preserva o sinal para cores/variação corretas
  if ($tpmovimento === 'D') {
    $valorExpr = '-SUM(r.valor)';
  } elseif ($tpmovimento === 'C') {
    $valorExpr = 'SUM(r.valor)';
  } else {
    $valorExpr = "SUM(CASE WHEN r.tpmovimento = 'C' THEN r.valor ELSE -r.valor END)";
  }

  return "
    WITH RECURSIVE hier AS (
      SELECT pc.cdcontacontabil AS leaf,
             pc.cdcontacontabil AS node,
             pc.cdcontapai
        FROM back.planocontabil pc
      UNION ALL
      SELECT h.leaf,
             p.cdcontacontabil AS node,
             p.cdcontapai
        FROM hier h
        JOIN back.planocontabil p ON p.cdconta = h.cdcontapai
    ),
    real_a AS (
      SELECT h.node AS cdcontacontabil,
             r.mesref,
             {$valorExpr} AS valor
        FROM back.realizado r
        JOIN back.planocontabil pc ON pc.cdcontacontabil = r.cdplanocontabil
        JOIN hier h ON h.leaf = pc.cdcontacontabil
       WHERE r.cdempresa = ?
         AND r.flcancelado = 0
         {$extraWhere}
         AND r.anoref = ?
         AND r.mesref IN ({$phA})
       GROUP BY h.node, r.mesref
    ),
    real_b AS (
      SELECT h.node AS cdcontacontabil,
             r.mesref,
             {$valorExpr} AS valor
        FROM back.realizado r
        JOIN back.planocontabil pc ON pc.cdcontacontabil = r.cdplanocontabil
        JOIN hier h ON h.leaf = pc.cdcontacontabil
       WHERE r.cdempresa = ?
         AND r.flcancelado = 0
         {$extraWhere}
         AND r.anoref = ?
         AND r.mesref IN ({$phB})
       GROUP BY h.node, r.mesref
    )
    SELECT 'A' AS periodo, cdcontacontabil, mesref, valor FROM real_a
    UNION ALL
    SELECT 'B' AS periodo, cdcontacontabil, mesref, valor FROM real_b
  ";
}

/**
 * Monta array de parâmetros para a query direta (CC/tpmovimento).
 */
function buildParamsDireta(int $cdempresa, ?int $cdcentrocusto, ?string $tpmovimento, int $anoA, array $mesesA, int $anoB, array $mesesB): array {
  // Cada bloco (real_a e real_b): empresa [+ cc] [+ tp] + ano + meses
  $bloco = function($ano, $meses) use ($cdempresa, $cdcentrocusto, $tpmovimento) {
    $p = [$cdempresa];
    if ($cdcentrocusto) $p[] = $cdcentrocusto;
    if ($tpmovimento)   $p[] = $tpmovimento;
    $p[] = $ano;
    $p = array_merge($p, $meses);
    return $p;
  };
  return array_merge($bloco($anoA, $mesesA), $bloco($anoB, $mesesB));
}

/* ============================================================
 * EXPORT COMPARATIVO CSV
 * ============================================================ */
function exportComparativoCSV() {
  global $pdo, $cdempresa;

  // Reutiliza a mesma lógica do comparar, mas gera CSV
  ob_start();
  compararPeriodos();
  $json = ob_get_clean();

  $data = json_decode($json, true);
  if (!($data['ok'] ?? false)) {
    header('Content-Type: application/json; charset=utf-8');
    echo $json;
    exit;
  }

  $labelA = $_GET['label_a'] ?? 'Período A';
  $labelB = $_GET['label_b'] ?? 'Período B';

  // Apelido da empresa
  $st = $pdo->prepare("SELECT dsapelido FROM back.empresa WHERE cdempresa = :emp LIMIT 1");
  $st->execute([':emp' => $cdempresa]);
  $apelido = (string)($st->fetchColumn() ?: '');
  $safe = $apelido ? preg_replace('/[^A-Za-z0-9_-]/', '_', $apelido) : '';

  header_remove('Content-Type');
  header('Content-Type: text/csv; charset=utf-8');
  $fname = 'comparativo_realizado';
  if ($safe !== '') $fname .= '_' . $safe;
  $fname .= '.csv';
  header('Content-Disposition: attachment; filename="' . $fname . '"');

  $sep = ';';
  $headers = ['CdConta', 'Conta', 'Tipo', $labelA, '% ' . $labelA, $labelB, '% ' . $labelB, 'Variação (R$)', 'Variação (%)'];

  echo implode($sep, array_map(function($h) {
    return '"' . str_replace('"', '""', $h) . '"';
  }, $headers)) . "\n";

  foreach ($data['dados'] as $d) {
    $line = [
      $d['cdconta'],
      $d['dsconta'],
      $d['fltipo'],
      number_format($d['valor_a'], 2, ',', '.'),
      number_format($d['pct_a'], 2, ',', '.') . '%',
      number_format($d['valor_b'], 2, ',', '.'),
      number_format($d['pct_b'], 2, ',', '.') . '%',
      number_format($d['variacao_abs'], 2, ',', '.'),
      number_format($d['variacao_pct'], 2, ',', '.') . '%',
    ];
    echo implode($sep, array_map(function($v) {
      return '"' . str_replace('"', '""', (string)$v) . '"';
    }, $line)) . "\n";
  }

  // Linha de total
  $t = $data['totais'];
  $totalLine = [
    '', 'TOTAL GERAL', '',
    number_format($t['total_a'], 2, ',', '.'), '',
    number_format($t['total_b'], 2, ',', '.'), '',
    number_format($t['variacao_abs'], 2, ',', '.'),
    number_format($t['variacao_pct'], 2, ',', '.') . '%',
  ];
  echo implode($sep, array_map(function($v) {
    return '"' . str_replace('"', '""', (string)$v) . '"';
  }, $totalLine)) . "\n";

  exit;
}

/* ============================================================
 * DETALHE DE LANÇAMENTOS (drill-down por conta)
 * ============================================================ */
function detalheLancamentos() {
  global $pdo, $cdempresa;

  $cdconta         = trim($_GET['cdconta'] ?? '');
  $cdcontacontabil = !empty($_GET['cdcontacontabil']) ? (int)$_GET['cdcontacontabil'] : null;
  $ehSintetica     = empty($cdcontacontabil);

  if ($cdconta === '') {
    json_out_comp(['ok' => false, 'msg' => 'Conta não informada.'], 400);
  }

  $where  = ['r.cdempresa = :emp', 'r.flcancelado = 0'];
  $params = [':emp' => $cdempresa];

  // Filtro de conta: analítica (exact PK) ou sintética (prefixo)
  if (!$ehSintetica) {
    $where[] = 'r.cdplanocontabil = :cdpc';
    $params[':cdpc'] = $cdcontacontabil;
  } else {
    $where[] = '(pc.cdconta = :cdconta OR pc.cdconta LIKE :cdconta_pref)';
    $params[':cdconta']      = $cdconta;
    $params[':cdconta_pref'] = $cdconta . '.%';
  }

  // Filtro de período: datas livres ou ano+meses
  $dtinicio = $_GET['dtinicio'] ?? '';
  $dtfim    = $_GET['dtfim']    ?? '';
  if ($dtinicio !== '' && $dtfim !== '') {
    $where[] = 'r.dtmovimento >= :dtini';
    $where[] = 'r.dtmovimento <= :dtfim';
    $params[':dtini'] = $dtinicio;
    $params[':dtfim'] = $dtfim;
  } else {
    $ano   = (int)($_GET['ano'] ?? 0);
    $meses = array_filter(array_map('intval', (array)($_GET['meses'] ?? [])));
    if ($ano && !empty($meses)) {
      $where[] = 'r.anoref = :ano';
      $params[':ano'] = $ano;
      $phM = [];
      foreach ($meses as $i => $m) {
        $key = ':mes' . $i;
        $phM[] = $key;
        $params[$key] = $m;
      }
      $where[] = 'r.mesref IN (' . implode(',', $phM) . ')';
    }
  }

  // Filtros opcionais
  if (!empty($_GET['cdcentrocusto'])) {
    $where[] = 'r.cdcentrocusto = :cc';
    $params[':cc'] = (int)$_GET['cdcentrocusto'];
  }
  if (!empty($_GET['tpmovimento']) && in_array($_GET['tpmovimento'], ['C','D'])) {
    $where[] = 'r.tpmovimento = :tp';
    $params[':tp'] = $_GET['tpmovimento'];
  }

  $sql = "
    SELECT r.cdrealizado, r.cdplanocontabil, r.dtmovimento,
           r.dscomplemento, r.valor, r.tpmovimento,
           pc.cdconta, pc.dsconta
      FROM back.realizado r
      JOIN back.planocontabil pc ON pc.cdcontacontabil = r.cdplanocontabil
     WHERE " . implode(' AND ', $where) . "
     ORDER BY pc.cdconta, r.dtmovimento, r.cdrealizado
     LIMIT 5001
  ";

  $st = $pdo->prepare($sql);
  $st->execute($params);
  $rows = $st->fetchAll(PDO::FETCH_ASSOC);

  $truncado = false;
  if (count($rows) > 5000) {
    $rows = array_slice($rows, 0, 5000);
    $truncado = true;
  }

  json_out_comp(['ok' => true, 'lancamentos' => $rows, 'truncado' => $truncado]);
}
