/**
 * Gera HTML do dashboard — parametrizado por agente
 * Updated: 2026-02-09 - 4 decimal places for spread
 */
const { buildHeader, buildFooter, buildHead, getChartJsScript } = require('./ui-template');

function buildHTML(data, agente, isAgentDashboard = true, diasPeriodo = null, asyncLoad = false, isEmulating = false) {
  const now = new Date().toLocaleString('pt-BR');
  const isAdminDash = diasPeriodo !== null;
  // When emulating, use the emulator's role if provided, otherwise default to admin
  const emulatorRole = agente.emulatorRole || 'admin';
  const role = isEmulating ? emulatorRole : (isAdminDash ? 'admin' : 'agente');
  // Determine the back URL based on emulator's role
  const backUrl = emulatorRole === 'corporate' ? '/corporate' : '/admin';

  const pageScripts = getChartJsScript();

  const dashboardCSS = `
  /* Emulation Banner */
  .emulation-banner {
    background: linear-gradient(90deg, #FF6B35, #F7931E);
    color: white;
    padding: 10px 24px;
    text-align: center;
    font-size: 13px;
    font-weight: 600;
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 16px;
  }
  .emulation-banner a {
    background: rgba(255,255,255,0.2);
    color: white;
    padding: 6px 16px;
    border-radius: 6px;
    text-decoration: none;
    font-size: 12px;
    border: 1px solid rgba(255,255,255,0.3);
  }
  .emulation-banner a:hover {
    background: rgba(255,255,255,0.3);
  }

  /* Live Badge */
  .live-badge {
    background: rgba(255,255,255,0.15);
    padding: 6px 14px;
    border-radius: 20px;
    font-size: 11px;
    font-weight: 600;
    border: 1px solid rgba(255,255,255,0.2);
    margin-right: 12px;
  }
  .live-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    background: #4ADE80;
    border-radius: 50%;
    margin-right: 6px;
    animation: pulse 2s infinite;
  }
  @keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }

  /* Filters */
  .filters {
    background: var(--card);
    border-bottom: 1px solid var(--border);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
  }
  .filters-inner {
    max-width: 1600px;
    margin: 0 auto;
    padding: 14px 40px;
    display: flex;
    gap: 24px;
    align-items: center;
    flex-wrap: wrap;
  }
  .filter-group { display: flex; align-items: center; gap: 8px; }
  .filter-group label {
    font-size: 12px; font-weight: 600; color: var(--text-secondary);
    text-transform: uppercase; letter-spacing: 0.3px;
  }
  .filter-group input, .filter-group select {
    padding: 8px 14px; border: 1.5px solid var(--border); border-radius: 8px;
    font-size: 13px; font-family: inherit; background: white; color: var(--text); transition: all 0.15s;
  }
  .filter-group input:focus, .filter-group select:focus {
    outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(108,63,160,0.12);
  }
  .btn-apply {
    background: var(--primary); color: white; border: none; padding: 9px 24px;
    border-radius: 8px; font-size: 13px; font-weight: 600; font-family: inherit;
    cursor: pointer; transition: all 0.15s; box-shadow: 0 1px 3px rgba(108,63,160,0.3);
  }
  .btn-apply:hover { background: var(--primary-light); transform: translateY(-1px); }
  .btn-export {
    background: var(--green); color: white; border: none; padding: 9px 20px;
    border-radius: 8px; font-size: 13px; font-weight: 600; font-family: inherit;
    cursor: pointer; transition: all 0.15s; box-shadow: 0 1px 3px rgba(30,142,62,0.3);
    margin-left: 8px;
  }
  .btn-export:hover { background: #25a244; transform: translateY(-1px); }

  /* Trading Terminal - Live Rates */
  .trading-terminal {
    background: linear-gradient(135deg, #0F1923 0%, #1A2332 50%, #0D1B2A 100%);
    border-top: 1px solid rgba(0,255,136,0.15);
    border-bottom: 1px solid rgba(0,255,136,0.15);
    padding: 16px 40px;
    position: relative;
    overflow: hidden;
  }
  .trading-terminal::before {
    content: '';
    position: absolute; top: 0; left: 0; right: 0; height: 1px;
    background: linear-gradient(90deg, transparent, rgba(0,255,136,0.3), transparent);
  }
  .trading-terminal::after {
    content: '';
    position: absolute; bottom: 0; left: 0; right: 0; height: 1px;
    background: linear-gradient(90deg, transparent, rgba(0,255,136,0.3), transparent);
  }
  .live-rate-bar {
    display: flex; align-items: center; justify-content: center; gap: 20px;
    max-width: 1600px; margin: 0 auto;
  }
  .terminal-title {
    font-size: 10px; font-weight: 700; color: rgba(0,255,136,0.6);
    text-transform: uppercase; letter-spacing: 2px; margin-right: 8px;
    font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
  }
  .live-rate-dot {
    width: 7px; height: 7px; border-radius: 50%; background: #00FF88;
    display: inline-block; animation: blink 1.5s infinite;
    box-shadow: 0 0 6px rgba(0,255,136,0.6);
  }
  @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.2; } }
  .rate-pair-group {
    display: flex; align-items: center; gap: 8px;
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.06);
    border-radius: 8px; padding: 8px 14px;
  }
  .rate-pair-label {
    font-size: 11px; font-weight: 800; color: rgba(255,255,255,0.35);
    text-transform: uppercase; letter-spacing: 1px; writing-mode: vertical-rl;
    text-orientation: mixed; font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
  }
  .live-rate-btn {
    display: flex; flex-direction: column; align-items: center; gap: 1px;
    padding: 8px 18px; border-radius: 6px; border: 1px solid transparent;
    font-family: inherit; font-size: 14px; font-weight: 700; cursor: default;
    transition: all 0.25s; min-width: 150px; justify-content: center;
    position: relative;
  }
  .rate-flags { font-size: 9px; opacity: 0.5; letter-spacing: 1px; line-height: 1; }
  .live-rate-btn.compra {
    background: rgba(0,255,136,0.08); color: #00FF88;
    border-color: rgba(0,255,136,0.2);
  }
  .live-rate-btn.compra:hover { background: rgba(0,255,136,0.14); }
  .live-rate-btn.venda {
    background: rgba(255,68,68,0.08); color: #FF4444;
    border-color: rgba(255,68,68,0.2);
  }
  .live-rate-btn.venda:hover { background: rgba(255,68,68,0.14); }
  .live-rate-btn .rate-value {
    font-size: 22px; font-weight: 800; letter-spacing: -0.5px;
    font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
    font-variant-numeric: tabular-nums;
    text-shadow: 0 0 12px currentColor;
  }
  .live-rate-btn .rate-type {
    font-size: 9px; text-transform: uppercase; letter-spacing: 1.5px;
    opacity: 0.5; font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
  }
  .live-rate-btn.pulse .rate-value { animation: ratePulse 0.4s ease; }
  @keyframes ratePulse {
    0% { transform: scale(1); text-shadow: 0 0 12px currentColor; }
    50% { transform: scale(1.06); text-shadow: 0 0 24px currentColor, 0 0 48px currentColor; }
    100% { transform: scale(1); text-shadow: 0 0 12px currentColor; }
  }
  .rate-separator {
    width: 1px; height: 40px; background: rgba(255,255,255,0.08);
  }
  .live-rate-time {
    font-size: 10px; color: rgba(255,255,255,0.25);
    font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
    letter-spacing: 0.5px;
  }

  .container { padding: 28px 40px; max-width: 1600px; margin: 0 auto; }

  .kpi-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 28px; }
  .kpi-card {
    background: var(--card); border-radius: 12px; padding: 20px 22px;
    border: 1px solid var(--border); box-shadow: 0 1px 3px rgba(0,0,0,0.06);
    display: flex; align-items: flex-start; gap: 14px; transition: box-shadow 0.15s; overflow: hidden;
  }
  .kpi-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.08); }
  .kpi-icon {
    width: 44px; height: 44px; border-radius: 10px;
    display: flex; align-items: center; justify-content: center; font-size: 20px; flex-shrink: 0;
  }
  .kpi-icon.purple { background: var(--primary-bg); color: var(--primary); }
  .kpi-icon.green { background: var(--green-bg); color: var(--green); }
  .kpi-icon.blue { background: var(--blue-bg); color: var(--blue); }
  .kpi-icon.orange { background: var(--orange-bg); color: var(--orange); }
  .kpi-info { flex: 1; min-width: 0; }
  .kpi-card .kpi-label { font-size: 11px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; }
  .kpi-card .kpi-value { font-size: 20px; font-weight: 800; color: var(--text); line-height: 1.2; letter-spacing: -0.3px; word-break: break-word; font-variant-numeric: tabular-nums; }
  .kpi-card .kpi-sub { font-size: 11px; color: var(--text-muted); margin-top: 3px; font-weight: 400; }
  .kpi-card .kpi-change { font-size: 11px; margin-top: 4px; font-weight: 600; display: inline-block; padding: 2px 6px; border-radius: 4px; }
  .kpi-card .kpi-change.up { background: var(--green-bg); color: var(--green); }
  .kpi-card .kpi-change.down { background: #FEE2E2; color: #DC2626; }
  .kpi-card .kpi-change.neutral { background: #F3F4F6; color: var(--text-muted); }

  .charts-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 20px;
    margin-bottom: 28px;
  }
  .chart-card {
    background: var(--card);
    border-radius: 16px;
    padding: 24px;
    border: 1px solid var(--border);
    box-shadow: 0 2px 8px rgba(0,0,0,0.04), 0 1px 2px rgba(0,0,0,0.06);
    display: flex;
    flex-direction: column;
    min-height: 380px;
  }
  .chart-card h3 {
    font-size: 14px;
    font-weight: 700;
    margin-bottom: 20px;
    color: var(--text);
    padding-bottom: 12px;
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .chart-card h3::before {
    content: '';
    width: 4px;
    height: 16px;
    background: var(--primary);
    border-radius: 2px;
  }
  .chart-card .chart-container {
    flex: 1;
    position: relative;
    min-height: 280px;
  }
  .chart-card canvas {
    width: 100% !important;
    height: 100% !important;
    max-height: 320px;
  }

  .table-card { background: var(--card); border-radius: 12px; border: 1px solid var(--border); box-shadow: 0 1px 3px rgba(0,0,0,0.06); overflow: hidden; margin-bottom: 28px; }
  .table-card h3 { font-size: 15px; font-weight: 700; padding: 18px 22px; border-bottom: 1px solid var(--border); }
  .table-wrap { overflow-x: auto; }
  table { width: 100%; border-collapse: collapse; font-size: 13px; }
  thead th { background: #FAFBFC; padding: 11px 16px; text-align: left; font-weight: 600; color: var(--text-secondary); font-size: 11px; text-transform: uppercase; letter-spacing: 0.4px; border-bottom: 2px solid var(--border); white-space: nowrap; position: sticky; top: 0; }
  tbody td { padding: 11px 16px; border-bottom: 1px solid #F3F4F6; white-space: nowrap; font-variant-numeric: tabular-nums; }
  tbody tr:hover { background: #F8F5FF; }
  tbody tr:nth-child(even) { background: #FAFBFC; }
  tbody tr:nth-child(even):hover { background: #F8F5FF; }
  .num { text-align: right; }


  /* Portfolio Analysis Styles */
  .section-title {
    font-size: 18px; font-weight: 700; color: var(--text); margin: 32px 0 16px 0;
    padding-bottom: 8px; border-bottom: 2px solid var(--primary);
  }
  .portfolio-kpi-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 28px; }
  .client-table-card { background: var(--card); border-radius: 12px; border: 1px solid var(--border); box-shadow: 0 1px 3px rgba(0,0,0,0.06); overflow: hidden; margin-bottom: 28px; }
  .client-table-card h3 { font-size: 15px; font-weight: 700; padding: 18px 22px; border-bottom: 1px solid var(--border); display: flex; justify-content: space-between; align-items: center; }
  .client-table-card .table-info { font-size: 12px; font-weight: 400; color: var(--text-muted); }
  .client-table thead th { cursor: pointer; user-select: none; transition: background 0.15s; }
  .client-table thead th:hover { background: var(--primary-bg); }
  .client-table thead th.sorted-asc::after { content: ' \\25B2'; font-size: 10px; color: var(--primary); }
  .client-table thead th.sorted-desc::after { content: ' \\25BC'; font-size: 10px; color: var(--primary); }
  .recency-green { background: var(--green-bg) !important; color: var(--green); font-weight: 600; }
  .recency-yellow { background: var(--orange-bg) !important; color: var(--orange); font-weight: 600; }
  .recency-red { background: #FEE2E2 !important; color: #DC2626; font-weight: 600; }

  /* Spread Liquido Styles */
  .spread-high { background: var(--green-bg) !important; color: var(--green); font-weight: 600; }
  .spread-medium { background: var(--orange-bg) !important; color: var(--orange); font-weight: 600; }
  .spread-low { background: #FEE2E2 !important; color: #DC2626; font-weight: 600; }
  .spread-negative { background: #DC2626 !important; color: #FFFFFF; font-weight: 700; }

  /* Netting Chart Styles */
  .netting-section { margin-top: 32px; }
  .netting-kpi-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 20px; }
  .netting-kpi { background: var(--card); border-radius: 12px; padding: 18px 22px; border: 1px solid var(--border); text-align: center; }
  .netting-kpi .label { font-size: 11px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; margin-bottom: 6px; }
  .netting-kpi .value { font-size: 22px; font-weight: 800; }
  .netting-kpi .value.positive { color: var(--green); }
  .netting-kpi .value.negative { color: #DC2626; }
  .netting-kpi .value.neutral { color: var(--blue); }
  .netting-kpi .sub { font-size: 11px; color: var(--text-muted); margin-top: 4px; }

  /* Additional Charts Grid */
  .charts-grid-3 {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    margin-bottom: 28px;
  }
  .chart-card.full-width {
    grid-column: span 2;
  }
  .chart-card.half-width {
    grid-column: span 1;
  }

  /* Section Headers */
  .section-header {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 36px 0 20px 0;
    padding-bottom: 12px;
    border-bottom: 2px solid var(--primary);
  }
  .section-header h2 {
    font-size: 18px;
    font-weight: 800;
    color: var(--text);
    margin: 0;
  }
  .section-header .section-badge {
    background: var(--primary-bg);
    color: var(--primary);
    font-size: 11px;
    font-weight: 600;
    padding: 4px 10px;
    border-radius: 12px;
  }

  /* Responsive */
  @media (max-width: 1400px) {
    .charts-grid-3 { grid-template-columns: repeat(2, 1fr); }
    .chart-card.full-width { grid-column: span 2; }
  }
  @media (max-width: 1100px) {
    .kpi-grid { grid-template-columns: repeat(2, 1fr); }
    .portfolio-kpi-grid { grid-template-columns: repeat(2, 1fr); }
    .netting-kpi-grid { grid-template-columns: repeat(2, 1fr); }
  }
  @media (max-width: 900px) {
    .trading-terminal { padding: 12px 16px; }
    .live-rate-bar { flex-wrap: wrap; gap: 12px; }
    .terminal-title { display: none; }
    .rate-pair-group { padding: 6px 10px; }
    .live-rate-btn { min-width: 100px; padding: 6px 12px; }
    .live-rate-btn .rate-value { font-size: 16px; }
    .rate-separator { display: none; }
    .charts-grid { grid-template-columns: 1fr; }
    .charts-grid-3 { grid-template-columns: 1fr; }
    .chart-card.full-width { grid-column: span 1; }
    .chart-card { min-height: 320px; }
    .container { padding: 20px; }
    .filters-inner { padding: 12px 20px; }
    .header-inner { padding: 16px 20px; flex-direction: column; gap: 12px; }
  }
  @media (max-width: 600px) {
    .trading-terminal { padding: 10px 12px; }
    .live-rate-bar { gap: 8px; }
    .rate-pair-label { font-size: 9px; }
    .live-rate-btn { min-width: 80px; padding: 5px 8px; }
    .live-rate-btn .rate-value { font-size: 14px; }
    .live-rate-btn .rate-type { font-size: 7px; }
    .rate-flags { font-size: 8px; }
    .live-rate-time { font-size: 8px; }
    .kpi-grid { grid-template-columns: 1fr; }
    .portfolio-kpi-grid { grid-template-columns: 1fr; }
    .netting-kpi-grid { grid-template-columns: 1fr; }
  }

  /* Dark Mode overrides */
  [data-theme="dark"] thead th { background: var(--bg); }
  [data-theme="dark"] tbody td { border-bottom-color: var(--border); }
  [data-theme="dark"] tbody tr:hover { background: rgba(255,255,255,0.04); }
  [data-theme="dark"] tbody tr:nth-child(even) { background: rgba(255,255,255,0.02); }
  [data-theme="dark"] tbody tr:nth-child(even):hover { background: rgba(255,255,255,0.04); }
  [data-theme="dark"] .kpi-card .kpi-change.neutral { background: var(--border); }
  [data-theme="dark"] .kpi-card .kpi-change.down { background: rgba(248,81,73,0.15); color: var(--red); }
  [data-theme="dark"] .filters { background: var(--card); border-bottom-color: var(--border); }
  [data-theme="dark"] .filters input,
  [data-theme="dark"] .filters select { background: var(--bg); color: var(--text); border-color: var(--border); }
  [data-theme="dark"] .btn-export { background: var(--green); }
  [data-theme="dark"] .recency-red { background: rgba(248,81,73,0.15) !important; color: var(--red); }
  [data-theme="dark"] .spread-low { background: rgba(248,81,73,0.15) !important; color: var(--red); }
  [data-theme="dark"] .spread-negative { background: var(--red) !important; }
`;

  return `<!DOCTYPE html>
<html lang="pt-BR">
<head>
${buildHead('Dashboard', dashboardCSS, pageScripts)}
</head>
<body>

${isEmulating ? `
<div class="emulation-banner">
  <span>Modo Emulacao - Visualizando como: ${agente.nome}</span>
  <a href="${backUrl}">Voltar ao BI - CCC</a>
</div>
` : ''}
${buildHeader({ role, userName: agente.nome, activePage: 'dashboard', showNav: !isEmulating, permissions: agente.permissions || [] })}

<div class="filters">
  <div class="filters-inner">
    <div class="filter-group"><label>De:</label><input type="date" id="filterStart"></div>
    <div class="filter-group"><label>Ate:</label><input type="date" id="filterEnd"></div>
    <div class="filter-group">
      <label>Granulacao:</label>
      <select id="filterGran">
        <option value="dia">Dia</option>
        <option value="mes" selected>Mes</option>
        <option value="ano">Ano</option>
      </select>
    </div>
    <div class="filter-group">
      <label>Fluxo:</label>
      <select id="filterFluxo">
        <option value="">Todos</option>
        <option value="BRL \\u2192 USD">BRL &rarr; USD</option>
        <option value="USD \\u2192 BRL">USD &rarr; BRL</option>
      </select>
    </div>
    <div class="filter-group">
      <label>Cliente:</label>
      <select id="filterCliente"><option value="">Todos</option></select>
    </div>
    <button class="btn-apply" onclick="applyFilters()">Aplicar</button>
    <button class="btn-export" onclick="exportCSV()">&#x2B07; Exportar CSV</button>
  </div>
</div>

<div class="trading-terminal">
  <div class="live-rate-bar">
    <span class="live-rate-dot"></span>
    <span class="terminal-title">Live Rates</span>

    <div class="rate-pair-group">
      <span class="rate-pair-label">USD</span>
      <button class="live-rate-btn compra">
        <span class="rate-flags">\uD83C\uDDFA\uD83C\uDDF8 \u2192 \uD83C\uDDE7\uD83C\uDDF7</span>
        <span class="rate-type">Compra</span>
        <span class="rate-value" id="rateUsdCompra">--</span>
      </button>
      <button class="live-rate-btn venda">
        <span class="rate-flags">\uD83C\uDDE7\uD83C\uDDF7 \u2192 \uD83C\uDDFA\uD83C\uDDF8</span>
        <span class="rate-type">Venda</span>
        <span class="rate-value" id="rateUsdVenda">--</span>
      </button>
    </div>

    <div class="rate-separator"></div>

    <div class="rate-pair-group">
      <span class="rate-pair-label">EUR</span>
      <button class="live-rate-btn compra">
        <span class="rate-flags">\uD83C\uDDEA\uD83C\uDDFA \u2192 \uD83C\uDDE7\uD83C\uDDF7</span>
        <span class="rate-type">Compra</span>
        <span class="rate-value" id="rateEurCompra">--</span>
      </button>
      <button class="live-rate-btn venda">
        <span class="rate-flags">\uD83C\uDDE7\uD83C\uDDF7 \u2192 \uD83C\uDDEA\uD83C\uDDFA</span>
        <span class="rate-type">Venda</span>
        <span class="rate-value" id="rateEurVenda">--</span>
      </button>
    </div>

    <span class="live-rate-time" id="rateTime">--</span>
  </div>
</div>

<div class="container">
  <div class="kpi-grid" id="kpiGrid"></div>

  <!-- Volume Charts Section -->
  <div class="section-header">
    <h2>Volume e Taxas</h2>
    <span class="section-badge">Principais</span>
  </div>
  <div class="charts-grid">
    <div class="chart-card">
      <h3>Volume BRL / USD por Periodo</h3>
      <div class="chart-container"><canvas id="chartVolume"></canvas></div>
    </div>
    <div class="chart-card">
      <h3>Volume por Cliente (Top 10)</h3>
      <div class="chart-container"><canvas id="chartClientes"></canvas></div>
    </div>
  </div>
  <div class="charts-grid">
    <div class="chart-card">
      <h3>Taxa Cobrada vs PTAX</h3>
      <div class="chart-container"><canvas id="chartTaxas"></canvas></div>
    </div>
    <div class="chart-card">
      <h3>Evolucao de Clientes Ativos</h3>
      <div class="chart-container"><canvas id="chartClientesAtivos"></canvas></div>
    </div>
  </div>

  <!-- Portfolio Analysis Charts -->
  <div class="section-header">
    <h2>Analise de Carteira</h2>
    <span class="section-badge">Portfolio</span>
  </div>
  <div class="charts-grid">
    <div class="chart-card">
      <h3>Distribuicao de Clientes por Volume</h3>
      <div class="chart-container"><canvas id="chartDistribuicao"></canvas></div>
    </div>
    <div class="chart-card">
      <h3>Curva de Pareto (80/20)</h3>
      <div class="chart-container"><canvas id="chartPareto"></canvas></div>
    </div>
  </div>

  <!-- Netting / Balance In-Out Section -->
  <div class="section-header">
    <h2>Balance In/Out - Eficiencia IOF</h2>
    <span class="section-badge">Netting</span>
  </div>
  <div class="netting-kpi-grid" id="nettingKpis"></div>
  <div class="charts-grid">
    <div class="chart-card">
      <h3>Fluxo: Entrada vs Saida (USD)</h3>
      <div class="chart-container"><canvas id="chartNetting"></canvas></div>
    </div>
    <div class="chart-card">
      <h3>Eficiencia de Netting por Periodo</h3>
      <div class="chart-container"><canvas id="chartEficiencia"></canvas></div>
    </div>
  </div>

  <div class="table-card">
    <h3 id="tableTitle">Transacoes</h3>
    <div class="table-wrap">
      <table><thead id="tableHead"></thead><tbody id="tableBody"></tbody></table>
    </div>
  </div>

  <!-- Portfolio Analysis Section -->
  <h2 class="section-title">Analise de Carteira</h2>
  <div class="portfolio-kpi-grid" id="portfolioKpiGrid"></div>
  <div class="client-table-card">
    <h3>Metricas por Cliente <span class="table-info" id="clientTableInfo"></span></h3>
    <div class="table-wrap">
      <table class="client-table"><thead id="clientTableHead"></thead><tbody id="clientTableBody"></tbody></table>
    </div>
  </div>
</div>

${buildFooter()}

<script>
let RAW_DATA = ${asyncLoad ? '[]' : JSON.stringify(data)};
const ASYNC_LOAD = ${asyncLoad};
const DIAS_PERIODO = ${diasPeriodo || 90};

let filtered = [];
let charts = {};

// Portfolio Analysis Variables (must be declared before init)
let clientMetrics = [];
let clientSortColumn = 'volumeTotal';
let clientSortDirection = 'desc';

const fmtBRL = v => v.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' });
const fmtUSD = v => v.toLocaleString('pt-BR', { style: 'currency', currency: 'USD' });
const fmtNum = (v, d=2) => v.toLocaleString('pt-BR', { minimumFractionDigits: d, maximumFractionDigits: d });
const fmtPct = v => fmtNum(v, 2) + '%';
const fmtPct4 = v => fmtNum(v, 4) + '%'; // 4 decimal places for spread

// Loading overlay
function showLoading(msg) {
  let overlay = document.getElementById('loadingOverlay');
  if (!overlay) {
    overlay = document.createElement('div');
    overlay.id = 'loadingOverlay';
    overlay.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.7);display:flex;align-items:center;justify-content:center;z-index:9999;flex-direction:column;gap:16px;';
    overlay.innerHTML = '<div style="width:50px;height:50px;border:4px solid #fff;border-top-color:#7600be;border-radius:50%;animation:spin 1s linear infinite;"></div><div style="color:#fff;font-size:16px;" id="loadingText">Carregando...</div><style>@keyframes spin{to{transform:rotate(360deg)}}</style>';
    document.body.appendChild(overlay);
  }
  document.getElementById('loadingText').textContent = msg || 'Carregando...';
  overlay.style.display = 'flex';
}
function hideLoading() {
  const overlay = document.getElementById('loadingOverlay');
  if (overlay) overlay.style.display = 'none';
}

// Async data loading
async function loadDataAsync() {
  showLoading('Carregando dados (' + DIAS_PERIODO + ' dias)...');
  try {
    const resp = await fetch('/admin/api/data?dias=' + DIAS_PERIODO);
    const json = await resp.json();
    if (json.success) {
      RAW_DATA = json.data;
      document.querySelector('.subtitle').textContent = 'Dashboard de Transacoes BRL ↔ USD (' + json.count.toLocaleString('pt-BR') + ' registros)';
      initDashboard();
    } else {
      alert('Erro ao carregar dados: ' + json.error);
    }
  } catch (err) {
    alert('Erro de conexao: ' + err.message);
  }
  hideLoading();
}

// Live USD/BRL + EUR/BRL Rate
let _lastUsdBid = null, _lastUsdAsk = null, _lastEurBid = null, _lastEurAsk = null;
function pulseEl(el) { el.closest('.live-rate-btn').classList.add('pulse'); setTimeout(() => el.closest('.live-rate-btn').classList.remove('pulse'), 500); }
async function fetchLiveRate() {
  try {
    const resp = await fetch('/api/cotacao');
    const json = await resp.json();
    const usd = json.USDBRL;
    const eur = json.EURBRL;
    if (usd) {
      const bidRaw = parseFloat(usd.bid), askRaw = parseFloat(usd.ask);
      const bid = bidRaw * (1 - 0.0043);  // compra: -0.38% -5bps = -0.43%
      const ask = askRaw * (1 + 0.0005);  // venda: +5bps = +0.05%
      const elC = document.getElementById('rateUsdCompra'), elV = document.getElementById('rateUsdVenda');
      elC.textContent = bid.toFixed(4); elV.textContent = ask.toFixed(4);
      if (_lastUsdBid !== null && bidRaw !== _lastUsdBid) pulseEl(elC);
      if (_lastUsdAsk !== null && askRaw !== _lastUsdAsk) pulseEl(elV);
      _lastUsdBid = bidRaw; _lastUsdAsk = askRaw;
    }
    if (eur) {
      const bid = parseFloat(eur.bid), ask = parseFloat(eur.ask);
      const elC = document.getElementById('rateEurCompra'), elV = document.getElementById('rateEurVenda');
      elC.textContent = bid.toFixed(4); elV.textContent = ask.toFixed(4);
      if (_lastEurBid !== null && bid !== _lastEurBid) pulseEl(elC);
      if (_lastEurAsk !== null && ask !== _lastEurAsk) pulseEl(elV);
      _lastEurBid = bid; _lastEurAsk = ask;
    }
    document.getElementById('rateTime').textContent = new Date().toLocaleTimeString('pt-BR');
  } catch (e) { /* silently retry next cycle */ }
}
function startLiveRate() { fetchLiveRate(); setInterval(fetchLiveRate, 3000); }

function initDashboard() {
  const clientes = [...new Set(RAW_DATA.map(r => r.cliente))].sort();
  const sel = document.getElementById('filterCliente');
  sel.innerHTML = '<option value="">Todos</option>';
  clientes.forEach(c => { const o = document.createElement('option'); o.value = c; o.textContent = c; sel.appendChild(o); });

  const dates = RAW_DATA.map(r => r.data_operacao ? r.data_operacao.slice(0, 10) : null).filter(Boolean).sort();
  if (dates.length) {
    document.getElementById('filterStart').value = dates[0];
    document.getElementById('filterEnd').value = dates[dates.length - 1];
  }
  applyFilters();
  startLiveRate();
}

(function init() {
  if (ASYNC_LOAD) {
    loadDataAsync();
    return;
  }
  const clientes = [...new Set(RAW_DATA.map(r => r.cliente))].sort();
  const sel = document.getElementById('filterCliente');
  clientes.forEach(c => { const o = document.createElement('option'); o.value = c; o.textContent = c; sel.appendChild(o); });

  const dates = RAW_DATA.map(r => r.data_operacao ? r.data_operacao.slice(0, 10) : null).filter(Boolean).sort();
  if (dates.length) {
    document.getElementById('filterStart').value = dates[0];
    document.getElementById('filterEnd').value = dates[dates.length - 1];
  }
  applyFilters();
  startLiveRate();
})();

function applyFilters() {
  const start = document.getElementById('filterStart').value;
  const end = document.getElementById('filterEnd').value;
  const gran = document.getElementById('filterGran').value;
  const cliente = document.getElementById('filterCliente').value;
  const fluxo = document.getElementById('filterFluxo').value;

  filtered = RAW_DATA.filter(r => {
    const dateOnly = r.data_operacao ? r.data_operacao.slice(0, 10) : null;
    if (start && dateOnly && dateOnly < start) return false;
    if (end && dateOnly && dateOnly > end) return false;
    if (cliente && r.cliente !== cliente) return false;
    if (fluxo && r.fluxo !== fluxo) return false;
    return true;
  });

  renderKPIs();
  try { renderCharts(gran); } catch(e) { console.error('Chart error:', e); }
  renderTable();
  renderPortfolioAnalysis();
  try { renderPortfolioCharts(gran); } catch(e) { console.error('Portfolio chart error:', e); }
  try { renderNettingAnalysis(gran); } catch(e) { console.error('Netting chart error:', e); }
}

function calcPreviousPeriod(start, end) {
  if (!start || !end) return { start: null, end: null };
  const s = new Date(start);
  const e = new Date(end);
  const diffDays = Math.ceil((e - s) / (1000 * 60 * 60 * 24)) + 1;
  const prevEnd = new Date(s);
  prevEnd.setDate(prevEnd.getDate() - 1);
  const prevStart = new Date(prevEnd);
  prevStart.setDate(prevStart.getDate() - diffDays + 1);
  return {
    start: prevStart.toISOString().slice(0, 10),
    end: prevEnd.toISOString().slice(0, 10)
  };
}

function calcKPIsForData(data) {
  const n = data.length;
  const totalBRL = data.reduce((s, r) => s + r.valor_reais, 0);
  const totalUSD = data.reduce((s, r) => s + r.valor_dolar, 0);
  const taxaMedia = n && totalUSD ? data.reduce((s, r) => s + r.taxa_cobrada * r.valor_dolar, 0) / totalUSD : 0;
  const spreadMedio = n ? data.reduce((s, r) => s + r.spread_pct, 0) / n : 0;
  const iofTotal = data.reduce((s, r) => s + r.iof_valor_rs, 0);
  const ticketMedio = n ? totalUSD / n : 0;
  const clientes = new Set(data.map(r => r.cliente)).size;
  return { n, totalBRL, totalUSD, taxaMedia, spreadMedio, iofTotal, ticketMedio, clientes };
}

function fmtChange(current, previous, invert = false) {
  if (!previous || previous === 0) return '';
  const pct = ((current - previous) / previous) * 100;
  if (Math.abs(pct) < 0.5) return '<span class="kpi-change neutral">~0%</span>';
  const isUp = invert ? pct < 0 : pct > 0;
  const cls = isUp ? 'up' : 'down';
  const arrow = pct > 0 ? '&#x25B2;' : '&#x25BC;';
  return '<span class="kpi-change ' + cls + '">' + arrow + ' ' + Math.abs(pct).toFixed(1) + '%</span>';
}

function renderKPIs() {
  const start = document.getElementById('filterStart').value;
  const end = document.getElementById('filterEnd').value;
  const cliente = document.getElementById('filterCliente').value;
  const fluxo = document.getElementById('filterFluxo').value;

  const curr = calcKPIsForData(filtered);

  const prev = calcPreviousPeriod(start, end);
  let prevData = [];
  if (prev.start && prev.end) {
    prevData = RAW_DATA.filter(r => {
      const dateOnly = r.data_operacao ? r.data_operacao.slice(0, 10) : null;
      if (!dateOnly || dateOnly < prev.start || dateOnly > prev.end) return false;
      if (cliente && r.cliente !== cliente) return false;
      if (fluxo && r.fluxo !== fluxo) return false;
      return true;
    });
  }
  const prevKPIs = calcKPIsForData(prevData);
  const hasPrev = prevData.length > 0;

  document.getElementById('kpiGrid').innerHTML = \`
    <div class="kpi-card"><div class="kpi-icon purple">&#x2194;</div><div class="kpi-info"><div class="kpi-label">Transacoes</div><div class="kpi-value">\${curr.n}</div><div class="kpi-sub">operacoes</div>\${hasPrev ? fmtChange(curr.n, prevKPIs.n) : ''}</div></div>
    <div class="kpi-card"><div class="kpi-icon green">R$</div><div class="kpi-info"><div class="kpi-label">Volume BRL</div><div class="kpi-value">\${fmtBRL(curr.totalBRL)}</div><div class="kpi-sub">total movimentado</div>\${hasPrev ? fmtChange(curr.totalBRL, prevKPIs.totalBRL) : ''}</div></div>
    <div class="kpi-card"><div class="kpi-icon blue">US$</div><div class="kpi-info"><div class="kpi-label">Volume USD</div><div class="kpi-value">\${fmtUSD(curr.totalUSD)}</div><div class="kpi-sub">total movimentado</div>\${hasPrev ? fmtChange(curr.totalUSD, prevKPIs.totalUSD) : ''}</div></div>
    <div class="kpi-card"><div class="kpi-icon orange">&#x2195;</div><div class="kpi-info"><div class="kpi-label">Taxa Media</div><div class="kpi-value">\${fmtNum(curr.taxaMedia, 4)}</div><div class="kpi-sub">ponderada BRL/USD</div>\${hasPrev ? fmtChange(curr.taxaMedia, prevKPIs.taxaMedia) : ''}</div></div>
    <div class="kpi-card"><div class="kpi-icon purple">%</div><div class="kpi-info"><div class="kpi-label">Spread Medio</div><div class="kpi-value">\${fmtPct(curr.spreadMedio)}</div><div class="kpi-sub">sobre taxa cobrada</div>\${hasPrev ? fmtChange(curr.spreadMedio, prevKPIs.spreadMedio) : ''}</div></div>
    <div class="kpi-card"><div class="kpi-icon green">&#x00A7;</div><div class="kpi-info"><div class="kpi-label">IOF Total</div><div class="kpi-value">\${fmtBRL(curr.iofTotal)}</div><div class="kpi-sub">recolhido no periodo</div>\${hasPrev ? fmtChange(curr.iofTotal, prevKPIs.iofTotal) : ''}</div></div>
    <div class="kpi-card"><div class="kpi-icon blue">&#x00D8;</div><div class="kpi-info"><div class="kpi-label">Ticket Medio</div><div class="kpi-value">\${fmtUSD(curr.ticketMedio)}</div><div class="kpi-sub">por operacao</div>\${hasPrev ? fmtChange(curr.ticketMedio, prevKPIs.ticketMedio) : ''}</div></div>
    <div class="kpi-card"><div class="kpi-icon orange">&#x263A;</div><div class="kpi-info"><div class="kpi-label">Clientes Ativos</div><div class="kpi-value">\${curr.clientes}</div><div class="kpi-sub">no periodo</div>\${hasPrev ? fmtChange(curr.clientes, prevKPIs.clientes) : ''}</div></div>
  \`;
}

function groupByPeriod(gran) {
  const map = {};
  filtered.forEach(r => {
    if (!r.data_operacao) return;
    const dateOnly = r.data_operacao.slice(0, 10);
    let key;
    if (gran === 'dia') key = dateOnly;
    else if (gran === 'mes') key = dateOnly.slice(0, 7);
    else key = dateOnly.slice(0, 4);
    if (!map[key]) map[key] = { totalUSD: 0, totalBRL: 0, count: 0, sumWeightTaxa: 0, sumWeightPtax: 0 };
    map[key].totalUSD += r.valor_dolar;
    map[key].totalBRL += r.valor_reais;
    map[key].count += 1;
    map[key].sumWeightTaxa += r.taxa_cobrada * r.valor_dolar;
    map[key].sumWeightPtax += r.taxa_ptax * r.valor_dolar;
  });
  const keys = Object.keys(map).sort();
  return keys.map(k => ({
    label: gran === 'dia' ? k.split('-').reverse().join('/') : gran === 'mes' ? k.split('-').reverse().join('/') : k,
    ...map[k],
    taxaMedia: map[k].totalUSD ? map[k].sumWeightTaxa / map[k].totalUSD : 0,
    ptaxMedia: map[k].totalUSD ? map[k].sumWeightPtax / map[k].totalUSD : 0,
  }));
}

function destroyCharts() { Object.values(charts).forEach(c => c.destroy()); charts = {}; }

function renderCharts(gran) {
  destroyCharts();
  const periods = groupByPeriod(gran);
  const labels = periods.map(p => p.label);

  const chartDefaults = {
    responsive: true,
    maintainAspectRatio: false,
    plugins: {
      legend: { position: 'top', labels: { font: { size: 11, family: 'Inter' }, padding: 16, usePointStyle: true, pointStyle: 'circle' } },
      tooltip: { backgroundColor: 'rgba(0,0,0,0.8)', titleFont: { size: 12 }, bodyFont: { size: 11 }, padding: 12, cornerRadius: 8 }
    }
  };

  charts.volume = new Chart(document.getElementById('chartVolume'), {
    type: 'bar',
    data: {
      labels,
      datasets: [
        { label: 'Volume BRL', data: periods.map(p => p.totalBRL), backgroundColor: 'rgba(30,142,62,0.75)', borderRadius: 6, yAxisID: 'yBRL' },
        { label: 'Volume USD', data: periods.map(p => p.totalUSD), backgroundColor: 'rgba(26,115,232,0.75)', borderRadius: 6, yAxisID: 'yUSD' }
      ]
    },
    options: {
      ...chartDefaults,
      scales: {
        x: { grid: { display: false }, ticks: { font: { size: 10 } } },
        yBRL: { type: 'linear', position: 'left', ticks: { callback: v => 'R$ ' + (v >= 1000000 ? (v/1000000).toFixed(1) + 'M' : v >= 1000 ? (v/1000).toFixed(0) + 'k' : v.toFixed(0)), font: { size: 10 } }, title: { display: true, text: 'BRL', font: { size: 11 } }, grid: { color: 'rgba(0,0,0,0.06)' } },
        yUSD: { type: 'linear', position: 'right', ticks: { callback: v => '$ ' + (v >= 1000000 ? (v/1000000).toFixed(1) + 'M' : v >= 1000 ? (v/1000).toFixed(0) + 'k' : v.toFixed(0)), font: { size: 10 } }, title: { display: true, text: 'USD', font: { size: 11 } }, grid: { display: false } }
      }
    }
  });

  const clientMap = {};
  filtered.forEach(r => { clientMap[r.cliente] = (clientMap[r.cliente] || 0) + r.valor_dolar; });
  const topClientes = Object.entries(clientMap).sort((a,b) => b[1] - a[1]).slice(0, 10);
  const colors = ['#7600be','#8B5FBF','#2980B9','#27AE60','#E67E22','#E74C3C','#3498DB','#1ABC9C','#9B59B6','#F39C12'];
  charts.clientes = new Chart(document.getElementById('chartClientes'), {
    type: 'bar',
    data: {
      labels: topClientes.map(c => c[0].length > 18 ? c[0].slice(0,18) + '...' : c[0]),
      datasets: [{ label: 'Volume USD', data: topClientes.map(c => c[1]), backgroundColor: colors, borderRadius: 6, borderSkipped: false }]
    },
    options: {
      ...chartDefaults,
      indexAxis: 'y',
      plugins: { ...chartDefaults.plugins, legend: { display: false } },
      scales: {
        x: { grid: { color: 'rgba(0,0,0,0.06)' }, ticks: { callback: v => '$ ' + (v >= 1000000 ? (v/1000000).toFixed(1) + 'M' : v >= 1000 ? (v/1000).toFixed(0) + 'k' : v.toFixed(0)), font: { size: 10 } } },
        y: { grid: { display: false }, ticks: { font: { size: 11 } } }
      }
    }
  });

  charts.taxas = new Chart(document.getElementById('chartTaxas'), {
    type: 'line',
    data: {
      labels,
      datasets: [
        { label: 'Taxa Cobrada', data: periods.map(p => p.taxaMedia), borderColor: '#7600be', backgroundColor: 'rgba(108,63,160,0.08)', fill: true, tension: 0.4, pointRadius: 4, pointBackgroundColor: '#7600be', borderWidth: 2 },
        { label: 'PTAX', data: periods.map(p => p.ptaxMedia), borderColor: '#2980B9', backgroundColor: 'rgba(41,128,185,0.08)', fill: true, tension: 0.4, pointRadius: 4, pointBackgroundColor: '#2980B9', borderWidth: 2 }
      ]
    },
    options: {
      ...chartDefaults,
      scales: {
        x: { grid: { display: false }, ticks: { font: { size: 10 } } },
        y: { grid: { color: 'rgba(0,0,0,0.06)' }, ticks: { callback: v => v.toFixed(4), font: { size: 10 } } }
      }
    }
  });
}

function fmtDate(d) {
  if (!d) return '-';
  const parts = d.split(' ');
  const ymd = parts[0].split('-').reverse().join('/');
  return parts[1] ? ymd + ' ' + parts[1] : ymd;
}

function exportCSV() {
  if (!filtered.length) { alert('Nenhuma transacao para exportar.'); return; }

  const headers = ['Fluxo','Data/Hora','Cliente','Valor BRL','Valor USD','IOF %','IOF R$','PTAX','Taxa Cobrada','Spread','Spread %','Spread Liq %','Status'];
  const rows = filtered.map(r => {
    const spreadLiq = r.spread_pct - 0.20;
    return [
      r.fluxo,
      r.data_operacao || '',
      '"' + (r.cliente || '').replace(/"/g, '""') + '"',
      r.valor_reais.toFixed(2),
      r.valor_dolar.toFixed(2),
      r.iof_pct.toFixed(2),
      r.iof_valor_rs.toFixed(2),
      r.taxa_ptax.toFixed(4),
      r.taxa_cobrada.toFixed(4),
      r.spread_bruto.toFixed(4),
      r.spread_pct.toFixed(4),
      spreadLiq.toFixed(4),
      r.status || ''
    ].join(';');
  });

  const bom = '\\uFEFF';
  const csv = bom + headers.join(';') + '\\n' + rows.join('\\n');
  const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  const today = new Date().toISOString().slice(0,10);
  a.download = 'transacoes_' + today + '.csv';
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

function calcSpreadLiquido(spreadPct) {
  // Spread Líquido = Spread Cobrado - 0.20% (custo operacional)
  const CUSTO_OPERACIONAL = 0.20;
  return spreadPct - CUSTO_OPERACIONAL;
}

function getSpreadLiqClass(spreadLiq) {
  if (spreadLiq < 0) return 'spread-negative';
  if (spreadLiq >= 0.5) return 'spread-high';
  if (spreadLiq >= 0.2) return 'spread-medium';
  return 'spread-low';
}

function renderTable() {
  // Filtrar apenas transações finalizadas
  const brlUsd = filtered.filter(r => r.fluxo === 'BRL \\u2192 USD' && r.status === 'finalizado');
  const usdBrl = filtered.filter(r => r.fluxo === 'USD \\u2192 BRL' && r.status && r.status !== '0000-00-00');

  document.getElementById('tableTitle').textContent = 'Transacoes (' + filtered.length + ')';

  let html = '';
  if (brlUsd.length) {
    html += '<tr><td colspan="12" style="background:var(--primary-bg);font-weight:700;padding:10px 16px;color:var(--primary);font-size:13px;">BRL \\u2192 USD (' + brlUsd.length + ')</td></tr>';
    html += brlUsd.map(r => {
      const spreadLiq = calcSpreadLiquido(r.spread_pct);
      return \`<tr>
      <td>\${fmtDate(r.data_operacao)}</td><td>\${r.cliente}</td>
      <td class="num">\${fmtBRL(r.valor_reais)}</td><td class="num">\${fmtUSD(r.valor_dolar)}</td>
      <td class="num">\${fmtPct(r.iof_pct)}</td><td class="num">\${fmtBRL(r.iof_valor_rs)}</td>
      <td class="num">\${fmtNum(r.taxa_ptax, 4)}</td><td class="num">\${fmtNum(r.taxa_cobrada, 4)}</td>
      <td class="num">\${fmtNum(r.spread_bruto, 4)}</td><td class="num">\${fmtPct4(r.spread_pct)}</td>
      <td class="num \${getSpreadLiqClass(spreadLiq)}">\${fmtPct4(spreadLiq)}</td>
      <td>\${r.status || '-'}</td>
    </tr>\`;
    }).join('');
  }
  if (usdBrl.length) {
    html += '<tr><td colspan="12" style="background:var(--blue-bg);font-weight:700;padding:10px 16px;color:var(--blue);font-size:13px;">USD \\u2192 BRL (' + usdBrl.length + ')</td></tr>';
    html += usdBrl.map(r => {
      const spreadLiq = calcSpreadLiquido(r.spread_pct);
      return \`<tr>
      <td>\${fmtDate(r.data_operacao)}</td><td>\${r.cliente}</td>
      <td class="num">\${fmtBRL(r.valor_reais)}</td><td class="num">\${fmtUSD(r.valor_dolar)}</td>
      <td class="num">\${fmtPct(r.iof_pct)}</td><td class="num">\${fmtBRL(r.iof_valor_rs)}</td>
      <td class="num">\${fmtNum(r.taxa_ptax, 4)}</td><td class="num">\${fmtNum(r.taxa_cobrada, 4)}</td>
      <td class="num">\${fmtNum(r.spread_bruto, 4)}</td><td class="num">\${fmtPct4(r.spread_pct)}</td>
      <td class="num \${getSpreadLiqClass(spreadLiq)}">\${fmtPct4(spreadLiq)}</td>
      <td>\${r.status || '-'}</td>
    </tr>\`;
    }).join('');
  }

  document.getElementById('tableHead').innerHTML = '<tr><th>Data/Hora</th><th>Cliente</th><th>Valor BRL</th><th>Valor USD</th><th>IOF %</th><th>IOF R$</th><th>PTAX</th><th>Taxa Cobrada</th><th>Spread</th><th>Spread %</th><th>Spread Liq %</th><th>Status</th></tr>';
  document.getElementById('tableBody').innerHTML = html;
}

// Portfolio Analysis Functions

function calcClientMetrics() {
  const today = new Date();
  const clientMap = {};

  // Calculate metrics for each client using ALL historical data (RAW_DATA)
  RAW_DATA.forEach(r => {
    if (!clientMap[r.cliente]) {
      clientMap[r.cliente] = {
        cliente: r.cliente,
        transactions: [],
        volumeTotal: 0,
        totalOperacoes: 0
      };
    }
    clientMap[r.cliente].transactions.push(r);
    clientMap[r.cliente].volumeTotal += r.valor_dolar;
    clientMap[r.cliente].totalOperacoes += 1;
  });

  // Calculate derived metrics
  return Object.values(clientMap).map(c => {
    const dates = c.transactions
      .map(t => t.data_operacao ? new Date(t.data_operacao.replace(' ', 'T')) : null)
      .filter(d => d && !isNaN(d.getTime()))
      .sort((a, b) => b - a);

    const lastDate = dates[0];
    const firstDate = dates[dates.length - 1];

    // Recency: days since last transaction
    const recencia = lastDate ? Math.floor((today - lastDate) / (1000 * 60 * 60 * 24)) : 999;

    // Frequency: average transactions per month
    let frequencia = 0;
    if (firstDate && lastDate && c.totalOperacoes > 0) {
      const monthsDiff = Math.max(1, (lastDate - firstDate) / (1000 * 60 * 60 * 24 * 30));
      frequencia = c.totalOperacoes / monthsDiff;
    }

    // LTV: Total USD volume historically
    const ltv = c.volumeTotal;

    // Ticket Medio: Average USD per transaction
    const ticketMedio = c.totalOperacoes > 0 ? c.volumeTotal / c.totalOperacoes : 0;

    return {
      cliente: c.cliente,
      ltv,
      recencia,
      frequencia,
      ticketMedio,
      totalOperacoes: c.totalOperacoes,
      volumeTotal: c.volumeTotal,
      lastDate
    };
  });
}

function calcPortfolioKPIs(metrics) {
  metrics = metrics || [];
  const start = document.getElementById('filterStart').value;
  const end = document.getElementById('filterEnd').value;

  // Get current period clients
  const currentClients = new Set(filtered.map(r => r.cliente));

  // Calculate previous period
  const prev = calcPreviousPeriod(start, end);
  let prevClients = new Set();
  if (prev.start && prev.end) {
    const prevData = RAW_DATA.filter(r => {
      const dateOnly = r.data_operacao ? r.data_operacao.slice(0, 10) : null;
      return dateOnly && dateOnly >= prev.start && dateOnly <= prev.end;
    });
    prevClients = new Set(prevData.map(r => r.cliente));
  }

  // Top 10 concentration: % of volume from top 10 clients
  const clientVolumes = {};
  filtered.forEach(r => {
    clientVolumes[r.cliente] = (clientVolumes[r.cliente] || 0) + r.valor_dolar;
  });
  const sortedClients = Object.entries(clientVolumes).sort((a, b) => b[1] - a[1]);
  const totalVolume = sortedClients.reduce((sum, c) => sum + c[1], 0);
  const top10Volume = sortedClients.slice(0, 10).reduce((sum, c) => sum + c[1], 0);
  const concentracaoTop10 = totalVolume > 0 ? (top10Volume / totalVolume) * 100 : 0;

  // Retention rate: % of clients who operated in both current and previous period
  const retainedClients = [...currentClients].filter(c => prevClients.has(c));
  const taxaRetencao = prevClients.size > 0 ? (retainedClients.length / prevClients.size) * 100 : 0;

  // New clients: Clients with first transaction in current period
  const allClientsBeforeCurrent = new Set();
  if (start) {
    RAW_DATA.filter(r => {
      const dateOnly = r.data_operacao ? r.data_operacao.slice(0, 10) : null;
      return dateOnly && dateOnly < start;
    }).forEach(r => allClientsBeforeCurrent.add(r.cliente));
  }
  const novosClientes = [...currentClients].filter(c => !allClientsBeforeCurrent.has(c)).length;

  // Clients at risk: Clients with recency > 30 days who were previously active
  const clientsEmRisco = metrics.filter(c => {
    return c.recencia > 30 && c.totalOperacoes >= 2;
  }).length;

  return { concentracaoTop10, taxaRetencao, novosClientes, clientsEmRisco };
}

function renderPortfolioKPIs(metrics) {
  const kpis = calcPortfolioKPIs(metrics);

  document.getElementById('portfolioKpiGrid').innerHTML = \`
    <div class="kpi-card"><div class="kpi-icon purple">&#x1F4CA;</div><div class="kpi-info"><div class="kpi-label">Concentracao Top 10</div><div class="kpi-value">\${fmtPct(kpis.concentracaoTop10)}</div><div class="kpi-sub">do volume total</div></div></div>
    <div class="kpi-card"><div class="kpi-icon green">&#x21BB;</div><div class="kpi-info"><div class="kpi-label">Taxa Retencao</div><div class="kpi-value">\${fmtPct(kpis.taxaRetencao)}</div><div class="kpi-sub">vs periodo anterior</div></div></div>
    <div class="kpi-card"><div class="kpi-icon blue">&#x2795;</div><div class="kpi-info"><div class="kpi-label">Novos Clientes</div><div class="kpi-value">\${kpis.novosClientes}</div><div class="kpi-sub">primeira operacao no periodo</div></div></div>
    <div class="kpi-card"><div class="kpi-icon orange">&#x26A0;</div><div class="kpi-info"><div class="kpi-label">Clientes em Risco</div><div class="kpi-value">\${kpis.clientsEmRisco}</div><div class="kpi-sub">recencia > 30 dias</div></div></div>
  \`;
}

function getRecencyClass(recencia) {
  if (recencia < 15) return 'recency-green';
  if (recencia <= 30) return 'recency-yellow';
  return 'recency-red';
}

function sortClientMetrics(column) {
  if (clientSortColumn === column) {
    clientSortDirection = clientSortDirection === 'asc' ? 'desc' : 'asc';
  } else {
    clientSortColumn = column;
    clientSortDirection = 'desc';
  }

  clientMetrics.sort((a, b) => {
    let valA = a[column];
    let valB = b[column];

    if (column === 'cliente') {
      valA = valA.toLowerCase();
      valB = valB.toLowerCase();
      return clientSortDirection === 'asc' ? valA.localeCompare(valB) : valB.localeCompare(valA);
    }

    return clientSortDirection === 'asc' ? valA - valB : valB - valA;
  });

  renderClientTable();
}

function renderClientTable() {
  const headers = [
    { key: 'cliente', label: 'Cliente' },
    { key: 'ltv', label: 'LTV (USD)' },
    { key: 'recencia', label: 'Recencia (dias)' },
    { key: 'frequencia', label: 'Frequencia (op/mes)' },
    { key: 'ticketMedio', label: 'Ticket Medio (USD)' },
    { key: 'totalOperacoes', label: 'Total Operacoes' },
    { key: 'volumeTotal', label: 'Volume Total (USD)' }
  ];

  const headerHtml = headers.map(h => {
    const sortClass = clientSortColumn === h.key ? (clientSortDirection === 'asc' ? 'sorted-asc' : 'sorted-desc') : '';
    return \`<th class="\${sortClass}" onclick="sortClientMetrics('\${h.key}')">\${h.label}</th>\`;
  }).join('');

  document.getElementById('clientTableHead').innerHTML = '<tr>' + headerHtml + '</tr>';
  document.getElementById('clientTableInfo').textContent = '(' + clientMetrics.length + ' clientes)';

  const bodyHtml = clientMetrics.map(c => \`
    <tr>
      <td>\${c.cliente}</td>
      <td class="num">\${fmtUSD(c.ltv)}</td>
      <td class="num \${getRecencyClass(c.recencia)}">\${c.recencia}</td>
      <td class="num">\${fmtNum(c.frequencia, 2)}</td>
      <td class="num">\${fmtUSD(c.ticketMedio)}</td>
      <td class="num">\${c.totalOperacoes}</td>
      <td class="num">\${fmtUSD(c.volumeTotal)}</td>
    </tr>
  \`).join('');

  document.getElementById('clientTableBody').innerHTML = bodyHtml;
}

function renderPortfolioAnalysis() {
  clientMetrics = calcClientMetrics();
  clientMetrics.sort((a, b) => b[clientSortColumn] - a[clientSortColumn]);
  renderPortfolioKPIs(clientMetrics);
  renderClientTable();
}

// ============================================
// ============================================
// PORTFOLIO ANALYSIS CHARTS
// ============================================

function renderPortfolioCharts(gran) {
  // Destroy existing portfolio charts
  if (charts.distribuicao) charts.distribuicao.destroy();
  if (charts.pareto) charts.pareto.destroy();
  if (charts.clientesAtivos) charts.clientesAtivos.destroy();

  // 1. Client Distribution by Volume (Doughnut Chart)
  const volumeRanges = {
    '<$1k': 0,
    '$1k-5k': 0,
    '$5k-20k': 0,
    '$20k-50k': 0,
    '>$50k': 0
  };

  const clientVolumes = {};
  filtered.forEach(r => {
    clientVolumes[r.cliente] = (clientVolumes[r.cliente] || 0) + r.valor_dolar;
  });

  Object.values(clientVolumes).forEach(vol => {
    if (vol < 1000) volumeRanges['<$1k']++;
    else if (vol < 5000) volumeRanges['$1k-5k']++;
    else if (vol < 20000) volumeRanges['$5k-20k']++;
    else if (vol < 50000) volumeRanges['$20k-50k']++;
    else volumeRanges['>$50k']++;
  });

  const distLabels = Object.keys(volumeRanges);
  const distData = Object.values(volumeRanges);
  const distColors = ['#E74C3C', '#E67E22', '#F1C40F', '#2ECC71', '#3498DB'];

  charts.distribuicao = new Chart(document.getElementById('chartDistribuicao'), {
    type: 'doughnut',
    data: {
      labels: distLabels,
      datasets: [{
        data: distData,
        backgroundColor: distColors,
        borderWidth: 3,
        borderColor: '#fff',
        hoverBorderWidth: 4,
        hoverOffset: 8
      }]
    },
    options: {
      responsive: true,
      maintainAspectRatio: false,
      cutout: '55%',
      plugins: {
        legend: {
          position: 'right',
          labels: { font: { size: 11, family: 'Inter' }, boxWidth: 14, padding: 12, usePointStyle: true }
        },
        tooltip: {
          backgroundColor: 'rgba(0,0,0,0.8)',
          padding: 12,
          cornerRadius: 8,
          callbacks: {
            label: function(ctx) {
              const total = ctx.dataset.data.reduce((a, b) => a + b, 0);
              const pct = total > 0 ? ((ctx.parsed / total) * 100).toFixed(1) : 0;
              return ctx.label + ': ' + ctx.parsed + ' clientes (' + pct + '%)';
            }
          }
        }
      }
    }
  });

  // 2. Pareto Chart (80/20)
  const sortedClients = Object.entries(clientVolumes).sort((a, b) => b[1] - a[1]);
  const totalVolume = sortedClients.reduce((sum, c) => sum + c[1], 0);

  let cumulative = 0;
  let pareto80Index = -1;
  const paretoData = sortedClients.map((c, i) => {
    cumulative += c[1];
    const cumPct = totalVolume > 0 ? (cumulative / totalVolume) * 100 : 0;
    if (pareto80Index === -1 && cumPct >= 80) {
      pareto80Index = i;
    }
    return {
      cliente: c[0].length > 15 ? c[0].slice(0, 15) + '...' : c[0],
      volume: c[1],
      cumPct
    };
  }).slice(0, 20); // Limit to top 20 for readability

  const paretoLabels = paretoData.map(p => p.cliente);
  const paretoVolumes = paretoData.map(p => p.volume);
  const paretoCumPct = paretoData.map(p => p.cumPct);

  // Calculate background colors to highlight 80% threshold
  const paretoBarColors = paretoData.map((p, i) => {
    if (pareto80Index !== -1 && i <= pareto80Index) {
      return 'rgba(108, 63, 160, 0.8)';
    }
    return 'rgba(108, 63, 160, 0.4)';
  });

  charts.pareto = new Chart(document.getElementById('chartPareto'), {
    type: 'bar',
    data: {
      labels: paretoLabels,
      datasets: [
        {
          type: 'bar',
          label: 'Volume USD',
          data: paretoVolumes,
          backgroundColor: paretoBarColors,
          borderRadius: 6,
          yAxisID: 'yVolume'
        },
        {
          type: 'line',
          label: '% Acumulado',
          data: paretoCumPct,
          borderColor: '#E67E22',
          backgroundColor: 'transparent',
          borderWidth: 3,
          tension: 0.4,
          pointRadius: 4,
          pointBackgroundColor: '#E67E22',
          yAxisID: 'yPct'
        }
      ]
    },
    options: {
      responsive: true,
      maintainAspectRatio: false,
      plugins: {
        legend: { position: 'top', labels: { font: { size: 11, family: 'Inter' }, padding: 16, usePointStyle: true } },
        tooltip: {
          backgroundColor: 'rgba(0,0,0,0.8)',
          padding: 12,
          cornerRadius: 8,
          callbacks: {
            label: function(ctx) {
              if (ctx.datasetIndex === 0) {
                return 'Volume: ' + fmtUSD(ctx.parsed.y);
              }
              return '% Acumulado: ' + ctx.parsed.y.toFixed(1) + '%';
            }
          }
        }
      },
      scales: {
        x: {
          grid: { display: false },
          ticks: { font: { size: 9 }, maxRotation: 45, minRotation: 45 }
        },
        yVolume: {
          type: 'linear',
          position: 'left',
          grid: { color: 'rgba(0,0,0,0.06)' },
          ticks: {
            callback: v => '$ ' + (v >= 1000000 ? (v/1000000).toFixed(1) + 'M' : v >= 1000 ? (v/1000).toFixed(0) + 'k' : v.toFixed(0)),
            font: { size: 10 }
          },
          title: { display: true, text: 'Volume USD', font: { size: 11 } }
        },
        yPct: {
          type: 'linear',
          position: 'right',
          min: 0,
          max: 100,
          ticks: {
            callback: v => v + '%',
            font: { size: 10 }
          },
          title: { display: true, text: '% Acumulado', font: { size: 11 } },
          grid: { display: false }
        }
      }
    }
  });

  // 3. Active Clients Evolution (Line Chart)
  const periodMap = {};
  filtered.forEach(r => {
    if (!r.data_operacao) return;
    const dateOnly = r.data_operacao.slice(0, 10);
    let key;
    if (gran === 'dia') key = dateOnly;
    else if (gran === 'mes') key = dateOnly.slice(0, 7);
    else key = dateOnly.slice(0, 4);

    if (!periodMap[key]) periodMap[key] = new Set();
    periodMap[key].add(r.cliente);
  });

  const periodKeys = Object.keys(periodMap).sort();
  const activeClientsLabels = periodKeys.map(k =>
    gran === 'dia' ? k.split('-').reverse().join('/') :
    gran === 'mes' ? k.split('-').reverse().join('/') : k
  );
  const activeClientsData = periodKeys.map(k => periodMap[k].size);

  charts.clientesAtivos = new Chart(document.getElementById('chartClientesAtivos'), {
    type: 'line',
    data: {
      labels: activeClientsLabels,
      datasets: [{
        label: 'Clientes Ativos',
        data: activeClientsData,
        borderColor: '#1E8E3E',
        backgroundColor: 'rgba(30, 142, 62, 0.1)',
        fill: true,
        tension: 0.4,
        pointRadius: 5,
        pointBackgroundColor: '#1E8E3E',
        borderWidth: 3
      }]
    },
    options: {
      responsive: true,
      maintainAspectRatio: false,
      plugins: {
        legend: { display: false },
        tooltip: { backgroundColor: 'rgba(0,0,0,0.8)', padding: 12, cornerRadius: 8 }
      },
      scales: {
        y: {
          beginAtZero: true,
          grid: { color: 'rgba(0,0,0,0.06)' },
          ticks: {
            stepSize: 1,
            font: { size: 10 }
          },
          title: { display: true, text: 'Qtd Clientes', font: { size: 11 } }
        },
        x: {
          grid: { display: false },
          ticks: { font: { size: 10 } }
        }
      }
    }
  });
}

// Netting Analysis - Balance In/Out for IOF Efficiency
function renderNettingAnalysis(gran) {
  const brlUsd = filtered.filter(r => r.fluxo === 'BRL \\u2192 USD');
  const usdBrl = filtered.filter(r => r.fluxo === 'USD \\u2192 BRL');

  // Calculate totals
  const totalOut = brlUsd.reduce((s, r) => s + r.valor_dolar, 0); // Money going OUT (to USA)
  const totalIn = usdBrl.reduce((s, r) => s + r.valor_dolar, 0);  // Money coming IN (from USA)
  const netBalance = totalIn - totalOut;
  const nettingEfficiency = totalOut > 0 ? Math.min(100, (totalIn / totalOut) * 100) : 0;

  // Render KPIs
  document.getElementById('nettingKpis').innerHTML = \`
    <div class="netting-kpi">
      <div class="label">Saida (BRL\\u2192USD)</div>
      <div class="value negative">\${fmtUSD(totalOut)}</div>
      <div class="sub">\${brlUsd.length} operacoes</div>
    </div>
    <div class="netting-kpi">
      <div class="label">Entrada (USD\\u2192BRL)</div>
      <div class="value positive">\${fmtUSD(totalIn)}</div>
      <div class="sub">\${usdBrl.length} operacoes</div>
    </div>
    <div class="netting-kpi">
      <div class="label">Posicao Liquida (Net)</div>
      <div class="value \${netBalance >= 0 ? 'positive' : 'negative'}">\${netBalance >= 0 ? '+' : ''}\${fmtUSD(netBalance)}</div>
      <div class="sub">\${netBalance >= 0 ? 'Posicao comprada' : 'Posicao vendida'}</div>
    </div>
    <div class="netting-kpi">
      <div class="label">Eficiencia Netting</div>
      <div class="value \${nettingEfficiency >= 80 ? 'positive' : nettingEfficiency >= 50 ? 'neutral' : 'negative'}">\${fmtNum(nettingEfficiency, 1)}%</div>
      <div class="sub">\${nettingEfficiency >= 100 ? 'IOF 100% compensado' : netBalance >= 0 ? 'Saldo positivo' : 'Saldo negativo'}</div>
    </div>
  \`;

  // Group by period for charts
  const periodMap = {};
  filtered.forEach(r => {
    if (!r.data_operacao) return;
    const dateOnly = r.data_operacao.slice(0, 10);
    let key;
    if (gran === 'dia') key = dateOnly;
    else if (gran === 'mes') key = dateOnly.slice(0, 7);
    else key = dateOnly.slice(0, 4);

    if (!periodMap[key]) periodMap[key] = { inflow: 0, outflow: 0 };
    if (r.fluxo === 'BRL \\u2192 USD') {
      periodMap[key].outflow += r.valor_dolar;
    } else {
      periodMap[key].inflow += r.valor_dolar;
    }
  });

  const periods = Object.keys(periodMap).sort();
  const labels = periods.map(k => gran === 'dia' ? k.split('-').reverse().join('/') : gran === 'mes' ? k.split('-').reverse().join('/') : k);
  const inflowData = periods.map(k => periodMap[k].inflow);
  const outflowData = periods.map(k => periodMap[k].outflow);
  const netData = periods.map(k => periodMap[k].inflow - periodMap[k].outflow);
  const efficiencyData = periods.map(k => {
    const out = periodMap[k].outflow;
    const eff = out > 0 ? Math.min(100, (periodMap[k].inflow / out) * 100) : 0;
    return eff;
  });

  // Destroy existing charts
  if (charts.netting) charts.netting.destroy();
  if (charts.eficiencia) charts.eficiencia.destroy();

  // Netting Flow Chart
  charts.netting = new Chart(document.getElementById('chartNetting'), {
    type: 'bar',
    data: {
      labels,
      datasets: [
        { label: 'Entrada (USD\\u2192BRL)', data: inflowData, backgroundColor: 'rgba(30,142,62,0.75)', borderRadius: 6 },
        { label: 'Saida (BRL\\u2192USD)', data: outflowData.map(v => -v), backgroundColor: 'rgba(220,38,38,0.75)', borderRadius: 6 },
        { label: 'Posicao Net', data: netData, type: 'line', borderColor: '#1A73E8', backgroundColor: 'rgba(26,115,232,0.1)', borderWidth: 3, pointRadius: 5, pointBackgroundColor: netData.map(v => v >= 0 ? '#1E8E3E' : '#DC2626'), fill: false, tension: 0.4 }
      ]
    },
    options: {
      responsive: true,
      maintainAspectRatio: false,
      plugins: {
        legend: { position: 'top', labels: { font: { size: 11, family: 'Inter' }, padding: 16, usePointStyle: true } },
        tooltip: {
          backgroundColor: 'rgba(0,0,0,0.8)',
          padding: 12,
          cornerRadius: 8,
          callbacks: {
            label: ctx => {
              if (ctx.dataset.label === 'Posicao Net') {
                return ctx.dataset.label + ': ' + (ctx.raw >= 0 ? '+' : '') + fmtUSD(ctx.raw);
              }
              return ctx.dataset.label + ': ' + fmtUSD(Math.abs(ctx.raw));
            }
          }
        }
      },
      scales: {
        x: { grid: { display: false }, ticks: { font: { size: 10 } } },
        y: {
          grid: { color: 'rgba(0,0,0,0.06)' },
          ticks: { callback: v => (v >= 0 ? '+' : '') + (Math.abs(v) >= 1000000 ? (v/1000000).toFixed(1) + 'M' : Math.abs(v) >= 1000 ? (v/1000).toFixed(0) + 'k' : v.toFixed(0)), font: { size: 10 } },
          title: { display: true, text: 'USD', font: { size: 11 } }
        }
      }
    }
  });

  // Efficiency Chart
  charts.eficiencia = new Chart(document.getElementById('chartEficiencia'), {
    type: 'line',
    data: {
      labels,
      datasets: [{
        label: 'Eficiencia Netting %',
        data: efficiencyData,
        borderColor: '#7600be',
        backgroundColor: 'rgba(108,63,160,0.1)',
        fill: true,
        tension: 0.4,
        pointRadius: 5,
        pointBackgroundColor: '#7600be',
        borderWidth: 3
      }, {
        label: 'Meta 100%',
        data: periods.map(() => 100),
        borderColor: '#1E8E3E',
        borderDash: [6, 4],
        borderWidth: 2,
        pointRadius: 0,
        fill: false
      }]
    },
    options: {
      responsive: true,
      maintainAspectRatio: false,
      plugins: {
        legend: { position: 'top', labels: { font: { size: 11, family: 'Inter' }, padding: 16, usePointStyle: true } },
        tooltip: { backgroundColor: 'rgba(0,0,0,0.8)', padding: 12, cornerRadius: 8 }
      },
      scales: {
        x: { grid: { display: false }, ticks: { font: { size: 10 } } },
        y: {
          min: 0, max: 120,
          grid: { color: 'rgba(0,0,0,0.06)' },
          ticks: { callback: v => v + '%', font: { size: 10 } },
          title: { display: true, text: 'Eficiencia', font: { size: 11 } }
        }
      }
    }
  });
}
<\/script>
</body>
</html>`;
}

module.exports = { buildHTML };
