/* Exportación dashboard — PDF decorado + Excel interactivo (filtro año / rango / meses) */

const EXPORT_PDF = {
  brand: [20, 48, 92],
  accent: [16, 124, 72],
  success: [22, 120, 68],
  danger: [185, 40, 40],
  warning: [200, 130, 20],
  muted: [110, 118, 130],
  panel: [245, 247, 252],
  white: [255, 255, 255],
};

const EXPORT_XLS = {
  brand: 'FF14305C',
  accent: 'FF107C4C',
  success: 'FF167848',
  danger: 'FFB92828',
  section: 'FFE8EEF5',
  money: '"$"#,##0',
  pct: '0.0"%"',
};

function fmtExportMoney (n) {
  return `$${(Number(n) || 0).toLocaleString('es-MX', { maximumFractionDigits: 2 })}`;
}

function fmtExportPctSplit (mens, tienda, total) {
  const t = Number(total) || 0;
  if (t <= 0) return '—';
  const pm = Math.round(((Number(mens) || 0) / t) * 1000) / 10;
  const pt = Math.round(((Number(tienda) || 0) / t) * 1000) / 10;
  return `${pm}% mens · ${pt}% tienda`;
}

function computeExportBreakdown (months) {
  if (typeof sumChartMonthsBreakdown === 'function') {
    return sumChartMonthsBreakdown(months);
  }
  return (months || []).reduce((acc, m) => ({
    pagos: acc.pagos + (Number(m.pagos) || 0),
    tienda: acc.tienda + (Number(m.tienda) || 0),
    total: acc.total + (Number(m.total) || 0),
    egresos: acc.egresos + (Number(m.egresos) || 0),
    egresosGastos: acc.egresosGastos + (Number(m.egresosGastos) || 0),
    egresosNomina: acc.egresosNomina + (Number(m.egresosNomina) || 0),
    egresosRetiros: acc.egresosRetiros + (Number(m.egresosRetiros) || 0),
    neto: acc.neto + (Number(m.neto) || 0),
  }), {
    pagos: 0, tienda: 0, total: 0, egresos: 0,
    egresosGastos: 0, egresosNomina: 0, egresosRetiros: 0, neto: 0,
  });
}

function buildFinanceExportPayload (stats, chartFilter, chartYear, chartSeries) {
  if (typeof buildDashboardChartExportContext === 'function') {
    const ctx = buildDashboardChartExportContext(stats, chartFilter, chartYear, chartSeries);
    if (!ctx.breakdown) ctx.breakdown = computeExportBreakdown(ctx.months);
    if (!ctx.currentMonth && typeof describeDashboardCurrentMonth === 'function') {
      ctx.currentMonth = describeDashboardCurrentMonth();
    }
    return ctx;
  }
  const s = stats || {};
  const months = typeof filterDashboardMonths === 'function'
    ? filterDashboardMonths(s.monthlySeries || [], chartFilter)
    : (s.monthlySeries || []);
  return {
    year: chartYear || s.year || new Date().getFullYear(),
    months,
    chartFilter,
    fin: s.finance || {},
    s,
    generatedAt: new Date().toISOString(),
    filterLabel: `Año ${chartYear}`,
    breakdown: computeExportBreakdown(months),
    currentMonth: typeof describeDashboardCurrentMonth === 'function'
      ? describeDashboardCurrentMonth()
      : null,
    tableHeaders: ['Mes'],
    tableRows: months.map((m) => [m.label]),
    seriesTotals: [],
    seriesState: chartSeries || {},
    activeSeries: [],
  };
}

function exportFileSlug (payload) {
  const f = payload.chartFilter;
  if (!f) return 'anio';
  if (f.mode === 'meses' && f.selectedMonths?.length) {
    return `meses-${[...f.selectedMonths].sort((x, y) => x - y).join('-')}`;
  }
  const a = Math.min(f.rangeFrom ?? 1, f.rangeTo ?? 12);
  const b = Math.max(f.rangeFrom ?? 1, f.rangeTo ?? 12);
  return a === b ? `m${a}` : `m${a}-${b}`;
}

/* ——— PDF ——— */

function pdfEnsureSpace (doc, y, need = 40) {
  if (y + need > 278) {
    doc.addPage();
    return 16;
  }
  return y;
}

function pdfHeaderBand (doc, y, title, subtitle) {
  doc.setFillColor(...EXPORT_PDF.brand);
  doc.rect(0, 0, 210, 32, 'F');
  doc.setTextColor(...EXPORT_PDF.white);
  doc.setFont('helvetica', 'bold');
  doc.setFontSize(20);
  doc.text(title, 14, 14);
  doc.setFont('helvetica', 'normal');
  doc.setFontSize(9);
  const lines = doc.splitTextToSize(subtitle || '', 182);
  doc.text(lines, 14, 22);
  doc.setTextColor(30, 30, 30);
  return 38;
}

function pdfSection (doc, y, title, color = EXPORT_PDF.brand) {
  y = pdfEnsureSpace(doc, y, 14);
  doc.setFillColor(...color);
  doc.rect(14, y, 182, 7, 'F');
  doc.setTextColor(...EXPORT_PDF.white);
  doc.setFont('helvetica', 'bold');
  doc.setFontSize(10);
  doc.text(title, 16, y + 5);
  doc.setTextColor(40, 40, 40);
  doc.setFont('helvetica', 'normal');
  return y + 11;
}

function pdfInfoBox (doc, y, lines) {
  doc.setFillColor(...EXPORT_PDF.panel);
  const h = 5 + lines.length * 5;
  doc.rect(14, y, 182, h, 'F');
  doc.setDrawColor(...EXPORT_PDF.muted);
  doc.rect(14, y, 182, h, 'S');
  doc.setFontSize(9);
  doc.setTextColor(50, 55, 65);
  lines.forEach((line, i) => {
    doc.text(line, 16, y + 5 + i * 5);
  });
  doc.setTextColor(30, 30, 30);
  return y + h + 6;
}

function pdfAutoTable (doc, y, opts) {
  if (typeof doc.autoTable !== 'function') return y;
  y = pdfEnsureSpace(doc, y, 30);
  doc.autoTable({
    margin: { left: 14, right: 14 },
    styles: { fontSize: 8, cellPadding: 2.2, lineColor: [220, 224, 230], lineWidth: 0.1 },
    headStyles: { fillColor: EXPORT_PDF.brand, textColor: EXPORT_PDF.white, fontStyle: 'bold' },
    alternateRowStyles: { fillColor: [250, 251, 253] },
    ...opts,
    startY: y,
  });
  return doc.lastAutoTable.finalY + 6;
}

function buildPdfKpiSections (fin, s, cmLabel) {
  const pct = (k) => (fin.pct?.[k] != null && fin.pct[k] !== '' ? `${fin.pct[k]}%` : '—');
  return [
    {
      title: `MES EN CURSO — ${cmLabel}`,
      rows: [
        ['Ingreso · mensualidad', fmtExportMoney(fin.ingresoMensualidadesMes ?? s.ingresosMensualidadesMes), pct('ingresoMensualidadesMes')],
        ['Ingreso · tienda en línea', fmtExportMoney(fin.ingresoTiendaMes ?? s.ingresosTiendaMes), pct('ingresoTiendaMes')],
        ['Ingresos totales del mes', fmtExportMoney(fin.ingresoTotalPagadoMes ?? s.pagadoMes), pct('ingresoTotalMes')],
        ['Egresos · nómina', fmtExportMoney(fin.egresosMesNomina ?? s.egresosMesNomina), ''],
        ['Egresos · gastos operativos', fmtExportMoney(fin.egresosMesGastos ?? s.egresosMesGastos), ''],
        ['Egresos · retiros', fmtExportMoney(fin.egresosMesRetiros ?? s.egresosMesRetiros), ''],
        ['Total egresos del mes', fmtExportMoney(fin.egresosMes ?? s.egresosMes), ''],
        ['Balance del mes', fmtExportMoney(fin.balanceMes ?? s.balanceMes), ''],
      ],
    },
    {
      title: 'ALUMNOS Y COBRANZA (mes en curso)',
      rows: [
        ['Alumnos activos', String(fin.activos ?? s.activos ?? 0), ''],
        ['Pagaron mensualidad', String(fin.alumnosPagaronMes ?? 0), pct('alumnosPagaronMes')],
        ['Activos con adeudo', fmtExportMoney(fin.totalAdeudoMes ?? s.totalAdeudoMes), `${fin.alumnosConAdeudo ?? 0} alumnos`],
        ['Cobrado mens. (activos pagaron)', fmtExportMoney(fin.ingresoMensualidadesActivosPagaron), ''],
        ['Adeudo 1.er recargo', `${fin.alumnosAdeudoRecargo1 ?? 0} alumnos`, fmtExportMoney(fin.pendienteRecargo1)],
        ['Adeudo 2.º recargo', `${fin.alumnosAdeudoRecargo2 ?? 0} alumnos`, fmtExportMoney(fin.pendienteRecargo2)],
        ['Adeudo ambos recargos', `${fin.alumnosAdeudoAmbosRecargos ?? 0} alumnos`, fmtExportMoney(fin.pendienteAmbosRecargos)],
      ],
    },
    {
      title: 'HISTÓRICO ACUMULADO',
      rows: [
        ['Mensualidades (histórico)', fmtExportMoney(fin.ingresoAcumuladoMensualidades ?? s.ingresoAcumuladoMensualidades), ''],
        ['Tienda en línea (histórico)', fmtExportMoney(fin.ingresoAcumuladoTienda ?? s.ingresoAcumuladoTienda), ''],
        ['Ingresos históricos total', fmtExportMoney(fin.ingresoAcumuladoHistorico ?? s.ingresoAcumuladoHistorico), ''],
        ['Egresos históricos · nómina', fmtExportMoney(fin.egresoAcumuladoNomina ?? s.egresoAcumuladoNomina), ''],
        ['Egresos históricos · gastos', fmtExportMoney(fin.egresoAcumuladoGastos ?? s.egresoAcumuladoGastos), ''],
        ['Egresos históricos · retiros', fmtExportMoney(fin.egresoAcumuladoRetiros ?? s.egresoAcumuladoRetiros), ''],
        ['Total egresos históricos', fmtExportMoney(fin.egresoAcumuladoHistorico ?? s.egresoAcumuladoHistorico), ''],
      ],
    },
  ];
}

function buildPdfMonthBreakdownRows (months) {
  const rows = (months || []).map((m) => [
    `${m.label} ${m.year}`,
    fmtExportMoney(m.pagos),
    fmtExportMoney(m.tienda),
    fmtExportMoney(m.total),
    fmtExportPctSplit(m.pagos, m.tienda, m.total),
    fmtExportMoney(m.egresosNomina),
    fmtExportMoney(m.egresosGastos),
    fmtExportMoney(m.egresosRetiros),
    fmtExportMoney(m.egresos),
    fmtExportMoney(m.neto),
  ]);
  const bd = computeExportBreakdown(months);
  rows.push([
    'TOTAL PERIODO',
    fmtExportMoney(bd.pagos),
    fmtExportMoney(bd.tienda),
    fmtExportMoney(bd.total),
    fmtExportPctSplit(bd.pagos, bd.tienda, bd.total),
    fmtExportMoney(bd.egresosNomina),
    fmtExportMoney(bd.egresosGastos),
    fmtExportMoney(bd.egresosRetiros),
    fmtExportMoney(bd.egresos),
    fmtExportMoney(bd.neto),
  ]);
  return rows;
}

async function exportDashboardFinancePdf (stats, chartFilter, chartYear, theme, chartSeries) {
  if (typeof tecosEnsureExportLibs === 'function') await tecosEnsureExportLibs();
  if (typeof jspdf === 'undefined' || !jspdf.jsPDF) {
    alert('jsPDF no cargado. Recarga la página (F5).');
    return;
  }
  const payload = buildFinanceExportPayload(stats, chartFilter, chartYear, chartSeries);
  const { jsPDF } = jspdf;
  const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' });
  const fin = payload.fin || {};
  const s = payload.s || {};
  const cm = payload.currentMonth;
  const cmLabel = cm?.label || 'Mes en curso';
  const genStr = new Date(payload.generatedAt).toLocaleString('es-MX');

  let y = pdfHeaderBand(
    doc,
    0,
    'TECOS ELITE VOLEIBOL',
    `Reporte financiero · ${payload.filterLabel || payload.year} · Generado ${genStr}`,
  );

  y = pdfInfoBox(doc, y, [
    `Filtro gráfica / desglose: ${payload.filterLabel || '—'}`,
    `Mes de facturación (KPIs): ${cmLabel}${cm?.calendarRange ? ` (${cm.calendarRange})` : ''}`,
    `Meses en reporte: ${payload.months.length ? payload.months.map((m) => `${m.label} ${m.year}`).join(', ') : 'Ninguno seleccionado'}`,
    `Series visibles: ${(payload.activeSeries || []).map((x) => x.label).join(', ') || 'Ninguna'}`,
  ]);

  buildPdfKpiSections(fin, s, cmLabel).forEach((sec) => {
    y = pdfSection(doc, y, sec.title);
    y = pdfAutoTable(doc, y, {
      head: [['Concepto', 'Valor', 'Detalle / %']],
      body: sec.rows,
      columnStyles: { 0: { cellWidth: 72 }, 1: { cellWidth: 42, halign: 'right' }, 2: { cellWidth: 58 } },
    });
  });

  if (typeof renderDashboardFinanceChartPng === 'function'
    && payload.months?.length
    && payload.activeSeries?.length
    && typeof buildDashboardChartDatasets === 'function') {
    try {
      const chartUrl = await renderDashboardFinanceChartPng({
        labels: payload.months.map((m) => m.label),
        datasets: buildDashboardChartDatasets(payload.months, payload.seriesState, theme),
        width: 1000,
        height: 440,
      });
      if (chartUrl) {
        y = pdfSection(doc, y, 'GRÁFICA INTERACTIVA (mismas series y meses del dashboard)', EXPORT_PDF.accent);
        y = pdfEnsureSpace(doc, y, 95);
        doc.addImage(chartUrl, 'PNG', 8, y, 194, 86);
        y += 92;
      }
    } catch (e) {
      console.warn('[Tecos] chart pdf', e);
    }
  }

  if (payload.months?.length) {
    y = pdfSection(doc, y, `DESGLOSE POR MES — ${payload.filterLabel}`, EXPORT_PDF.success);
    y = pdfAutoTable(doc, y, {
      head: [[
        'Mes', 'Mensualidades', 'Tienda', 'Total ingresos', 'Partición',
        'Nómina', 'Gastos', 'Retiros', 'Total egresos', 'Balance',
      ]],
      body: buildPdfMonthBreakdownRows(payload.months),
      styles: { fontSize: 7, cellPadding: 1.8 },
      headStyles: { fillColor: EXPORT_PDF.success },
      columnStyles: {
        0: { cellWidth: 22 },
        1: { halign: 'right', cellWidth: 20 },
        2: { halign: 'right', cellWidth: 18 },
        3: { halign: 'right', cellWidth: 20, fontStyle: 'bold' },
        4: { cellWidth: 24, fontSize: 6.5 },
        5: { halign: 'right', cellWidth: 17 },
        6: { halign: 'right', cellWidth: 17 },
        7: { halign: 'right', cellWidth: 16 },
        8: { halign: 'right', cellWidth: 20, fillColor: [255, 245, 245] },
        9: { halign: 'right', cellWidth: 20, fontStyle: 'bold' },
      },
      didParseCell: (data) => {
        if (data.section === 'body' && data.row.index === payload.months.length) {
          data.cell.styles.fontStyle = 'bold';
          data.cell.styles.fillColor = [235, 240, 248];
        }
      },
    });
    doc.setFontSize(7);
    doc.setTextColor(...EXPORT_PDF.muted);
    const note = 'Mensualidades: pagos «pagado» con due_date en el mes. Tienda: órdenes confirmadas/entregadas. Total ingresos = mens. + tienda. Egresos: nómina, gastos y retiros por fecha. Balance = ingresos − egresos.';
    const noteLines = doc.splitTextToSize(note, 182);
    doc.text(noteLines, 14, y);
    y += noteLines.length * 3.5 + 4;
  }

  if (payload.seriesTotals?.length) {
    y = pdfSection(doc, y, 'TOTALES POR SERIE (periodo filtrado)');
    y = pdfAutoTable(doc, y, {
      head: [['Serie', 'Total']],
      body: payload.seriesTotals.map((st) => [st.label, fmtExportMoney(st.total)]),
      columnStyles: { 1: { halign: 'right' } },
    });
  }

  const pageCount = doc.getNumberOfPages();
  for (let p = 1; p <= pageCount; p += 1) {
    doc.setPage(p);
    doc.setFontSize(8);
    doc.setTextColor(...EXPORT_PDF.muted);
    doc.text(`Tecos Elite Voleibol · Página ${p} de ${pageCount}`, 14, 290);
    doc.text(genStr, 150, 290);
  }

  doc.save(`tecos-finanzas-${payload.year}-${exportFileSlug(payload)}-${Date.now()}.pdf`);
}

/* ——— Excel ——— */

function xlsStyleHeader (row, colCount) {
  for (let c = 1; c <= colCount; c += 1) {
    const cell = row.getCell(c);
    cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: EXPORT_XLS.brand } };
    cell.font = { bold: true, color: { argb: 'FFFFFFFF' }, size: 11 };
    cell.alignment = { vertical: 'middle', horizontal: 'center', wrapText: true };
    cell.border = {
      top: { style: 'thin' }, bottom: { style: 'thin' },
      left: { style: 'thin' }, right: { style: 'thin' },
    };
  }
  row.height = 22;
}

function xlsStyleSection (cell, color = EXPORT_XLS.section) {
  cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: color } };
  cell.font = { bold: true, size: 11 };
}

function xlsMoney (cell) {
  cell.numFmt = EXPORT_XLS.money;
  cell.alignment = { horizontal: 'right' };
}

function xlsApplyAutofilter (ws, colCount, rowCount) {
  if (rowCount < 1) return;
  const endCol = String.fromCharCode(64 + Math.min(colCount, 26));
  ws.autoFilter = { from: 'A1', to: `${endCol}${rowCount}` };
}

function xlsAddKpiSheet (wb, payload) {
  const fin = payload.fin || {};
  const s = payload.s || {};
  const cm = payload.currentMonth?.label || 'Mes en curso';
  const ws = wb.addWorksheet('KPIs_mes_actual', { views: [{ state: 'frozen', ySplit: 1 }] });
  ws.getColumn(1).width = 38;
  ws.getColumn(2).width = 18;
  ws.getColumn(3).width = 14;
  ws.getColumn(4).width = 36;

  const addSection = (title, rows) => {
    const sr = ws.addRow([title, '', '', '']);
    xlsStyleSection(sr.getCell(1), EXPORT_XLS.section);
    sr.getCell(1).alignment = { horizontal: 'left' };
    ws.mergeCells(sr.number, 1, sr.number, 4);
    rows.forEach(([k, v, p, note]) => {
      const r = ws.addRow([k, v, p ?? '', note || '']);
      if (typeof v === 'number') xlsMoney(r.getCell(2));
      if (p !== '' && p != null && typeof p === 'number') {
        r.getCell(3).value = p / 100;
        r.getCell(3).numFmt = EXPORT_XLS.pct;
      }
    });
    ws.addRow([]);
  };

  ws.addRow(['TECOS ELITE — KPIs', cm, '', payload.filterLabel]);
  xlsStyleHeader(ws.getRow(1), 4);
  ws.addRow(['Generado', new Date(payload.generatedAt).toLocaleString('es-MX'), '', '']);
  ws.addRow([]);

  addSection('INGRESOS DEL MES (separados y total)', [
    ['Mensualidad del mes', fin.ingresoMensualidadesMes ?? s.ingresosMensualidadesMes, fin.pct?.ingresoMensualidadesMes, 'Pagos pagado · due_date en mes'],
    ['Tienda en línea del mes', fin.ingresoTiendaMes ?? s.ingresosTiendaMes, fin.pct?.ingresoTiendaMes, 'Órdenes confirmadas/entregadas'],
    ['Total ingresos del mes', fin.ingresoTotalPagadoMes ?? s.pagadoMes, fin.pct?.ingresoTotalMes, 'Mensualidad + tienda'],
  ]);

  addSection('EGRESOS DEL MES (Nómina y gastos)', [
    ['Nómina', fin.egresosMesNomina ?? s.egresosMesNomina, '', 'payroll_payments.paid_at'],
    ['Gastos operativos', fin.egresosMesGastos ?? s.egresosMesGastos, '', 'expenses.expense_at'],
    ['Retiros', fin.egresosMesRetiros ?? s.egresosMesRetiros, '', 'finance_withdrawals'],
    ['Total egresos', fin.egresosMes ?? s.egresosMes, '', ''],
    ['Balance del mes', fin.balanceMes ?? s.balanceMes, '', 'Ingresos − egresos'],
  ]);

  addSection('ALUMNOS', [
    ['Alumnos activos', fin.activos ?? s.activos, '', ''],
    ['Pagaron mensualidad', fin.alumnosPagaronMes, fin.pct?.alumnosPagaronMes, ''],
    ['Activos con adeudo ($)', fin.totalAdeudoMes ?? s.totalAdeudoMes, '', `${fin.alumnosConAdeudo ?? 0} alumnos`],
    ['Cobrado mens. activos pagaron', fin.ingresoMensualidadesActivosPagaron, '', ''],
  ]);

  addSection('HISTÓRICO', [
    ['Mensualidades histórico', fin.ingresoAcumuladoMensualidades ?? s.ingresoAcumuladoMensualidades, '', ''],
    ['Tienda histórico', fin.ingresoAcumuladoTienda ?? s.ingresoAcumuladoTienda, '', ''],
    ['Ingresos histórico total', fin.ingresoAcumuladoHistorico ?? s.ingresoAcumuladoHistorico, '', ''],
    ['Egresos histórico total', fin.egresoAcumuladoHistorico ?? s.egresoAcumuladoHistorico, '', ''],
  ]);
}

function xlsAddDesgloseMesSheet (wb, payload) {
  const ws = wb.addWorksheet('Desglose_por_mes', { views: [{ state: 'frozen', ySplit: 2 }] });
  const headers = [
    'Mes', 'Mensualidades', 'Tienda en línea', 'Total ingresos', 'Partición ingreso',
    'Nómina', 'Gastos operativos', 'Retiros', 'Total egresos', 'Balance neto',
  ];
  ws.addRow(['DESGLOSE POR MES', payload.filterLabel]);
  xlsStyleSection(ws.getCell('A1'));
  ws.mergeCells(1, 1, 1, headers.length);
  const hr = ws.addRow(headers);
  xlsStyleHeader(hr, headers.length);
  hr.getCell(4).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: EXPORT_XLS.success } };
  hr.getCell(9).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: EXPORT_XLS.danger } };

  const months = payload.months || [];
  const dataStart = 3;
  months.forEach((m, idx) => {
    const rn = dataStart + idx;
    const r = ws.getRow(rn);
    r.getCell(1).value = `${m.label} ${m.year}`;
    r.getCell(2).value = Number(m.pagos) || 0;
    r.getCell(3).value = Number(m.tienda) || 0;
    r.getCell(4).value = { formula: `B${rn}+C${rn}` };
    r.getCell(5).value = fmtExportPctSplit(m.pagos, m.tienda, m.total);
    r.getCell(6).value = Number(m.egresosNomina) || 0;
    r.getCell(7).value = Number(m.egresosGastos) || 0;
    r.getCell(8).value = Number(m.egresosRetiros) || 0;
    r.getCell(9).value = { formula: `F${rn}+G${rn}+H${rn}` };
    r.getCell(10).value = { formula: `D${rn}-I${rn}` };
    [2, 3, 4, 6, 7, 8, 9, 10].forEach((ci) => xlsMoney(r.getCell(ci)));
  });

  const bd = payload.breakdown || computeExportBreakdown(months);
  const totRn = dataStart + months.length;
  const totRow = ws.getRow(totRn);
  totRow.getCell(1).value = 'TOTAL PERIODO';
  totRow.getCell(2).value = bd.pagos;
  totRow.getCell(3).value = bd.tienda;
  totRow.getCell(4).value = bd.total;
  totRow.getCell(5).value = fmtExportPctSplit(bd.pagos, bd.tienda, bd.total);
  totRow.getCell(6).value = bd.egresosNomina;
  totRow.getCell(7).value = bd.egresosGastos;
  totRow.getCell(8).value = bd.egresosRetiros;
  totRow.getCell(9).value = bd.egresos;
  totRow.getCell(10).value = bd.neto;
  totRow.font = { bold: true };
  totRow.eachCell((c, i) => {
    if (i >= 2 && i <= 10) xlsMoney(c);
    c.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFDCE4F0' } };
  });

  headers.forEach((_, i) => { ws.getColumn(i + 1).width = i === 0 ? 16 : i === 4 ? 22 : 14; });
  xlsApplyAutofilter(ws, headers.length, totRn);
}

function xlsAddGraficaInteractivaSheet (wb, payload) {
  const ws = wb.addWorksheet('Grafica_interactiva', { views: [{ state: 'frozen', ySplit: 1 }] });
  const headers = payload.tableHeaders || ['Mes'];
  const hr = ws.addRow(['Incluir mes', ...headers]);
  xlsStyleHeader(hr, headers.length + 1);
  (payload.tableRows || []).forEach((row, i) => {
    const r = ws.addRow([true, ...row]);
    r.getCell(1).dataValidation = {
      type: 'list',
      allowBlank: false,
      formulae: ['"TRUE,FALSE"'],
    };
    headers.slice(1).forEach((_, ci) => {
      const v = row[ci];
      if (typeof v === 'number') xlsMoney(r.getCell(ci + 2));
    });
  });
  (payload.seriesTotals || []).forEach((st) => {
    const r = ws.addRow(['', `TOTAL · ${st.label}`, st.total]);
    r.getCell(2).font = { bold: true };
    xlsMoney(r.getCell(3));
  });
  ws.getColumn(1).width = 12;
  headers.forEach((_, i) => { ws.getColumn(i + 2).width = 16; });
  xlsApplyAutofilter(ws, headers.length + 1, ws.rowCount);
}

async function exportDashboardFinanceExcel (stats, chartFilter, chartYear, chartSeries) {
  if (typeof tecosEnsureExportLibs === 'function') await tecosEnsureExportLibs();
  if (typeof ExcelJS === 'undefined') {
    alert('ExcelJS no cargado. Recarga la página (F5).');
    return;
  }
  const payload = buildFinanceExportPayload(stats, chartFilter, chartYear, chartSeries);
  const fin = payload.fin || {};
  const s = payload.s || {};
  const wb = new ExcelJS.Workbook();
  wb.creator = 'Tecos Elite Voleibol';
  wb.created = new Date();
  wb.modified = new Date();

  const inicio = wb.addWorksheet('Inicio', { views: [{ showGridLines: false }] });
  inicio.getColumn(1).width = 22;
  inicio.getColumn(2).width = 50;
  inicio.addRow(['TECOS ELITE VOLEIBOL', 'Reporte financiero interactivo']);
  inicio.getRow(1).font = { bold: true, size: 16, color: { argb: EXPORT_XLS.brand } };
  [
    ['Generado', new Date(payload.generatedAt).toLocaleString('es-MX')],
    ['Año', payload.year],
    ['Filtro meses (gráfica y desglose)', payload.filterLabel],
    ['Mes facturación KPIs', payload.currentMonth?.label || '—'],
    ['Meses incluidos', payload.months.map((m) => `${m.label} ${m.year}`).join(', ') || '—'],
    ['Series en gráfica', (payload.activeSeries || []).map((x) => x.label).join(', ') || '—'],
    [],
    ['Hojas del archivo', ''],
    ['KPIs_mes_actual', 'Ingresos/egresos/alumnos del mes en curso (separado)'],
    ['Desglose_por_mes', 'Tabla detallada solo del filtro elegido (año, rango o meses)'],
    ['Grafica_interactiva', 'Misma matriz que el dashboard · filtra columna Incluir mes'],
    ['Series_visibles', 'Qué series tenías activas y totales del periodo'],
    ['Anio_completo', 'Los 12 meses · marca cuáles están en tu filtro'],
    ['Grafica_imagen', 'PNG de la gráfica exportada'],
    [],
    ['Uso', 'Activa Autofiltro en cada hoja. En Grafica_interactiva filtra TRUE en Incluir mes. Formato moneda en columnas $'],
  ].forEach((row) => inicio.addRow(row));

  xlsAddKpiSheet(wb, payload);
  xlsAddDesgloseMesSheet(wb, payload);
  xlsAddGraficaInteractivaSheet(wb, payload);

  const seriesWs = wb.addWorksheet('Series_visibles', { views: [{ state: 'frozen', ySplit: 1 }] });
  seriesWs.columns = [
    { header: 'Visible', key: 'vis', width: 12 },
    { header: 'Serie', key: 'label', width: 28 },
    { header: 'Total periodo', key: 'total', width: 18 },
    { header: 'Tipo', key: 'tipo', width: 14 },
  ];
  const serList = typeof DASHBOARD_CHART_SERIES !== 'undefined' ? DASHBOARD_CHART_SERIES : [];
  serList.forEach((ser) => {
    const on = !!payload.seriesState?.[ser.id];
    const tot = (payload.seriesTotals || []).find((t) => t.id === ser.id);
    const r = seriesWs.addRow({
      vis: on,
      label: ser.label,
      total: tot?.total ?? 0,
      tipo: ser.chartType === 'line' ? 'Línea' : (ser.stack === 'egreso' ? 'Egreso' : 'Ingreso'),
    });
    xlsMoney(r.getCell(3));
    r.getCell(1).dataValidation = { type: 'list', formulae: ['"TRUE,FALSE"'] };
  });
  xlsStyleHeader(seriesWs.getRow(1), 4);
  xlsApplyAutofilter(seriesWs, 4, seriesWs.rowCount);

  const anio = wb.addWorksheet('Anio_completo', { views: [{ state: 'frozen', ySplit: 1 }] });
  const allHeaders = ['En filtro', 'Mes', 'Mensualidades', 'Tienda', 'Total ingresos', 'Nómina', 'Gastos', 'Retiros', 'Total egresos', 'Balance'];
  const ahr = anio.addRow(allHeaders);
  xlsStyleHeader(ahr, allHeaders.length);
  (s.monthlySeries || []).forEach((m) => {
    const inFilter = payload.months.some((x) => x.month === m.month && x.year === m.year);
    const r = anio.addRow([
      inFilter,
      `${m.label} ${m.year}`,
      m.pagos ?? 0,
      m.tienda ?? 0,
      m.total ?? 0,
      m.egresosNomina ?? 0,
      m.egresosGastos ?? 0,
      m.egresosRetiros ?? 0,
      m.egresos ?? 0,
      m.neto ?? 0,
    ]);
    r.getCell(1).dataValidation = { type: 'list', formulae: ['"TRUE,FALSE"'] };
    for (let c = 3; c <= 10; c += 1) xlsMoney(r.getCell(c));
    if (inFilter) {
      r.eachCell((cell) => {
        cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE8F5EE' } };
      });
    }
  });
  allHeaders.forEach((_, i) => { anio.getColumn(i + 1).width = i === 0 ? 11 : 14; });
  xlsApplyAutofilter(anio, allHeaders.length, anio.rowCount);

  const wsGraf = wb.addWorksheet('Grafica_imagen');
  wsGraf.getColumn(1).width = 80;
  if (typeof renderDashboardFinanceChartPng === 'function'
    && payload.months?.length
    && payload.activeSeries?.length
    && typeof buildDashboardChartDatasets === 'function') {
    try {
      const chartUrl = await renderDashboardFinanceChartPng({
        labels: payload.months.map((m) => m.label),
        datasets: buildDashboardChartDatasets(payload.months, payload.seriesState, theme || {}),
        width: 1100,
        height: 480,
      });
      if (chartUrl?.includes(',')) {
        const imgId = wb.addImage({
          base64: chartUrl.slice(chartUrl.indexOf(',') + 1),
          extension: 'png',
        });
        wsGraf.mergeCells('A1:H1');
        wsGraf.getCell('A1').value = `Gráfica · ${payload.filterLabel}`;
        wsGraf.getCell('A1').font = { bold: true, size: 14, color: { argb: EXPORT_XLS.brand } };
        wsGraf.addImage(imgId, { tl: { col: 0, row: 2 }, ext: { width: 780, height: 340 } });
      }
    } catch (e) {
      console.warn('[Tecos] excel chart', e);
    }
  }

  const buffer = await wb.xlsx.writeBuffer();
  const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `tecos-dashboard-${payload.year}-${exportFileSlug(payload)}-${Date.now()}.xlsx`;
  a.click();
  URL.revokeObjectURL(url);
}

Object.assign(window, {
  buildFinanceExportPayload,
  exportDashboardFinancePdf,
  exportDashboardFinanceExcel,
});
