/**
 * BI Executive Dashboard Queries
 * Comprehensive analytics: KPIs, revenue P&L, strategic cohort analysis
 */
const { pool, fmtDate, fmtTrendRows, calcPrevPeriod } = require('./helpers');

// BI Analytics - Comprehensive data for admin BI dashboard
async function fetchBIData(dataInicio, dataFim, getAgenteName = null) {
  const { prevStartStr, prevEndStr } = calcPrevPeriod(dataInicio, dataFim);

  // Run all queries in parallel using pool (each gets its own connection)
  const [
    [kpiBrlUsd], [kpiUsdBrl], [kpiUsdUsd], [uniqueClients],
    [prevBrlUsd], [prevUsdBrl], [prevUsdUsd],
    [trendBrlUsd], [trendUsdBrl], [trendUsdUsd],
    [topClients], [retention], [clientsAtRisk], [agentRanking]
  ] = await Promise.all([
    // 1. BRL→USD KPIs (real revenue = P&L formula)
    pool.execute(`
        SELECT COUNT(*) as qtd,
          ROUND(COALESCE(SUM(t.amount_usd), 0), 2) as vol_usd,
          ROUND(COALESCE(SUM(t.amount_brl), 0), 2) as vol_brl,
          ROUND(COALESCE(SUM(
            (ROUND((t.amount_brl - IF(pm.provider IN ('ouribank','bs2'), 0, t.fee)) / t.ptax, 2) - COALESCE(t.pfee, 0))
            - (t.amount_usd + COALESCE(t.bonus_valor, 0) - COALESCE(t.taxa_cr, 0))
          ), 0), 2) as spread_revenue,
          ROUND(COALESCE(AVG((t.exchange_rate - t.ptax) / t.exchange_rate * 100), 0), 2) as avg_spread_pct,
          COUNT(DISTINCT t.id_conta) as clientes
        FROM br_transaction_to_usa t
        JOIN br_payment_methods pm ON t.payment_method_id = pm.id
        WHERE DATE(t.created_at) >= ? AND DATE(t.created_at) <= ?
          AND pm.provider IN ('dlocal','bexs','braza','bs2','ouribank','msb')
          AND t.ptax IS NOT NULL AND t.ptax > 0
          AND (t.status IN ('boleto_pago','finalizado') OR t.date_sent_usa <> '0000-00-00 00:00:00')
      `, [dataInicio, dataFim]),
      // 2. USD→BRL KPIs (real revenue: spread + fee for non-balance, fee only for balance)
      pool.execute(`
        SELECT COUNT(*) as qtd,
          ROUND(COALESCE(SUM(pb.valor), 0), 2) as vol_usd,
          ROUND(COALESCE(SUM(pb.valor_sol), 0), 2) as vol_brl,
          ROUND(COALESCE(SUM(
            CASE WHEN pb.tipo_envio = 'balance' THEN COALESCE(pb.fee, 0)
            ELSE COALESCE(CASE WHEN pb.ptax IS NOT NULL AND pb.ptax > 0 THEN ((pb.ptax - pb.cotacao) * pb.valor) / pb.ptax ELSE 0 END, 0) + COALESCE(pb.fee, 0)
            END
          ), 0), 2) as spread_revenue,
          ROUND(COALESCE(AVG(CASE WHEN pb.cotacao > 0 AND pb.tipo_envio != 'balance' THEN (pb.ptax - pb.cotacao) / pb.ptax * 100 ELSE 0 END), 0), 2) as avg_spread_pct,
          COUNT(DISTINCT pb.id_conta) as clientes
        FROM pagamento_br pb
        WHERE pb.valor > 0 AND pb.data_cp IS NOT NULL AND pb.data_cp <> '0000-00-00'
          AND DATE(CASE WHEN pb.tipo_envio = 'balance' THEN pb.data_cp ELSE pb.created_at END) >= ?
          AND DATE(CASE WHEN pb.tipo_envio = 'balance' THEN pb.data_cp ELSE pb.created_at END) <= ?
      `, [dataInicio, dataFim]),
      // 3. USD→USD KPIs
      pool.execute(`
        SELECT COUNT(*) as qtd,
          ROUND(COALESCE(SUM(valor), 0), 2) as vol_usd,
          COUNT(DISTINCT id_conta) as clientes
        FROM pagamento_br
        WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
          AND (cotacao IS NULL OR cotacao = 0 OR pgto = 'balance')
      `, [dataInicio, dataFim]),
      // 4. Unique active clients
      pool.execute(`
        SELECT COUNT(DISTINCT id_conta) as total FROM (
          SELECT id_conta FROM br_transaction_to_usa WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
          UNION
          SELECT id_conta FROM pagamento_br WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
        ) all_clients
      `, [dataInicio, dataFim, dataInicio, dataFim]),
      // 5. Previous period (real revenue formulas)
      pool.execute(`
        SELECT COUNT(*) as qtd, ROUND(COALESCE(SUM(t.amount_usd),0),2) as vol_usd,
          ROUND(COALESCE(SUM(
            (ROUND((t.amount_brl - IF(pm.provider IN ('ouribank','bs2'), 0, t.fee)) / t.ptax, 2) - COALESCE(t.pfee, 0))
            - (t.amount_usd + COALESCE(t.bonus_valor, 0) - COALESCE(t.taxa_cr, 0))
          ),0),2) as spread_revenue
        FROM br_transaction_to_usa t
        JOIN br_payment_methods pm ON t.payment_method_id = pm.id
        WHERE DATE(t.created_at) >= ? AND DATE(t.created_at) <= ?
          AND pm.provider IN ('dlocal','bexs','braza','bs2','ouribank','msb')
          AND t.ptax IS NOT NULL AND t.ptax > 0
          AND (t.status IN ('boleto_pago','finalizado') OR t.date_sent_usa <> '0000-00-00 00:00:00')
      `, [prevStartStr, prevEndStr]),
      pool.execute(`
        SELECT COUNT(*) as qtd, ROUND(COALESCE(SUM(pb.valor),0),2) as vol_usd,
          ROUND(COALESCE(SUM(
            CASE WHEN pb.tipo_envio = 'balance' THEN COALESCE(pb.fee, 0)
            ELSE COALESCE(CASE WHEN pb.ptax IS NOT NULL AND pb.ptax > 0 THEN ((pb.ptax - pb.cotacao) * pb.valor) / pb.ptax ELSE 0 END, 0) + COALESCE(pb.fee, 0)
            END
          ),0),2) as spread_revenue
        FROM pagamento_br pb
        WHERE pb.valor > 0 AND pb.data_cp IS NOT NULL AND pb.data_cp <> '0000-00-00'
          AND DATE(CASE WHEN pb.tipo_envio = 'balance' THEN pb.data_cp ELSE pb.created_at END) >= ?
          AND DATE(CASE WHEN pb.tipo_envio = 'balance' THEN pb.data_cp ELSE pb.created_at END) <= ?
      `, [prevStartStr, prevEndStr]),
      pool.execute(`
        SELECT COUNT(*) as qtd, ROUND(COALESCE(SUM(valor),0),2) as vol_usd
        FROM pagamento_br WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
          AND (cotacao IS NULL OR cotacao = 0 OR pgto = 'balance')
      `, [prevStartStr, prevEndStr]),
      // 6-8. Trends
      pool.execute(`
        SELECT DATE(created_at) as dia, COUNT(*) as qtd,
          ROUND(SUM(amount_usd), 2) as vol_usd,
          ROUND(AVG((exchange_rate - ptax) / exchange_rate * 100), 2) as avg_spread
        FROM br_transaction_to_usa WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
        GROUP BY DATE(created_at) ORDER BY dia
      `, [dataInicio, dataFim]),
      pool.execute(`
        SELECT DATE(created_at) as dia, COUNT(*) as qtd,
          ROUND(SUM(valor), 2) as vol_usd,
          ROUND(AVG(CASE WHEN cotacao > 0 THEN (ptax - cotacao) / ptax * 100 ELSE 0 END), 2) as avg_spread
        FROM pagamento_br WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
          AND cotacao IS NOT NULL AND cotacao > 0 AND (pgto IS NULL OR pgto != 'balance')
        GROUP BY DATE(created_at) ORDER BY dia
      `, [dataInicio, dataFim]),
      pool.execute(`
        SELECT DATE(created_at) as dia, COUNT(*) as qtd, ROUND(SUM(valor), 2) as vol_usd
        FROM pagamento_br WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
          AND (cotacao IS NULL OR cotacao = 0 OR pgto = 'balance')
        GROUP BY DATE(created_at) ORDER BY dia
      `, [dataInicio, dataFim]),
      // 9. Top clients
      pool.execute(`
        SELECT nome, SUM(vol) as total_usd, SUM(qtd) as total_qtd FROM (
          SELECT c.nome, SUM(t.amount_usd) as vol, COUNT(*) as qtd
          FROM br_transaction_to_usa t INNER JOIN conta c ON c.id_conta = t.id_conta
          WHERE DATE(t.created_at) >= ? AND DATE(t.created_at) <= ? GROUP BY c.nome
          UNION ALL
          SELECT c.nome, SUM(p.valor) as vol, COUNT(*) as qtd
          FROM pagamento_br p INNER JOIN conta c ON c.id_conta = p.id_conta
          WHERE DATE(p.created_at) >= ? AND DATE(p.created_at) <= ? GROUP BY c.nome
        ) combined GROUP BY nome ORDER BY total_usd DESC LIMIT 10
      `, [dataInicio, dataFim, dataInicio, dataFim]),
      // 10. Retention
      pool.execute(`
        SELECT COUNT(DISTINCT prev.id_conta) as prev_clients,
          COUNT(DISTINCT CASE WHEN curr.id_conta IS NOT NULL THEN prev.id_conta END) as retained
        FROM (
          SELECT DISTINCT id_conta FROM br_transaction_to_usa WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
          UNION
          SELECT DISTINCT id_conta FROM pagamento_br WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
        ) prev LEFT JOIN (
          SELECT DISTINCT id_conta FROM br_transaction_to_usa WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
          UNION
          SELECT DISTINCT id_conta FROM pagamento_br WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
        ) curr ON prev.id_conta = curr.id_conta
      `, [prevStartStr, prevEndStr, prevStartStr, prevEndStr, dataInicio, dataFim, dataInicio, dataFim]),
      // 11. Clients at risk
      pool.execute(`
        SELECT nome, MAX(last_op) as last_op, SUM(vol) as total_usd, SUM(qtd) as total_qtd,
          DATEDIFF(CURDATE(), MAX(last_op)) as days_inactive
        FROM (
          SELECT c.nome, MAX(t.created_at) as last_op, SUM(t.amount_usd) as vol, COUNT(*) as qtd
          FROM br_transaction_to_usa t INNER JOIN conta c ON c.id_conta = t.id_conta GROUP BY c.nome
          UNION ALL
          SELECT c.nome, MAX(p.created_at) as last_op, SUM(p.valor) as vol, COUNT(*) as qtd
          FROM pagamento_br p INNER JOIN conta c ON c.id_conta = p.id_conta GROUP BY c.nome
        ) combined GROUP BY nome HAVING MAX(last_op) < CURDATE() ORDER BY total_usd DESC LIMIT 20
      `),
      // 12. Agent ranking (real revenue formulas)
      pool.execute(`
        SELECT agente_id, SUM(vol) as total_usd, SUM(qtd) as total_qtd,
          ROUND(SUM(spread_rev), 2) as total_spread, COUNT(DISTINCT client_id) as clientes
        FROM (
          SELECT ac.agente_id, t.id_conta as client_id, SUM(t.amount_usd) as vol, COUNT(*) as qtd,
            SUM(
              (ROUND((t.amount_brl - IF(pm.provider IN ('ouribank','bs2'), 0, t.fee)) / t.ptax, 2) - COALESCE(t.pfee, 0))
              - (t.amount_usd + COALESCE(t.bonus_valor, 0) - COALESCE(t.taxa_cr, 0))
            ) as spread_rev
          FROM br_transaction_to_usa t
          INNER JOIN ag_contas ac ON ac.conta_id = t.id_conta
          INNER JOIN br_payment_methods pm ON t.payment_method_id = pm.id
          WHERE DATE(t.created_at) >= ? AND DATE(t.created_at) <= ?
            AND pm.provider IN ('dlocal','bexs','braza','bs2','ouribank','msb')
            AND t.ptax IS NOT NULL AND t.ptax > 0
            AND (t.status IN ('boleto_pago','finalizado') OR t.date_sent_usa <> '0000-00-00 00:00:00')
          GROUP BY ac.agente_id, t.id_conta
          UNION ALL
          SELECT ac.agente_id, p.id_conta as client_id, SUM(p.valor) as vol, COUNT(*) as qtd,
            SUM(
              CASE WHEN p.tipo_envio = 'balance' THEN COALESCE(p.fee, 0)
              ELSE COALESCE(CASE WHEN p.ptax IS NOT NULL AND p.ptax > 0 THEN ((p.ptax - p.cotacao) * p.valor) / p.ptax ELSE 0 END, 0) + COALESCE(p.fee, 0)
              END
            ) as spread_rev
          FROM pagamento_br p INNER JOIN ag_contas ac ON ac.conta_id = p.id_conta
          WHERE DATE(CASE WHEN p.tipo_envio = 'balance' THEN p.data_cp ELSE p.created_at END) >= ?
            AND DATE(CASE WHEN p.tipo_envio = 'balance' THEN p.data_cp ELSE p.created_at END) <= ?
            AND p.valor > 0 AND p.data_cp IS NOT NULL AND p.data_cp <> '0000-00-00'
          GROUP BY ac.agente_id, p.id_conta
        ) combined GROUP BY agente_id ORDER BY total_usd DESC LIMIT 10
      `, [dataInicio, dataFim, dataInicio, dataFim])
    ]);

    // Resolve agent names
    const agents = agentRanking.map((r, i) => {
      const nome = getAgenteName ? (getAgenteName(r.agente_id) || `Agente ${r.agente_id}`) : `Agente ${r.agente_id}`;
      return { rank: i + 1, agente_id: r.agente_id, nome,
        vol_usd: Number(r.total_usd), qtd: Number(r.total_qtd),
        spread_revenue: Number(r.total_spread), clientes: Number(r.clientes)
      };
    });

    // Format results
    const fmtKpi = (r) => ({
      qtd: Number(r?.qtd) || 0, vol_usd: Number(r?.vol_usd) || 0,
      vol_brl: Number(r?.vol_brl) || 0, spread_revenue: Number(r?.spread_revenue) || 0,
      avg_spread_pct: Number(r?.avg_spread_pct) || 0, clientes: Number(r?.clientes) || 0
    });

    const brl = fmtKpi(kpiBrlUsd[0]);
    const usd = fmtKpi(kpiUsdBrl[0]);
    const uu = { qtd: Number(kpiUsdUsd[0]?.qtd) || 0, vol_usd: Number(kpiUsdUsd[0]?.vol_usd) || 0, clientes: Number(kpiUsdUsd[0]?.clientes) || 0 };
    const totalQtd = brl.qtd + usd.qtd + uu.qtd;
    const totalVolUsd = brl.vol_usd + usd.vol_usd + uu.vol_usd;

    const pBrl = Number(prevBrlUsd[0]?.qtd) || 0;
    const pUsd = Number(prevUsdBrl[0]?.qtd) || 0;
    const pUu = Number(prevUsdUsd[0]?.qtd) || 0;
    const prevQtd = pBrl + pUsd + pUu;
    const prevVolUsd = (Number(prevBrlUsd[0]?.vol_usd) || 0) + (Number(prevUsdBrl[0]?.vol_usd) || 0) + (Number(prevUsdUsd[0]?.vol_usd) || 0);
    const prevSpread = (Number(prevBrlUsd[0]?.spread_revenue) || 0) + (Number(prevUsdBrl[0]?.spread_revenue) || 0);

    const retPrev = Number(retention[0]?.prev_clients) || 0;
    const retCurr = Number(retention[0]?.retained) || 0;

    return {
      kpis: {
        brlUsd: brl, usdBrl: usd, usdUsd: uu,
        total: {
          qtd: totalQtd, vol_usd: totalVolUsd,
          spread_revenue: brl.spread_revenue + usd.spread_revenue,
          clientes: Number(uniqueClients[0]?.total) || 0,
          ticket_medio: totalQtd > 0 ? Math.round(totalVolUsd / totalQtd) : 0
        }
      },
      comparison: { prev_qtd: prevQtd, prev_vol_usd: prevVolUsd, prev_spread: prevSpread },
      trend: { brlUsd: fmtTrendRows(trendBrlUsd), usdBrl: fmtTrendRows(trendUsdBrl), usdUsd: fmtTrendRows(trendUsdUsd) },
      topClients: topClients.map(r => ({ nome: r.nome, vol_usd: Number(r.total_usd), qtd: Number(r.total_qtd) })),
      retention: { prev_clients: retPrev, retained: retCurr, rate: retPrev > 0 ? Math.round(retCurr / retPrev * 100) : 0 },
      clientsAtRisk: clientsAtRisk.map(r => ({
        nome: r.nome, vol_usd: Number(r.total_usd), qtd: Number(r.total_qtd),
        last_op: r.last_op instanceof Date ? r.last_op.toISOString().slice(0, 10) : String(r.last_op).slice(0, 16),
        days_inactive: Number(r.days_inactive) || 0
      })),
      agentRanking: agents,
      netting: {
        saida_usd: brl.vol_usd, entrada_usd: usd.vol_usd,
        posicao_liquida: usd.vol_usd - brl.vol_usd,
        eficiencia: brl.vol_usd > 0 ? Math.min(100, Math.round(usd.vol_usd / brl.vol_usd * 100)) : 0
      }
    };
}

// Revenue Analytics - Real P&L by product with dynamic granularity
async function fetchRevenueAnalytics(dataInicio, dataFim, granularity = 'dia') {
    const validGran = ['dia', 'mes', 'ano'].includes(granularity) ? granularity : 'dia';

    let periodoInicio, periodoLabel;
    switch (validGran) {
      case 'ano':
        periodoInicio = "MAKEDATE(YEAR(dia), 1)";
        periodoLabel = "DATE_FORMAT(dia, '%Y')";
        break;
      case 'mes':
        periodoInicio = "CAST(DATE_FORMAT(dia, '%Y-%m-01') AS DATE)";
        periodoLabel = "DATE_FORMAT(dia, '%Y-%m')";
        break;
      default:
        periodoInicio = "DATE(dia)";
        periodoLabel = "DATE_FORMAT(dia, '%Y-%m-%d')";
    }

    const [rows] = await pool.execute(`
      WITH limites AS (
        SELECT
          CAST(? AS DATE) AS inicio,
          DATE_ADD(CAST(? AS DATE), INTERVAL 1 DAY) AS fim_exclusivo
      ),
      q1 AS (
        SELECT
          CASE
            WHEN pb.tipo_envio = 'balance' THEN DATE(pb.data_cp)
            ELSE DATE(pb.created_at)
          END AS dia,
          CONCAT('US→BR: ', COALESCE(pb.tipo_envio, 'desconhecido')) AS produto,
          CASE
            WHEN pb.tipo_envio = 'balance' THEN COALESCE(pb.fee, 0)
            ELSE COALESCE(
              CASE
                WHEN pb.ptax IS NOT NULL AND pb.ptax > 0
                  THEN ((pb.ptax - pb.cotacao) * pb.valor) / pb.ptax
                ELSE 0
              END, 0
            ) + COALESCE(pb.fee, 0)
          END AS receita
        FROM pagamento_br pb
        JOIN limites l ON (
          CASE WHEN pb.tipo_envio = 'balance' THEN pb.data_cp ELSE pb.created_at END
        ) >= l.inicio
        AND (
          CASE WHEN pb.tipo_envio = 'balance' THEN pb.data_cp ELSE pb.created_at END
        ) < l.fim_exclusivo
        WHERE pb.valor > 0
          AND pb.data_cp IS NOT NULL
          AND pb.data_cp <> '0000-00-00'
      ),
      q2 AS (
        SELECT
          DATE(t.created_at) AS dia,
          CASE
            WHEN t.cobranca_id IS NOT NULL THEN 'BR→US: Checkout'
            ELSE 'BR→US: CambioTransfer'
          END AS produto,
          (
            (
              ROUND((t.amount_brl - IF(pm.provider IN ('ouribank','bs2'), 0, t.fee)) / t.ptax, 2)
              - COALESCE(t.pfee, 0)
            ) - (
              t.amount_usd + COALESCE(t.bonus_valor, 0) - COALESCE(t.taxa_cr, 0)
            )
          ) AS receita
        FROM br_transaction_to_usa t
        JOIN br_payment_methods pm ON t.payment_method_id = pm.id
        JOIN limites l ON t.created_at >= l.inicio AND t.created_at < l.fim_exclusivo
        WHERE pm.provider IN ('dlocal','bexs','braza','bs2','ouribank','msb')
          AND t.ptax IS NOT NULL AND t.ptax > 0
          AND (
            t.status IN ('boleto_pago','finalizado')
            OR t.date_sent_usa <> '0000-00-00 00:00:00'
          )
      ),
      unioned AS (
        SELECT dia, produto, receita FROM q1
        UNION ALL
        SELECT dia, produto, receita FROM q2
      )
      SELECT
        ${periodoInicio} AS periodo_inicio,
        ${periodoLabel} AS periodo_label,
        produto,
        ROUND(SUM(receita), 2) AS receita,
        COUNT(*) AS qtd
      FROM unioned
      GROUP BY periodo_inicio, periodo_label, produto
      ORDER BY periodo_inicio, produto
    `, [dataInicio, dataFim]);

    // Also get totals by product
    const [totals] = await pool.execute(`
      WITH limites AS (
        SELECT
          CAST(? AS DATE) AS inicio,
          DATE_ADD(CAST(? AS DATE), INTERVAL 1 DAY) AS fim_exclusivo
      ),
      q1 AS (
        SELECT
          CASE
            WHEN pb.tipo_envio = 'balance' THEN COALESCE(pb.fee, 0)
            ELSE COALESCE(
              CASE WHEN pb.ptax IS NOT NULL AND pb.ptax > 0
                THEN ((pb.ptax - pb.cotacao) * pb.valor) / pb.ptax ELSE 0
              END, 0
            ) + COALESCE(pb.fee, 0)
          END AS receita,
          'US→BR' AS direcao,
          COALESCE(pb.tipo_envio, 'desconhecido') AS tipo
        FROM pagamento_br pb
        JOIN limites l ON (
          CASE WHEN pb.tipo_envio = 'balance' THEN pb.data_cp ELSE pb.created_at END
        ) >= l.inicio
        AND (
          CASE WHEN pb.tipo_envio = 'balance' THEN pb.data_cp ELSE pb.created_at END
        ) < l.fim_exclusivo
        WHERE pb.valor > 0 AND pb.data_cp IS NOT NULL AND pb.data_cp <> '0000-00-00'
      ),
      q2 AS (
        SELECT
          (
            (ROUND((t.amount_brl - IF(pm.provider IN ('ouribank','bs2'), 0, t.fee)) / t.ptax, 2) - COALESCE(t.pfee, 0))
            - (t.amount_usd + COALESCE(t.bonus_valor, 0) - COALESCE(t.taxa_cr, 0))
          ) AS receita,
          'BR→US' AS direcao,
          CASE WHEN t.cobranca_id IS NOT NULL THEN 'Checkout' ELSE 'CambioTransfer' END AS tipo
        FROM br_transaction_to_usa t
        JOIN br_payment_methods pm ON t.payment_method_id = pm.id
        JOIN limites l ON t.created_at >= l.inicio AND t.created_at < l.fim_exclusivo
        WHERE pm.provider IN ('dlocal','bexs','braza','bs2','ouribank','msb')
          AND t.ptax IS NOT NULL AND t.ptax > 0
          AND (t.status IN ('boleto_pago','finalizado') OR t.date_sent_usa <> '0000-00-00 00:00:00')
      )
      SELECT
        direcao,
        tipo,
        ROUND(SUM(receita), 2) AS total_receita,
        COUNT(*) AS total_qtd
      FROM (SELECT receita, direcao, tipo FROM q1 UNION ALL SELECT receita, direcao, tipo FROM q2) all_data
      GROUP BY direcao, tipo
      ORDER BY direcao, tipo
    `, [dataInicio, dataFim]);

    const timeline = rows.map(r => ({
      periodo_inicio: r.periodo_inicio instanceof Date ? r.periodo_inicio.toISOString().slice(0, 10) : String(r.periodo_inicio).slice(0, 10),
      periodo_label: r.periodo_label,
      produto: r.produto,
      receita: Number(r.receita),
      qtd: Number(r.qtd)
    }));

    const totalsByProduct = totals.map(r => ({
      direcao: r.direcao,
      tipo: r.tipo,
      produto: r.direcao + ': ' + r.tipo,
      receita: Number(r.total_receita),
      qtd: Number(r.total_qtd)
    }));

    const grandTotal = totalsByProduct.reduce((s, r) => s + r.receita, 0);
    const grandQtd = totalsByProduct.reduce((s, r) => s + r.qtd, 0);
    const receitaBrUs = totalsByProduct.filter(r => r.direcao === 'BR→US').reduce((s, r) => s + r.receita, 0);
    const receitaUsBr = totalsByProduct.filter(r => r.direcao === 'US→BR').reduce((s, r) => s + r.receita, 0);

    return {
      timeline,
      totals: totalsByProduct,
      summary: {
        total_receita: Math.round(grandTotal * 100) / 100,
        total_qtd: grandQtd,
        receita_br_us: Math.round(receitaBrUs * 100) / 100,
        receita_us_br: Math.round(receitaUsBr * 100) / 100,
        ticket_medio_receita: grandQtd > 0 ? Math.round(grandTotal / grandQtd * 100) / 100 : 0
      },
      granularity: validGran
    };
}

async function fetchBIStrategic(dataInicio, dataFim) {
  const { prevStartStr, prevEndStr } = calcPrevPeriod(dataInicio, dataFim);

  // Run all 4 sections in parallel — each uses pool (own connections)
  const [cohorts, expansion, crossSell, maturity] = await Promise.all([
    _fetchCohorts(),
    _fetchExpansion(dataInicio, dataFim, prevStartStr, prevEndStr),
    _fetchCrossSell(dataInicio, dataFim),
    _fetchMaturity(dataInicio, dataFim, prevStartStr, prevEndStr)
  ]);

  return { cohorts, expansion, crossSell, maturity };
}

async function _fetchCohorts() {
  const [[cohortClients], [activeMonths]] = await Promise.all([
    pool.execute(`
      SELECT id_conta, DATE_FORMAT(MIN(first_op), '%Y-%m') as cohort_month FROM (
        SELECT id_conta, MIN(created_at) as first_op FROM br_transaction_to_usa GROUP BY id_conta
        UNION ALL
        SELECT id_conta, MIN(created_at) as first_op FROM pagamento_br
          WHERE cotacao IS NOT NULL AND cotacao > 0 AND (pgto IS NULL OR pgto != 'balance')
        GROUP BY id_conta
      ) f GROUP BY id_conta
    `),
    pool.execute(`
      SELECT id_conta, active_month FROM (
        SELECT id_conta, DATE_FORMAT(created_at, '%Y-%m') as active_month
        FROM br_transaction_to_usa GROUP BY id_conta, DATE_FORMAT(created_at, '%Y-%m')
        UNION
        SELECT id_conta, DATE_FORMAT(created_at, '%Y-%m') as active_month
        FROM pagamento_br WHERE cotacao IS NOT NULL AND cotacao > 0 AND (pgto IS NULL OR pgto != 'balance')
        GROUP BY id_conta, DATE_FORMAT(created_at, '%Y-%m')
      ) m
    `)
  ]);

  const clientCohort = {};
  cohortClients.forEach(r => { clientCohort[r.id_conta] = r.cohort_month; });
  const clientMonths = {};
  activeMonths.forEach(r => {
    if (!clientMonths[r.id_conta]) clientMonths[r.id_conta] = new Set();
    clientMonths[r.id_conta].add(r.active_month);
  });
  const allMonths = [...new Set([...cohortClients.map(r => r.cohort_month), ...activeMonths.map(r => r.active_month)])].sort();

  const cohortMap = {};
  cohortClients.forEach(r => {
    const cm = r.cohort_month;
    if (!cohortMap[cm]) cohortMap[cm] = { size: 0, months: {} };
    cohortMap[cm].size++;
  });
  Object.keys(clientCohort).forEach(clientId => {
    const cm = clientCohort[clientId];
    const months = clientMonths[clientId] || new Set();
    const cmIdx = allMonths.indexOf(cm);
    months.forEach(am => {
      const amIdx = allMonths.indexOf(am);
      const offset = amIdx - cmIdx;
      if (offset >= 0) {
        if (!cohortMap[cm].months[offset]) cohortMap[cm].months[offset] = 0;
        cohortMap[cm].months[offset]++;
      }
    });
  });

  const cohortKeys = Object.keys(cohortMap).sort().slice(-12);
  return cohortKeys.map(cm => {
    const c = cohortMap[cm];
    const maxOff = allMonths.length - allMonths.indexOf(cm);
    const retention = [];
    for (let i = 0; i < Math.min(maxOff, 13); i++) {
      retention.push(c.size > 0 ? Math.round((c.months[i] || 0) / c.size * 100) : 0);
    }
    return { month: cm, size: c.size, retention };
  });
}

async function _fetchExpansion(dataInicio, dataFim, prevStartStr, prevEndStr) {
  const [[currRevenue], [prevRevenue]] = await Promise.all([
    pool.execute(`
      SELECT id_conta, ROUND(SUM(revenue), 2) as revenue, ROUND(SUM(vol_usd), 2) as vol_usd FROM (
        SELECT id_conta, SUM((exchange_rate - ptax) / exchange_rate * amount_usd) as revenue, SUM(amount_usd) as vol_usd
        FROM br_transaction_to_usa WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
        GROUP BY id_conta
        UNION ALL
        SELECT id_conta, SUM((ptax - cotacao) / ptax * valor) as revenue, SUM(valor) as vol_usd
        FROM pagamento_br WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
          AND cotacao IS NOT NULL AND cotacao > 0 AND (pgto IS NULL OR pgto != 'balance')
        GROUP BY id_conta
      ) c GROUP BY id_conta
    `, [dataInicio, dataFim, dataInicio, dataFim]),
    pool.execute(`
      SELECT id_conta, ROUND(SUM(revenue), 2) as revenue, ROUND(SUM(vol_usd), 2) as vol_usd FROM (
        SELECT id_conta, SUM((exchange_rate - ptax) / exchange_rate * amount_usd) as revenue, SUM(amount_usd) as vol_usd
        FROM br_transaction_to_usa WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
        GROUP BY id_conta
        UNION ALL
        SELECT id_conta, SUM((ptax - cotacao) / ptax * valor) as revenue, SUM(valor) as vol_usd
        FROM pagamento_br WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
          AND cotacao IS NOT NULL AND cotacao > 0 AND (pgto IS NULL OR pgto != 'balance')
        GROUP BY id_conta
      ) p GROUP BY id_conta
    `, [prevStartStr, prevEndStr, prevStartStr, prevEndStr])
  ]);

  const currMap = {};
  currRevenue.forEach(r => { currMap[r.id_conta] = { revenue: Number(r.revenue), vol_usd: Number(r.vol_usd) }; });
  const prevMap = {};
  prevRevenue.forEach(r => { prevMap[r.id_conta] = { revenue: Number(r.revenue), vol_usd: Number(r.vol_usd) }; });

  const allClientIds = new Set([...Object.keys(currMap), ...Object.keys(prevMap)]);
  const expansion = {
    new_clients: { count: 0, revenue: 0 },
    expansion: { count: 0, revenue: 0 },
    stable: { count: 0, revenue: 0 },
    contraction: { count: 0, revenue: 0 },
    churned: { count: 0, revenue: 0 }
  };
  allClientIds.forEach(id => {
    const curr = currMap[id];
    const prev = prevMap[id];
    if (curr && !prev) {
      expansion.new_clients.count++; expansion.new_clients.revenue += curr.revenue;
    } else if (!curr && prev) {
      expansion.churned.count++; expansion.churned.revenue -= Math.abs(prev.revenue);
    } else if (curr && prev) {
      const absP = Math.abs(prev.revenue);
      const change = absP > 0 ? (curr.revenue - prev.revenue) / absP : 0;
      if (change > 0.1) {
        expansion.expansion.count++; expansion.expansion.revenue += (curr.revenue - prev.revenue);
      } else if (change < -0.1) {
        expansion.contraction.count++; expansion.contraction.revenue += (curr.revenue - prev.revenue);
      } else {
        expansion.stable.count++; expansion.stable.revenue += curr.revenue;
      }
    }
  });
  return expansion;
}

async function _fetchCrossSell(dataInicio, dataFim) {
  const [crossSellData] = await pool.execute(`
    SELECT c.id_conta, t.vol_usd as pay_vol, p.vol_usd as checkout_vol
    FROM (
      SELECT DISTINCT id_conta FROM br_transaction_to_usa WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
      UNION
      SELECT DISTINCT id_conta FROM pagamento_br WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
        AND cotacao IS NOT NULL AND cotacao > 0 AND (pgto IS NULL OR pgto != 'balance')
    ) c
    LEFT JOIN (
      SELECT id_conta, ROUND(SUM(amount_usd), 2) as vol_usd
      FROM br_transaction_to_usa WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
      GROUP BY id_conta
    ) t ON t.id_conta = c.id_conta
    LEFT JOIN (
      SELECT id_conta, ROUND(SUM(valor), 2) as vol_usd
      FROM pagamento_br WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
        AND cotacao IS NOT NULL AND cotacao > 0 AND (pgto IS NULL OR pgto != 'balance')
      GROUP BY id_conta
    ) p ON p.id_conta = c.id_conta
  `, [dataInicio, dataFim, dataInicio, dataFim, dataInicio, dataFim, dataInicio, dataFim]);

  const crossSell = { pay_only: { count: 0, vol: 0 }, checkout_only: { count: 0, vol: 0 }, both: { count: 0, vol: 0 } };
  crossSellData.forEach(r => {
    const pv = Number(r.pay_vol) || 0;
    const cv = Number(r.checkout_vol) || 0;
    if (pv > 0 && cv > 0) { crossSell.both.count++; crossSell.both.vol += pv + cv; }
    else if (pv > 0) { crossSell.pay_only.count++; crossSell.pay_only.vol += pv; }
    else if (cv > 0) { crossSell.checkout_only.count++; crossSell.checkout_only.vol += cv; }
  });
  return crossSell;
}

async function _fetchMaturity(dataInicio, dataFim, prevStartStr, prevEndStr) {
  const [maturityData] = await pool.execute(`
    SELECT id_conta,
      MIN(first_op) as first_op,
      FLOOR(DATEDIFF(CURDATE(), MIN(first_op)) / 30.44) as months_active,
      SUM(vol_usd) as lifetime_vol,
      SUM(CASE WHEN period = 'curr' THEN vol_usd ELSE 0 END) as curr_vol,
      SUM(CASE WHEN period = 'prev' THEN vol_usd ELSE 0 END) as prev_vol
    FROM (
      SELECT id_conta, MIN(created_at) as first_op, SUM(amount_usd) as vol_usd, 'all' as period
      FROM br_transaction_to_usa GROUP BY id_conta
      UNION ALL
      SELECT id_conta, MIN(created_at) as first_op, SUM(valor) as vol_usd, 'all' as period
      FROM pagamento_br WHERE cotacao IS NOT NULL AND cotacao > 0 AND (pgto IS NULL OR pgto != 'balance')
      GROUP BY id_conta
      UNION ALL
      SELECT id_conta, NULL, SUM(amount_usd) as vol_usd, 'curr' as period
      FROM br_transaction_to_usa WHERE DATE(created_at) >= ? AND DATE(created_at) <= ? GROUP BY id_conta
      UNION ALL
      SELECT id_conta, NULL, SUM(valor) as vol_usd, 'curr' as period
      FROM pagamento_br WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
        AND cotacao IS NOT NULL AND cotacao > 0 AND (pgto IS NULL OR pgto != 'balance')
      GROUP BY id_conta
      UNION ALL
      SELECT id_conta, NULL, SUM(amount_usd) as vol_usd, 'prev' as period
      FROM br_transaction_to_usa WHERE DATE(created_at) >= ? AND DATE(created_at) <= ? GROUP BY id_conta
      UNION ALL
      SELECT id_conta, NULL, SUM(valor) as vol_usd, 'prev' as period
      FROM pagamento_br WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
        AND cotacao IS NOT NULL AND cotacao > 0 AND (pgto IS NULL OR pgto != 'balance')
      GROUP BY id_conta
    ) combined GROUP BY id_conta
  `, [dataInicio, dataFim, dataInicio, dataFim, prevStartStr, prevEndStr, prevStartStr, prevEndStr]);

  const maturity = { new_client: { count: 0, vol: 0 }, growing: { count: 0, vol: 0 }, mature: { count: 0, vol: 0 }, declining: { count: 0, vol: 0 } };
  maturityData.forEach(r => {
    const months = Number(r.months_active) || 0;
    const cv = Number(r.curr_vol) || 0;
    const pv = Number(r.prev_vol) || 0;
    const lv = Number(r.lifetime_vol) || 0;
    if (months < 3) {
      maturity.new_client.count++; maturity.new_client.vol += lv;
    } else if (pv > 0 && cv < pv * 0.85) {
      maturity.declining.count++; maturity.declining.vol += lv;
    } else if (months >= 12) {
      maturity.mature.count++; maturity.mature.vol += lv;
    } else {
      maturity.growing.count++; maturity.growing.vol += lv;
    }
  });
  return maturity;
}

module.exports = {
  fetchBIData,
  fetchRevenueAnalytics,
  fetchBIStrategic
};
