/**
 * BI - CCC (Central Command Center) — CambioReal
 * Login Unificado: todos os usuarios acessam via /login
 *
 * Uso:  node server.js
 * Abre: http://localhost:3080
 */
require('dotenv').config();

const express = require('express');
const session = require('express-session');
const path = require('path');
const { authenticate, requireAuth, requireRole, requirePermission, createAgente, createUser } = require('./src/auth');
const { PANELS, PANEL_ROUTE } = require('./src/panels');
const { fetchTransacoes, fetchAllTransacoes, serialize, fetchDailyStats, fetchKPIs, fetchTrend30Days, fetchTopAgentes, fetchTrendByPeriod, fetchKPIsByPeriod, fetchBIData, fetchRevenueAnalytics, fetchBIStrategic, fetchTopClients, fetchClientSearch, fetchClientProfile, fetchClientData, fetchMerchantProfile, fetchMerchantData, fetchProviderPerformance, fetchFailedTransactions, fetchProviderTrend } = require('./src/queries');
const { buildAdminProvidersHTML } = require('./src/admin-providers');
const pool = require('./src/db-rds');
const { buildHTML } = require('./src/dashboard');
const { buildAdminHTML } = require('./src/admin-panel');
const { buildAdminHomeHTML } = require('./src/admin-home');
const { buildAdminDashboardHTML } = require('./src/admin-dashboard');
const { buildAdminBIHTML } = require('./src/admin-bi');
const { buildAdminClienteHTML } = require('./src/admin-cliente');
const { exportToExcel, createWorkbook, sendWorkbook } = require('./src/export/excel-export');
const { startAlertEngine, getAlerts, acknowledgeAlert, getAlertHistory, getUnackedCount } = require('./src/alerts/alert-engine');
const { predictChurnRisk } = require('./src/services/churn-predictor');
const { forecastFromTrend } = require('./src/services/forecast');
const { startETL } = require('./src/etl/daily-sync');
const bcrypt = require('bcrypt');
const db = require('./src/db-local');
const cache = require('./src/cache');

const app = express();
const PORT = process.env.PORT || 3080;

// Middleware
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(session({
  secret: process.env.SESSION_SECRET || 'bi-agentes-default-secret',
  resave: false,
  saveUninitialized: false,
  cookie: { maxAge: 8 * 60 * 60 * 1000 }, // 8 horas
}));

// Static files
app.use('/public', express.static(path.join(__dirname, 'public')));

// --- Unified Login Routes ---

// Helper function to get redirect URL based on user permissions
function getRedirectForUser(user) {
  const perms = Array.isArray(user.permissions) ? user.permissions : JSON.parse(user.permissions || '[]');
  const priority = ['corporate', 'bi', 'cliente', 'providers', 'usuarios', 'dashboard'];
  for (const key of priority) {
    if (perms.includes(key)) return PANEL_ROUTE[key];
  }
  return '/login';
}

// Root -> login page (or redirect if logged in)
app.get('/', (req, res) => {
  if (req.session?.user) {
    return res.redirect(getRedirectForUser(req.session.user));
  }
  res.redirect('/login');
});

// Login page
app.get('/login', (req, res) => {
  if (req.session?.user) {
    return res.redirect(getRedirectForUser(req.session.user));
  }
  res.sendFile(path.join(__dirname, 'public', 'login.html'));
});

// Unified Login POST - detects role and redirects accordingly
app.post('/login', async (req, res) => {
  const { email, senha } = req.body;
  const emailParam = encodeURIComponent(email || '');
  try {
    const user = await authenticate(email, senha);
    if (!user) return res.redirect(`/login?error=1&email=${emailParam}`);

    // Unified session
    const permissions = JSON.parse(user.permissions || '[]');
    req.session.user = {
      id: user.id,
      email: user.email,
      nome: user.nome,
      role: user.role || 'agente',
      agente_id: user.agente_id,
      permissions
    };

    // Redirect based on permissions
    res.redirect(getRedirectForUser(req.session.user));
  } catch (err) {
    console.error('Login error:', err);
    res.redirect(`/login?error=1&email=${emailParam}`);
  }
});

// Unified Logout
app.get('/logout', (req, res) => {
  req.session.destroy(() => res.redirect('/login'));
});

// Legacy admin login - redirect to unified login
app.get('/admin/login', (req, res) => {
  res.redirect('/login');
});

// Legacy admin logout - redirect to unified logout
app.get('/admin/logout', (req, res) => {
  res.redirect('/logout');
});

// --- Agent Routes ---

// Dashboard (requires 'dashboard' permission)
app.get('/dashboard', requirePermission('dashboard'), async (req, res) => {
  try {
    const user = req.session.user;
    const { rowsBrlUsd, rowsUsdBrl } = await fetchTransacoes(user.agente_id);
    const data = serialize(rowsBrlUsd, rowsUsdBrl);
    const html = buildHTML(data, user);
    res.send(html);
  } catch (err) {
    console.error('Dashboard error:', err);
    res.status(500).send('Erro ao carregar dashboard: ' + err.message);
  }
});

// --- Admin Routes (User Management - admin only) ---

// Admin home - User management panel
app.get('/admin', requirePermission('usuarios'), (req, res) => {
  try {
    const agentes = db.prepare('SELECT * FROM agentes ORDER BY id DESC').all();
    const html = buildAdminHTML(agentes, req.session.user);
    res.send(html);
  } catch (err) {
    console.error('Admin panel error:', err);
    res.status(500).send('Erro ao carregar painel admin: ' + err.message);
  }
});

// Alias: /admin/usuarios -> /admin
app.get('/admin/usuarios', requirePermission('usuarios'), (req, res) => {
  res.redirect('/admin');
});

// Legacy route - redirect to /admin
app.get('/admin/agentes', requirePermission('usuarios'), (req, res) => {
  res.redirect('/admin');
});

// --- Corporate Routes (Dashboard + Emulation - corporate and admin) ---

// Corporate Dashboard - Full KPIs, Trends and Ranking
app.get('/corporate', requirePermission('corporate'), async (req, res) => {
  try {
    const user = req.session.user;
    const html = buildAdminDashboardHTML(user);
    res.send(html);
  } catch (err) {
    console.error('Corporate dashboard error:', err);
    res.status(500).send('Erro ao carregar dashboard corporate: ' + err.message);
  }
});

// Legacy route - redirect to /corporate
app.get('/corporate/dashboard', requirePermission('corporate'), (req, res) => {
  res.redirect('/corporate');
});

// Corporate emulate agent - view dashboard as specific agent
app.get('/corporate/emular/:agente_id', requirePermission('corporate'), async (req, res) => {
  try {
    const agenteId = parseInt(req.params.agente_id);
    const agente = db.prepare('SELECT * FROM agentes WHERE agente_id = ?').get(agenteId);

    if (!agente) {
      return res.status(404).send('Agente nao encontrado');
    }

    const { rowsBrlUsd, rowsUsdBrl } = await fetchTransacoes(agenteId);
    const data = serialize(rowsBrlUsd, rowsUsdBrl);
    const html = buildHTML(data, {
      nome: agente.nome + ' (Emulando)',
      agente_id: agenteId,
      email: agente.email,
      emulatorRole: req.session.user.role,
      permissions: req.session.user.permissions || []
    }, true, null, false, true); // isEmulating = true
    res.send(html);
  } catch (err) {
    console.error('Corporate emulate error:', err);
    res.status(500).send('Erro ao emular agente: ' + err.message);
  }
});

// Legacy route - redirect to /corporate/emular
app.get('/admin/emular/:agente_id', requirePermission('corporate'), (req, res) => {
  res.redirect(`/corporate/emular/${req.params.agente_id}`);
});

// --- Live Rate Proxy (caches for 3s to avoid rate limiting) ---
let _rateCache = { data: null, ts: 0 };
app.get('/api/cotacao', async (req, res) => {
  try {
    const now = Date.now();
    if (_rateCache.data && now - _rateCache.ts < 3000) {
      return res.json(_rateCache.data);
    }
    const token = process.env.AWESOME_API_TOKEN || '';
    const url = 'https://economia.awesomeapi.com.br/json/last/USD-BRL,EUR-BRL' + (token ? '?token=' + token : '');
    const resp = await fetch(url);
    const json = await resp.json();
    _rateCache = { data: json, ts: now };
    res.json(json);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// --- Corporate API Routes (dashboard data - corporate and admin) ---

// API endpoint for corporate dashboard data
app.get('/corporate/api/data', requirePermission('corporate'), async (req, res) => {
  try {
    const dias = parseInt(req.query.dias) || 90;
    const { rowsBrlUsd, rowsUsdBrl } = await fetchAllTransacoes(dias);
    const data = serialize(rowsBrlUsd, rowsUsdBrl);
    res.json({ success: true, data, count: data.length });
  } catch (err) {
    console.error('Corporate API error:', err);
    res.status(500).json({ success: false, error: err.message });
  }
});

// API: KPIs (hoje vs média 30 dias) - com cache
app.get('/corporate/api/kpis', requirePermission('corporate'), async (req, res) => {
  try {
    const data = await cache.getOrFetch('kpis', fetchKPIs, 5 * 60 * 1000);
    res.json({ success: true, data });
  } catch (err) {
    console.error('KPIs API error:', err);
    res.status(500).json({ success: false, error: err.message });
  }
});

// API: Tendência 30 dias - com cache
app.get('/corporate/api/trend', requirePermission('corporate'), async (req, res) => {
  try {
    const data = await cache.getOrFetch('trend30', fetchTrend30Days, 10 * 60 * 1000);
    res.json({ success: true, data });
  } catch (err) {
    console.error('Trend API error:', err);
    res.status(500).json({ success: false, error: err.message });
  }
});

// API: Top 5 agentes - com cache por período
app.get('/corporate/api/top-agentes', requirePermission('corporate'), async (req, res) => {
  try {
    const dias = parseInt(req.query.dias) || 30;
    const cacheKey = `top-agentes-${dias}`;

    // Busca dados do RDS (com cache)
    const rawData = await cache.getOrFetch(cacheKey, () => fetchTopAgentes(dias), 10 * 60 * 1000);

    // Adiciona nomes dos agentes do SQLite local
    const data = rawData.map(r => {
      const agente = db.prepare('SELECT nome FROM agentes WHERE agente_id = ?').get(r.agente_id);
      return {
        ...r,
        agente: agente?.nome || `Agente #${r.agente_id}`
      };
    });

    res.json({ success: true, data });
  } catch (err) {
    console.error('Top Agentes API error:', err);
    res.status(500).json({ success: false, error: err.message });
  }
});

// API: Corporate Dashboard - KPIs por período
app.get('/corporate/api/kpis-period', requirePermission('corporate'), async (req, res) => {
  try {
    const { inicio, fim } = req.query;
    if (!inicio || !fim) {
      return res.status(400).json({ success: false, error: 'Parametros inicio e fim sao obrigatorios' });
    }
    const data = await fetchKPIsByPeriod(inicio, fim);
    res.json({ success: true, data });
  } catch (err) {
    console.error('Corporate KPIs API error:', err);
    res.status(500).json({ success: false, error: err.message });
  }
});

// API: Corporate Dashboard - Tendência por período
app.get('/corporate/api/trend-period', requirePermission('corporate'), async (req, res) => {
  try {
    const { inicio, fim } = req.query;
    if (!inicio || !fim) {
      return res.status(400).json({ success: false, error: 'Parametros inicio e fim sao obrigatorios' });
    }
    const data = await fetchTrendByPeriod(inicio, fim);
    res.json({ success: true, data });
  } catch (err) {
    console.error('Corporate Trend API error:', err);
    res.status(500).json({ success: false, error: err.message });
  }
});

// Legacy API routes - redirect to /corporate/api/*
app.get('/admin/api/data', requirePermission('corporate'), (req, res) => {
  res.redirect(`/corporate/api/data?${new URLSearchParams(req.query)}`);
});
app.get('/admin/api/kpis', requirePermission('corporate'), (req, res) => {
  res.redirect('/corporate/api/kpis');
});
app.get('/admin/api/trend', requirePermission('corporate'), (req, res) => {
  res.redirect('/corporate/api/trend');
});
app.get('/admin/api/top-agentes', requirePermission('corporate'), (req, res) => {
  res.redirect(`/corporate/api/top-agentes?${new URLSearchParams(req.query)}`);
});
app.get('/admin/api/corporate/kpis', requirePermission('corporate'), (req, res) => {
  res.redirect(`/corporate/api/kpis-period?${new URLSearchParams(req.query)}`);
});
app.get('/admin/api/corporate/trend', requirePermission('corporate'), (req, res) => {
  res.redirect(`/corporate/api/trend-period?${new URLSearchParams(req.query)}`);
});
app.get('/admin/dashboard', requirePermission('corporate'), (req, res) => {
  res.redirect('/corporate/dashboard');
});

// --- Admin BI Dashboard ---
app.get('/admin/bi', requirePermission('bi'), (req, res) => {
  try {
    res.set('Cache-Control', 'no-store, no-cache, must-revalidate');
    res.set('Pragma', 'no-cache');
    const html = buildAdminBIHTML(req.session.user);
    res.send(html);
  } catch (err) {
    console.error('Admin BI error:', err);
    res.status(500).send('Erro ao carregar BI: ' + err.message);
  }
});

app.get('/admin/api/bi', requirePermission('bi'), async (req, res) => {
  try {
    const start = req.query.start;
    const end = req.query.end;
    if (!start || !end) return res.status(400).json({ error: 'start and end required' });

    const getAgenteName = (agenteId) => {
      const row = db.prepare('SELECT nome FROM agentes WHERE agente_id = ?').get(agenteId);
      return row ? row.nome : null;
    };

    const data = await cache.getOrFetchRange('bi', start, end, () => fetchBIData(start, end, getAgenteName));
    res.json(data);
  } catch (err) {
    console.error('Admin BI API error:', err);
    res.status(500).json({ error: err.message });
  }
});

app.get('/admin/api/bi/revenue', requirePermission('bi'), async (req, res) => {
  try {
    const { start, end, granularity } = req.query;
    if (!start || !end) return res.status(400).json({ error: 'start and end required' });
    const g = granularity || 'dia';
    const data = await cache.getOrFetchRange(`bi-rev-${g}`, start, end, () => fetchRevenueAnalytics(start, end, g));
    res.json(data);
  } catch (err) {
    console.error('Revenue API error:', err);
    res.status(500).json({ error: err.message });
  }
});

app.get('/admin/api/bi/strategic', requirePermission('bi'), async (req, res) => {
  try {
    const { start, end } = req.query;
    if (!start || !end) return res.status(400).json({ error: 'start and end required' });
    const data = await cache.getOrFetchRange('bi-strat', start, end, () => fetchBIStrategic(start, end));
    res.json(data);
  } catch (err) {
    console.error('Strategic BI API error:', err);
    res.status(500).json({ error: err.message });
  }
});

// --- Admin Cliente Dashboard ---
app.get('/admin/cliente', requirePermission('cliente'), (req, res) => {
  try {
    res.set('Cache-Control', 'no-store, no-cache, must-revalidate');
    res.set('Pragma', 'no-cache');
    const html = buildAdminClienteHTML(req.session.user);
    res.send(html);
  } catch (err) {
    console.error('Admin Cliente error:', err);
    res.status(500).send('Erro ao carregar pagina de cliente: ' + err.message);
  }
});

app.get('/admin/api/clientes/top', requirePermission('cliente'), async (req, res) => {
  try {
    const data = await cache.getOrFetch('top-clients', fetchTopClients, 15 * 60 * 1000);
    res.json(data);
  } catch (err) {
    console.error('Top clients API error:', err);
    res.status(500).json({ error: err.message });
  }
});

app.get('/admin/api/clientes/search', requirePermission('cliente'), async (req, res) => {
  try {
    const q = (req.query.q || '').trim();
    if (q.length < 2) return res.json([]);
    const data = await fetchClientSearch(q);
    res.json(data);
  } catch (err) {
    console.error('Client search API error:', err);
    res.status(500).json({ error: err.message });
  }
});

app.get('/admin/api/cliente/:id/profile', requirePermission('cliente'), async (req, res) => {
  try {
    const clienteId = parseInt(req.params.id);
    if (!clienteId) return res.status(400).json({ error: 'Invalid client ID' });
    const [profile, merchant] = await Promise.all([
      fetchClientProfile(clienteId),
      fetchMerchantProfile(clienteId)
    ]);
    if (merchant.is_merchant) {
      const ck = merchant.checkout;
      profile.merchant = { empresa_id: merchant.empresa_id, nome_empresa: merchant.nome_empresa };
      profile.total_ops += ck.tx_count;
      profile.total_vol_usd += ck.vol_usd;
      profile.total_spread_revenue += ck.revenue;
      profile.ltv = profile.total_spread_revenue;
      // Extend date ranges
      const dates = [profile.first_op, ck.first_op].filter(Boolean);
      const lastDates = [profile.last_op, ck.last_op].filter(Boolean);
      if (dates.length) profile.first_op = dates.sort()[0];
      if (lastDates.length) {
        profile.last_op = lastDates.sort().pop();
        profile.days_inactive = Math.round((Date.now() - new Date(profile.last_op).getTime()) / 86400000);
      }
      profile.months_active = Math.max(profile.months_active, ck.months_active);
      profile.avg_monthly_vol = profile.months_active > 0 ? Math.round(profile.total_vol_usd / profile.months_active) : 0;
      profile.avg_monthly_ops = profile.months_active > 0 ? Math.round(profile.total_ops / profile.months_active * 10) / 10 : 0;
      profile.avg_monthly_revenue = profile.months_active > 0 ? Math.round(profile.total_spread_revenue / profile.months_active * 100) / 100 : 0;
      profile.checkout = ck;
    }
    res.json(profile);
  } catch (err) {
    console.error('Client profile API error:', err);
    res.status(500).json({ error: err.message });
  }
});

app.get('/admin/api/cliente/:id/data', requirePermission('cliente'), async (req, res) => {
  try {
    const clienteId = parseInt(req.params.id);
    const { start, end } = req.query;
    if (!clienteId || !start || !end) return res.status(400).json({ error: 'client ID, start and end required' });

    const merchant = await fetchMerchantProfile(clienteId);
    if (merchant.is_merchant) {
      const [data, mData] = await Promise.all([
        fetchClientData(clienteId, start, end),
        fetchMerchantData(merchant.empresa_id, start, end)
      ]);
      // Pass checkout KPIs separately (no merge into hero totals)
      data.kpis.checkout = mData.kpis;
      // Merchant-specific data
      data.merchant = {
        monthly: mData.monthly,
        topPayers: mData.topPayers,
        comparison: mData.comparison
      };
      // Merge transactions (checkout txs get flow="Checkout")
      data.transactions = data.transactions.concat(mData.transactions)
        .sort((a, b) => b.date.localeCompare(a.date));
      res.json(data);
    } else {
      const data = await fetchClientData(clienteId, start, end);
      res.json(data);
    }
  } catch (err) {
    console.error('Client data API error:', err);
    res.status(500).json({ error: err.message });
  }
});

// Create user (admin only)
app.post('/admin/agentes', requireRole('admin'), async (req, res) => {
  const { nome, email, agente_id, senha, role, permissions } = req.body;
  try {
    if (!nome || !email || !senha) {
      return res.status(400).json({ error: 'Nome, email e senha sao obrigatorios' });
    }

    const userRole = role || 'agente';
    const agenteId = agente_id || 0;

    // dashboard permission requires agente_id
    if (Array.isArray(permissions) && permissions.includes('dashboard') && !agenteId) {
      return res.status(400).json({ error: 'Agente ID e obrigatorio para acesso ao Meu Dashboard' });
    }

    const result = await createUser(email, senha, nome, userRole, agenteId, Array.isArray(permissions) ? permissions : null);
    res.json({ success: true, id: result.lastInsertRowid });
  } catch (err) {
    console.error('Create user error:', err);
    if (err.message && err.message.includes('UNIQUE')) {
      return res.status(400).json({ error: 'E-mail ja cadastrado' });
    }
    res.status(500).json({ error: 'Erro ao criar usuario' });
  }
});

// Update user (admin only)
app.put('/admin/agentes/:id', requireRole('admin'), async (req, res) => {
  const { id } = req.params;
  const { nome, email, agente_id, ativo, senha, role, permissions } = req.body;
  try {
    const agent = db.prepare('SELECT * FROM agentes WHERE id = ?').get(id);
    if (!agent) {
      return res.status(404).json({ error: 'Usuario nao encontrado' });
    }

    if (senha) {
      const hash = await bcrypt.hash(senha, 10);
      db.prepare('UPDATE agentes SET senha_hash = ? WHERE id = ?').run(hash, id);
    }

    if (nome !== undefined) {
      db.prepare('UPDATE agentes SET nome = ? WHERE id = ?').run(nome, id);
    }
    if (email !== undefined) {
      db.prepare('UPDATE agentes SET email = ? WHERE id = ?').run(email, id);
    }
    if (agente_id !== undefined) {
      db.prepare('UPDATE agentes SET agente_id = ? WHERE id = ?').run(agente_id, id);
    }
    if (ativo !== undefined) {
      db.prepare('UPDATE agentes SET ativo = ? WHERE id = ?').run(ativo, id);
    }
    if (role !== undefined) {
      db.prepare('UPDATE agentes SET role = ? WHERE id = ?').run(role, id);
    }
    if (Array.isArray(permissions)) {
      // Anti-lockout: prevent user from removing 'usuarios' from their own permissions
      if (parseInt(id) === req.session.user.id && !permissions.includes('usuarios')) {
        return res.status(400).json({ error: 'Voce nao pode remover a permissao "Usuarios" de si mesmo' });
      }
      db.prepare('UPDATE agentes SET permissions = ? WHERE id = ?').run(JSON.stringify(permissions), id);
    }

    res.json({ success: true });
  } catch (err) {
    console.error('Update user error:', err);
    if (err.message && err.message.includes('UNIQUE')) {
      return res.status(400).json({ error: 'E-mail ja cadastrado' });
    }
    res.status(500).json({ error: 'Erro ao atualizar usuario' });
  }
});

// Delete/deactivate user (admin only)
app.delete('/admin/agentes/:id', requireRole('admin'), (req, res) => {
  const { id } = req.params;
  try {
    const result = db.prepare('UPDATE agentes SET ativo = 0 WHERE id = ?').run(id);
    if (result.changes === 0) {
      return res.status(404).json({ error: 'Usuario nao encontrado' });
    }
    res.json({ success: true });
  } catch (err) {
    console.error('Delete user error:', err);
    res.status(500).json({ error: 'Erro ao desativar usuario' });
  }
});

// --- Excel Export Endpoints ---

app.get('/admin/api/export/bi-excel', requirePermission('bi'), async (req, res) => {
  try {
    const { start, end } = req.query;
    if (!start || !end) return res.status(400).json({ error: 'start and end required' });
    const getAgenteName = (agenteId) => {
      const row = db.prepare('SELECT nome FROM agentes WHERE agente_id = ?').get(agenteId);
      return row ? row.nome : null;
    };
    const data = await fetchBIData(start, end, getAgenteName);

    // Build multi-sheet workbook
    const ExcelJS = require('exceljs');
    const workbook = new ExcelJS.Workbook();
    workbook.creator = 'CambioReal BI-CCC';

    // Sheet 1: KPI Summary
    const kpiSheet = workbook.addWorksheet('KPI Summary');
    kpiSheet.columns = [
      { header: 'Metric', key: 'metric', width: 30 },
      { header: 'Current', key: 'current', width: 18 },
      { header: 'Previous', key: 'previous', width: 18 },
      { header: 'Change %', key: 'change', width: 14 }
    ];
    const k = data.kpis.total;
    const c = data.comparison;
    const pctChg = (curr, prev) => prev > 0 ? Math.round((curr - prev) / prev * 100) : 0;
    kpiSheet.addRows([
      { metric: 'Total Transactions', current: k.qtd, previous: c.prev_qtd, change: pctChg(k.qtd, c.prev_qtd) },
      { metric: 'Total Volume USD', current: k.vol_usd, previous: c.prev_vol_usd, change: pctChg(k.vol_usd, c.prev_vol_usd) },
      { metric: 'Spread Revenue', current: k.spread_revenue, previous: c.prev_spread, change: pctChg(k.spread_revenue, c.prev_spread) },
      { metric: 'Active Clients', current: k.clientes, previous: '-', change: '-' },
      { metric: 'Avg Ticket', current: k.ticket_medio, previous: '-', change: '-' },
      { metric: 'Retention Rate %', current: data.retention.rate, previous: '-', change: '-' }
    ]);

    // Sheet 2: Top Clients
    const clientSheet = workbook.addWorksheet('Top Clients');
    clientSheet.columns = [
      { header: 'Client', key: 'nome', width: 30 },
      { header: 'Volume USD', key: 'vol_usd', width: 18 },
      { header: 'Transactions', key: 'qtd', width: 14 }
    ];
    data.topClients.forEach(c => clientSheet.addRow(c));

    // Sheet 3: Agent Ranking
    const agentSheet = workbook.addWorksheet('Agent Ranking');
    agentSheet.columns = [
      { header: 'Rank', key: 'rank', width: 8 },
      { header: 'Agent', key: 'nome', width: 25 },
      { header: 'Volume USD', key: 'vol_usd', width: 18 },
      { header: 'Transactions', key: 'qtd', width: 14 },
      { header: 'Spread Revenue', key: 'spread_revenue', width: 18 },
      { header: 'Clients', key: 'clientes', width: 12 }
    ];
    data.agentRanking.forEach(a => agentSheet.addRow(a));

    // Sheet 4: Clients at Risk
    const riskSheet = workbook.addWorksheet('Clients at Risk');
    riskSheet.columns = [
      { header: 'Client', key: 'nome', width: 30 },
      { header: 'Volume USD', key: 'vol_usd', width: 18 },
      { header: 'Transactions', key: 'qtd', width: 14 },
      { header: 'Last Activity', key: 'last_op', width: 18 },
      { header: 'Days Inactive', key: 'days_inactive', width: 14 }
    ];
    data.clientsAtRisk.forEach(c => riskSheet.addRow(c));

    // Style all sheet headers
    workbook.eachSheet(sheet => {
      const hr = sheet.getRow(1);
      hr.eachCell(cell => {
        cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF7600BE' } };
        cell.font = { bold: true, color: { argb: 'FFFFFFFF' }, size: 11 };
        cell.alignment = { vertical: 'middle', horizontal: 'center' };
      });
      hr.height = 28;
      sheet.views = [{ state: 'frozen', ySplit: 1 }];
    });

    await sendWorkbook(res, workbook, `BI_Executive_${start}_${end}`);
  } catch (err) {
    console.error('BI Export error:', err);
    res.status(500).json({ error: err.message });
  }
});

app.get('/admin/api/export/clients-excel', requirePermission('cliente'), async (req, res) => {
  try {
    const clients = await cache.getOrFetch('top-clients', fetchTopClients, 15 * 60 * 1000);
    await exportToExcel(res, clients, [
      { header: 'Client', key: 'nome', width: 30 },
      { header: 'Volume USD', key: 'vol', width: 18, type: 'currency' },
      { header: 'Operations', key: 'ops', width: 14, type: 'number' },
      { header: 'Months Active', key: 'months', width: 14, type: 'number' },
      { header: 'Last Activity', key: 'lastOp', width: 16, type: 'date' }
    ], 'Top Clients', `Top_Clients_${new Date().toISOString().slice(0, 10)}`);
  } catch (err) {
    console.error('Clients Export error:', err);
    res.status(500).json({ error: err.message });
  }
});

app.get('/admin/api/export/providers-excel', requirePermission('providers'), async (req, res) => {
  try {
    const { start, end } = req.query;
    if (!start || !end) return res.status(400).json({ error: 'start and end required' });
    const data = await fetchProviderPerformance(start, end);
    await exportToExcel(res, data.providers, [
      { header: 'Provider', key: 'provider', width: 18 },
      { header: 'Flow', key: 'flow', width: 12 },
      { header: 'Total Tx', key: 'total_tx', width: 12, type: 'number' },
      { header: 'Success Tx', key: 'success_tx', width: 12, type: 'number' },
      { header: 'Success Rate %', key: 'success_rate', width: 14, type: 'percentage' },
      { header: 'Volume USD', key: 'vol_usd', width: 18, type: 'currency' },
      { header: 'Avg Ticket', key: 'avg_ticket', width: 14, type: 'currency' },
      { header: 'Spread %', key: 'avg_spread_pct', width: 12, type: 'percentage' },
      { header: 'Settlement Hours', key: 'avg_settlement_hours', width: 16, type: 'number' }
    ], 'Providers', `Providers_${start}_${end}`);
  } catch (err) {
    console.error('Providers Export error:', err);
    res.status(500).json({ error: err.message });
  }
});

app.get('/admin/api/export/transactions-excel', requirePermission('bi'), async (req, res) => {
  try {
    const { start, end } = req.query;
    const dias = start && end ? null : 90;
    let data;
    if (start && end) {
      // Use fetchBIData for date-filtered transactions
      const biData = await fetchBIData(start, end);
      // Combine trend data into flat rows
      const allTrend = [];
      ['brlUsd', 'usdBrl', 'usdUsd'].forEach(flow => {
        (biData.trend[flow] || []).forEach(r => {
          allTrend.push({ date: r.dia, flow, transactions: r.qtd, volume_usd: r.vol_usd, avg_spread: r.avg_spread || 0 });
        });
      });
      data = allTrend;
    } else {
      const raw = await fetchAllTransacoes(dias || 90);
      data = serialize(raw.rowsBrlUsd, raw.rowsUsdBrl);
    }

    await exportToExcel(res, data, data.length > 0 && data[0].fluxo ? [
      { header: 'Flow', key: 'fluxo', width: 12 },
      { header: 'Client', key: 'cliente', width: 28 },
      { header: 'Date', key: 'data_operacao', width: 18 },
      { header: 'BRL', key: 'valor_reais', width: 14, type: 'currency' },
      { header: 'USD', key: 'valor_dolar', width: 14, type: 'currency' },
      { header: 'PTAX', key: 'taxa_ptax', width: 12 },
      { header: 'Rate', key: 'taxa_cobrada', width: 12 },
      { header: 'Spread', key: 'spread_bruto', width: 12 },
      { header: 'Spread %', key: 'spread_pct', width: 10, type: 'percentage' },
      { header: 'IOF %', key: 'iof_pct', width: 8 },
      { header: 'Status', key: 'status', width: 14 }
    ] : [
      { header: 'Date', key: 'date', width: 14 },
      { header: 'Flow', key: 'flow', width: 12 },
      { header: 'Transactions', key: 'transactions', width: 14, type: 'number' },
      { header: 'Volume USD', key: 'volume_usd', width: 16, type: 'currency' },
      { header: 'Avg Spread %', key: 'avg_spread', width: 14, type: 'percentage' }
    ], 'Transactions', `Transactions_${start || 'last90d'}_${end || 'today'}`);
  } catch (err) {
    console.error('Transactions Export error:', err);
    res.status(500).json({ error: err.message });
  }
});

// --- Forecast API ---
app.get('/admin/api/bi/forecast', requirePermission('bi'), async (req, res) => {
  try {
    const metric = req.query.metric || 'volume';
    const days = parseInt(req.query.days) || 30;
    // Get last 90 days of trend data for forecasting
    const now = new Date();
    const start = new Date(now.getTime() - 90 * 86400000).toISOString().slice(0, 10);
    const end = now.toISOString().slice(0, 10);
    const biData = await fetchBIData(start, end);

    // Combine all flows into daily totals
    const dailyMap = {};
    ['brlUsd', 'usdBrl', 'usdUsd'].forEach(flow => {
      (biData.trend[flow] || []).forEach(d => {
        if (!dailyMap[d.dia]) dailyMap[d.dia] = { dia: d.dia, vol_usd: 0, qtd: 0 };
        dailyMap[d.dia].vol_usd += d.vol_usd;
        dailyMap[d.dia].qtd += d.qtd;
      });
    });
    const trendData = Object.values(dailyMap).sort((a, b) => a.dia.localeCompare(b.dia));

    const metricKey = metric === 'transactions' ? 'qtd' : 'vol_usd';
    const result = forecastFromTrend(trendData, metricKey, days);
    res.json(result);
  } catch (err) {
    console.error('Forecast API error:', err);
    res.status(500).json({ error: err.message });
  }
});

// --- Churn Risk API ---
app.get('/admin/api/cliente/:id/churn', requirePermission('cliente'), async (req, res) => {
  try {
    const clienteId = parseInt(req.params.id);
    if (!clienteId) return res.status(400).json({ error: 'Invalid client ID' });

    // Get profile and recent data
    const profile = await fetchClientProfile(clienteId);
    const now = new Date();
    const end = now.toISOString().slice(0, 10);
    const start30 = new Date(now.getTime() - 30 * 86400000).toISOString().slice(0, 10);
    const start60 = new Date(now.getTime() - 60 * 86400000).toISOString().slice(0, 10);

    const clientData = await fetchClientData(clienteId, start60, end);

    // Count operations in current vs previous 30-day windows
    const currOps = (clientData.trend.brlUsd || [])
      .filter(d => d.dia >= start30)
      .reduce((s, d) => s + d.qtd, 0) +
      (clientData.trend.usdBrl || [])
      .filter(d => d.dia >= start30)
      .reduce((s, d) => s + d.qtd, 0);

    const prevOps = (clientData.trend.brlUsd || [])
      .filter(d => d.dia < start30 && d.dia >= start60)
      .reduce((s, d) => s + d.qtd, 0) +
      (clientData.trend.usdBrl || [])
      .filter(d => d.dia < start30 && d.dia >= start60)
      .reduce((s, d) => s + d.qtd, 0);

    // Determine product count
    let productCount = 0;
    if (profile.brlUsd && profile.brlUsd.qtd > 0) productCount++;
    if (profile.usdBrl && profile.usdBrl.qtd > 0) productCount++;

    const churn = predictChurnRisk({
      days_inactive: profile.days_inactive,
      avg_monthly_ops: profile.avg_monthly_ops,
      avg_monthly_vol: profile.avg_monthly_vol,
      months_active: profile.months_active,
      curr_ops: currOps,
      prev_ops: prevOps,
      product_count: productCount
    });

    res.json(churn);
  } catch (err) {
    console.error('Churn API error:', err);
    res.status(500).json({ error: err.message });
  }
});

// --- Alert API Endpoints ---

app.get('/admin/api/alerts', requirePermission('bi'), (req, res) => {
  try {
    const unacked = req.query.unacked === '1';
    const alerts = getAlerts(24, unacked);
    res.json({ alerts, unacked_count: getUnackedCount() });
  } catch (err) {
    console.error('Alerts API error:', err);
    res.status(500).json({ error: err.message });
  }
});

app.put('/admin/api/alerts/:id/ack', requirePermission('bi'), (req, res) => {
  try {
    const id = parseInt(req.params.id);
    acknowledgeAlert(id);
    res.json({ success: true });
  } catch (err) {
    console.error('Alert ack error:', err);
    res.status(500).json({ error: err.message });
  }
});

app.get('/admin/api/alerts/history', requirePermission('bi'), (req, res) => {
  try {
    const days = parseInt(req.query.days) || 7;
    const alerts = getAlertHistory(days);
    res.json({ alerts });
  } catch (err) {
    console.error('Alert history error:', err);
    res.status(500).json({ error: err.message });
  }
});

// --- Health Check ---
app.get('/health', async (req, res) => {
  const health = {
    status: 'ok',
    uptime: Math.round(process.uptime()),
    memory: {
      rss: Math.round(process.memoryUsage().rss / 1024 / 1024),
      heap: Math.round(process.memoryUsage().heapUsed / 1024 / 1024)
    },
    cache: cache.stats(),
    timestamp: new Date().toISOString()
  };
  try {
    const conn = await pool.getConnection();
    await conn.execute('SELECT 1');
    conn.release();
    health.mysql = 'connected';
  } catch (err) {
    health.mysql = 'error: ' + err.message;
    health.status = 'degraded';
  }
  res.json(health);
});

// --- Provider Dashboard ---
app.get('/admin/providers', requirePermission('providers'), (req, res) => {
  try {
    res.set('Cache-Control', 'no-store, no-cache, must-revalidate');
    const html = buildAdminProvidersHTML(req.session.user);
    res.send(html);
  } catch (err) {
    console.error('Admin Providers error:', err);
    res.status(500).send('Erro ao carregar providers: ' + err.message);
  }
});

app.get('/admin/api/providers', requirePermission('providers'), async (req, res) => {
  try {
    const { start, end } = req.query;
    if (!start || !end) return res.status(400).json({ error: 'start and end required' });
    const data = await cache.getOrFetchRange('providers', start, end, () => fetchProviderPerformance(start, end));
    res.json(data);
  } catch (err) {
    console.error('Provider API error:', err);
    res.status(500).json({ error: err.message });
  }
});

app.get('/admin/api/providers/failed', requirePermission('providers'), async (req, res) => {
  try {
    const { start, end } = req.query;
    if (!start || !end) return res.status(400).json({ error: 'start and end required' });
    const data = await cache.getOrFetchRange('providers-fail', start, end, () => fetchFailedTransactions(start, end));
    res.json(data);
  } catch (err) {
    console.error('Failed TX API error:', err);
    res.status(500).json({ error: err.message });
  }
});

app.get('/admin/api/providers/trend', requirePermission('providers'), async (req, res) => {
  try {
    const { start, end } = req.query;
    if (!start || !end) return res.status(400).json({ error: 'start and end required' });
    const data = await cache.getOrFetchRange('providers-trend', start, end, () => fetchProviderTrend(start, end));
    res.json(data);
  } catch (err) {
    console.error('Provider Trend API error:', err);
    res.status(500).json({ error: err.message });
  }
});

// Start
app.listen(PORT, () => {
  console.log(`BI - CCC rodando: http://localhost:${PORT}`);

  // Inicializa cache com auto-refresh (atualiza a cada 5 minutos)
  console.log('[Cache] Inicializando cache com auto-refresh...');
  cache.registerAutoRefresh('kpis', fetchKPIs, 5 * 60 * 1000);
  cache.registerAutoRefresh('trend30', fetchTrend30Days, 10 * 60 * 1000);
  cache.registerAutoRefresh('top-agentes-30', () => fetchTopAgentes(30), 10 * 60 * 1000);
  cache.registerAutoRefresh('top-agentes-7', () => fetchTopAgentes(7), 10 * 60 * 1000);
  cache.registerAutoRefresh('top-agentes-90', () => fetchTopAgentes(90), 10 * 60 * 1000);

  // Start alert engine
  startAlertEngine();

  // Start ETL daily sync (MySQL RDS → SQLite analytics)
  startETL();
});
