/* Componentes visuales del dashboard admin */

const dashPanelBox = (isMobile) => (
  isMobile ? { width: '100%', maxWidth: '100%', minWidth: 0, boxSizing: 'border-box' } : undefined
);

const DASHBOARD_MONTH_NAMES = [
  'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
  'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre',
];

function describeDashboardCurrentMonth (now = new Date()) {
  const y = now.getFullYear();
  const mi = now.getMonth();
  const short = typeof DASHBOARD_MONTH_LABELS !== 'undefined' ? DASHBOARD_MONTH_LABELS : [];
  const name = DASHBOARD_MONTH_NAMES[mi] || short[mi] || `Mes ${mi + 1}`;
  const lastDay = new Date(y, mi + 1, 0).getDate();
  return {
    year: y,
    month: mi + 1,
    monthIndex: mi,
    label: `${name} ${y}`,
    shortLabel: `${short[mi] || name} ${y}`,
    calendarRange: `del 1 al ${lastDay} de ${name} de ${y}`,
  };
}

function 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,
  });
}

const DashboardPeriodBadge = ({ theme, label, variant = 'current' }) => (
  <span className={`tecos-dash-period-badge tecos-dash-period-badge--${variant}`} style={{
    borderColor: variant === 'chart' ? theme.accent : theme.primary,
    color: variant === 'chart' ? theme.accent : theme.primary,
    background: variant === 'chart' ? `${theme.accent}18` : `${theme.primary}18`,
  }}>{label}</span>
);

const DashboardPeriodDetailList = ({ theme, items }) => (
  <ul className="tecos-dash-period-list">
    {items.map((item) => (
      <li key={item.k} className="tecos-dash-period-list__item" style={{ borderColor: theme.border }}>
        <div className="tecos-dash-period-list__head">
          <span className="tecos-dash-period-list__k" style={{ color: theme.text }}>{item.k}</span>
          {item.v != null && item.v !== '' && (
            <strong className="tecos-dash-period-list__v" style={{ color: item.c || theme.text }}>{item.v}</strong>
          )}
        </div>
        <p className="tecos-dash-period-list__d" style={{ color: theme.textDim }}>{item.d}</p>
      </li>
    ))}
  </ul>
);

const INV_RESET_UNLOCK_KEY = 'tecos:inv-reset-unlocked';
const INV_RESET_UNLOCK_MS = 15 * 60 * 1000;

const readInventoryResetUnlocked = () => {
  try {
    const raw = typeof sessionStorage !== 'undefined' ? sessionStorage.getItem(INV_RESET_UNLOCK_KEY) : null;
    if (!raw) return false;
    const { until } = JSON.parse(raw);
    return typeof until === 'number' && Date.now() < until;
  } catch {
    return false;
  }
};

const setInventoryResetUnlockedSession = () => {
  if (typeof sessionStorage === 'undefined') return;
  sessionStorage.setItem(INV_RESET_UNLOCK_KEY, JSON.stringify({ until: Date.now() + INV_RESET_UNLOCK_MS }));
  window.dispatchEvent(new CustomEvent('tecos:inv-reset-unlock-changed'));
};

const clearInventoryResetUnlockedSession = () => {
  if (typeof sessionStorage === 'undefined') return;
  sessionStorage.removeItem(INV_RESET_UNLOCK_KEY);
  window.dispatchEvent(new CustomEvent('tecos:inv-reset-unlock-changed'));
};

const useInventoryResetUnlock = () => {
  const [unlocked, setUnlocked] = React.useState(() => readInventoryResetUnlocked());
  React.useEffect(() => {
    const sync = () => setUnlocked(readInventoryResetUnlocked());
    window.addEventListener('tecos:inv-reset-unlock-changed', sync);
    const id = window.setInterval(sync, 15000);
    return () => {
      window.removeEventListener('tecos:inv-reset-unlock-changed', sync);
      window.clearInterval(id);
    };
  }, []);
  const unlock = () => {
    setInventoryResetUnlockedSession();
    setUnlocked(true);
  };
  const lock = () => {
    clearInventoryResetUnlockedSession();
    setUnlocked(false);
  };
  return { unlocked, unlock, lock };
};

const onInventoryResetRevealGesture = (e, { unlocked, onNeedPassword, onOpenReset }) => {
  if (e.detail !== 3) return;
  e.preventDefault();
  if (unlocked) onOpenReset?.();
  else onNeedPassword?.();
};

const InventoryResetPasswordModal = ({ theme, open, onClose, onVerify, busy, err }) => {
  const [password, setPassword] = React.useState('');
  React.useEffect(() => {
    if (!open) setPassword('');
  }, [open]);
  if (!open) return null;
  const submit = () => { if (password.trim() && !busy) onVerify?.(password); };
  return (
    <Modal theme={theme} title="Confirmar administrador" onClose={onClose} width={420}>
      <p style={{ margin: '0 0 12px', fontSize: 13, color: theme.textDim, fontFamily: 'Inter, sans-serif', lineHeight: 1.5 }}>
        Escribe tu <strong>contraseña de administrador</strong> para mostrar la opción de reiniciar el historial de inventario (válida 15 minutos).
      </p>
      <Input
        theme={theme}
        label="Contraseña"
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        autoComplete="current-password"
        onKeyDown={(e) => { if (e.key === 'Enter') submit(); }}
      />
      {err ? <p style={{ margin: '12px 0 0', fontSize: 12, color: theme.danger, fontFamily: 'Inter, sans-serif' }}>{err}</p> : null}
      <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end', marginTop: 20, flexWrap: 'wrap' }}>
        <Btn theme={theme} kind="ghost" onClick={onClose} disabled={busy}>Cancelar</Btn>
        <Btn theme={theme} kind="primary" onClick={submit} disabled={!password.trim() || busy}>
          {busy ? 'Verificando…' : 'Desbloquear'}
        </Btn>
      </div>
    </Modal>
  );
};

/** Botón de reinicio: solo visible tras desbloqueo con contraseña. */
const InventoryResetUnlockedButton = ({ theme, onClick, busy, onLock, label = 'Reiniciar historial inventario' }) => (
  <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
    <Btn
      theme={theme}
      kind="ghost"
      size="sm"
      icon="trash"
      onClick={onClick}
      disabled={busy}
      style={{ color: theme.danger, borderColor: `${theme.danger}66`, fontSize: 11 }}
    >
      {label}
    </Btn>
    {onLock && (
      <button
        type="button"
        onClick={onLock}
        title="Ocultar de nuevo"
        style={{
          border: 'none', background: 'transparent', color: theme.textMute,
          fontSize: 10, cursor: 'pointer', fontFamily: 'Inter, sans-serif', textDecoration: 'underline',
        }}
      >
        Ocultar
      </button>
    )}
  </div>
);

/** Modal compartido: vaciar historial de movimientos de inventario (Inventario + Dashboard). */
const InventoryResetHistoryModal = ({ theme, open, onClose, onConfirm, busy, err }) => {
  const [typed, setTyped] = React.useState('');
  React.useEffect(() => {
    if (!open) setTyped('');
  }, [open]);
  if (!open) return null;
  const ok = typed.trim().toUpperCase() === 'REINICIAR';
  return (
    <Modal theme={theme} title="Reiniciar historial de inventario" onClose={onClose} width={480}>
      <p style={{ margin: '0 0 12px', fontSize: 13, color: theme.textDim, fontFamily: 'Inter, sans-serif', lineHeight: 1.5 }}>
        Se borrarán todos los registros de <strong>compras y descuentos</strong> del historial de inventario. El stock actual <strong>no</strong> cambia; las órdenes en tienda siguen en el sistema.
      </p>
      <p style={{ margin: '0 0 12px', fontSize: 12, color: theme.warning, fontFamily: 'Inter, sans-serif' }}>
        Acción irreversible. Elimina antes órdenes de prueba en Verificar comprobantes si no quieres rastros en reportes.
      </p>
      <Input
        theme={theme}
        label='Escribe REINICIAR para confirmar'
        value={typed}
        onChange={(e) => setTyped(e.target.value)}
        autoComplete="off"
      />
      {err ? <p style={{ margin: '12px 0 0', fontSize: 12, color: theme.danger, fontFamily: 'Inter, sans-serif' }}>{err}</p> : null}
      <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end', marginTop: 20, flexWrap: 'wrap' }}>
        <Btn theme={theme} kind="ghost" onClick={onClose} disabled={busy}>Cancelar</Btn>
        <Btn theme={theme} kind="danger" icon="trash" onClick={onConfirm} disabled={!ok || busy}>
          {busy ? 'Reiniciando…' : 'Borrar historial'}
        </Btn>
      </div>
    </Modal>
  );
};

/** Panel superior: qué mes usa cada bloque del dashboard y qué datos suma (colapsable, en vivo). */
const DashboardPeriodOverview = ({
  theme, currentMonth, fin, meta, fmtMoney, chartFilter, chartYear, chartMonths,
  chartTotal, chartPagos, chartTienda, loading, refreshing, open, onOpenChange, onNav,
  invResetUnlocked,
  onInvResetNeedPassword,
  onResetInventoryHistory,
  onInvResetLock,
}) => {
  const chartLabel = typeof describeChartFilter === 'function'
    ? describeChartFilter(chartFilter, chartYear)
    : `Año ${chartYear}`;
  const bd = sumChartMonthsBreakdown(chartMonths);
  const hasChart = chartMonths.length > 0;
  const cm = currentMonth?.label || '—';
  const cmShort = currentMonth?.shortLabel || cm;
  const liveAt = fin.loadedAt
    ? new Date(fin.loadedAt).toLocaleTimeString('es-MX', { hour: '2-digit', minute: '2-digit', second: '2-digit' })
    : null;
  const finReg = meta?.financeEgresosRegistros || {};

  const egresosNominaItems = [
    {
      k: 'Nómina (pagos a entrenadores)',
      v: loading ? '…' : fmtMoney(fin.egresosMesNomina),
      c: theme.urgent,
      d: `${loading ? '…' : fin.egresosMesNominaCount} pago(s) en ${cmShort} · tabla payroll_payments, campo paid_at. Se registran en Nómina y gastos → Registrar pago.`,
    },
    {
      k: 'Gastos operativos',
      v: loading ? '…' : fmtMoney(fin.egresosMesGastos),
      c: theme.warning,
      d: `${loading ? '…' : fin.egresosMesGastosCount} gasto(s) en ${cmShort} · tabla expenses, campo expense_at. Alta/edición en Nómina y gastos → Gastos operativos.`,
    },
    {
      k: 'Retiros de caja',
      v: loading ? '…' : fmtMoney(fin.egresosMesRetiros),
      c: theme.danger,
      d: `${loading ? '…' : fin.egresosMesRetirosCount} retiro(s) en ${cmShort} · tabla finance_withdrawals, campo withdrawn_at.`,
    },
    {
      k: 'Total egresos del mes',
      v: loading ? '…' : fmtMoney(fin.egresosMes),
      c: theme.danger,
      d: `Suma de los tres rubros anteriores (${loading ? '…' : fin.egresosMesMovimientosCount} movimiento(s) en ${cmShort}). La gráfica reparte lo mismo por mes calendario.`,
    },
  ];

  const currentMonthItems = [
    {
      k: 'Mensualidades del mes',
      v: loading ? '…' : fmtMoney(fin.ingresoMensualidadesMes),
      c: theme.primary,
      d: `Suma pagos con estado «pagado» cuyo periodo de mensualidad (fecha de vencimiento / due_date) cae en ${cm}. No incluye comprobantes en revisión.`,
    },
    {
      k: 'Tienda en línea del mes',
      v: loading ? '…' : fmtMoney(fin.ingresoTiendaMes),
      c: theme.accent,
      d: `Suma órdenes confirmadas o entregadas con fecha de confirmación o creación dentro de ${cm}.`,
    },
    {
      k: 'Total ingresos del mes',
      v: loading ? '…' : fmtMoney(fin.ingresoTotalPagadoMes),
      c: theme.success,
      d: `Mensualidades (${loading ? '…' : fmtMoney(fin.ingresoMensualidadesMes)}) + tienda (${loading ? '…' : fmtMoney(fin.ingresoTiendaMes)}).`,
    },
    ...egresosNominaItems,
    {
      k: 'Balance del mes',
      v: loading ? '…' : fmtMoney(fin.balanceMes),
      c: (fin.balanceMes || 0) >= 0 ? theme.success : theme.danger,
      d: `Ingresos del mes − egresos del mes (${cm}). Fórmula: ${fmtMoney(fin.ingresoTotalPagadoMes)} − ${fmtMoney(fin.egresosMes)}.`,
    },
    {
      k: 'Adeudo y cobranza',
      v: loading ? '…' : fmtMoney(fin.totalAdeudoMes),
      c: theme.danger,
      d: `${loading ? '…' : fin.alumnosConAdeudo} alumno(s) activos con adeudo del mes de facturación ${cm} (misma regla que lista Alumnos y portal). Recargos según tramo de hoy: ${fin.tramoHoy || '—'}.`,
    },
    {
      k: 'Ingreso acumulado (histórico)',
      v: loading ? '…' : fmtMoney(fin.ingresoAcumuladoHistorico),
      c: theme.info,
      d: 'Todos los meses: cada mensualidad confirmada + cada orden de tienda confirmada, sin límite de fecha.',
    },
  ];

  const collapsedSummary = loading
    ? 'Cargando totales…'
    : `${cmShort}: ingresos ${fmtMoney(fin.ingresoTotalPagadoMes)} · egresos ${fmtMoney(fin.egresosMes)} `
      + `(nómina ${fmtMoney(fin.egresosMesNomina)} + gastos ${fmtMoney(fin.egresosMesGastos)} + retiros ${fmtMoney(fin.egresosMesRetiros)}) · balance ${fmtMoney(fin.balanceMes)}`;

  const isMobile = useIsMobile();
  return (
    <Card theme={theme} style={dashPanelBox(isMobile)}>
      <div className="tecos-dash-period-collapse-head">
        <div className="tecos-dash-period-collapse-head__main">
          <h3 className="tecos-dash-section-head__title" style={{ color: theme.text, margin: 0 }}>
            PERIODOS Y QUÉ SE SUMA
          </h3>
          <p className="tecos-dash-period-collapse-summary" style={{ color: theme.textDim }}>
            {collapsedSummary}
          </p>
          <div
            className="tecos-dash-period-live"
            style={{ color: theme.textMute, cursor: 'default', userSelect: 'none' }}
            onClick={(e) => onInvResetNeedPassword && onInventoryResetRevealGesture(e, {
              unlocked: invResetUnlocked,
              onNeedPassword: onInvResetNeedPassword,
              onOpenReset: onResetInventoryHistory,
            })}
          >
            {refreshing && (
              <span className="tecos-dash-period-live__dot" style={{ background: theme.success }} aria-hidden />
            )}
            <span>
              {refreshing ? 'Actualizando en vivo…' : 'En vivo'}
              {liveAt ? ` · Última lectura ${liveAt}` : ''}
              {!loading && finReg.nomina != null && (
                <span> · Nómina y gastos: {finReg.nomina} pagos nómina, {finReg.gastos} gastos, {finReg.retiros} retiros en sistema</span>
              )}
            </span>
          </div>
        </div>
        <div className="tecos-dash-period-collapse-actions">
          {invResetUnlocked && onResetInventoryHistory && (
            <InventoryResetUnlockedButton
              theme={theme}
              onClick={onResetInventoryHistory}
              onLock={onInvResetLock}
              label="Reiniciar historial"
            />
          )}
          <button type="button" className="tecos-dash-period-toggle" onClick={() => onNav?.('nomina-gastos')} style={{
            border: `1px solid ${theme.border}`, color: theme.textDim, background: theme.bgInput,
          }}>Nómina y gastos</button>
          <button
            type="button"
            className="tecos-dash-period-toggle tecos-dash-period-toggle--primary"
            onClick={() => onOpenChange?.(!open)}
            aria-expanded={open}
            style={{
              border: `1px solid ${theme.primary}`, color: theme.primary, background: `${theme.primary}14`,
            }}
          >
            {open ? 'Ocultar detalle' : 'Ver detalle de periodos'}
            <Icon name="chev" size={14} style={{ transform: open ? 'rotate(-90deg)' : 'rotate(90deg)', transition: 'transform .2s' }} />
          </button>
        </div>
      </div>

      {open && (
        <div className="tecos-dash-period-grid tecos-dash-period-grid--open">
          <section className="tecos-dash-period-block" style={{ borderColor: theme.primary }}>
            <div className="tecos-dash-period-block__title">
              <DashboardPeriodBadge theme={theme} label={`MES EN CURSO · ${cm}`} variant="current" />
              <span style={{ color: theme.textDim, fontSize: 12, fontFamily: 'Inter, sans-serif' }}>
                Calendario {currentMonth?.calendarRange || ''}
              </span>
            </div>
            <p className="tecos-dash-period-block__intro" style={{ color: theme.textDim }}>
              KPIs, alumnos e ingresos/egresos usan este mes. Todo se recalcula en vivo al cambiar pagos, alumnos, tienda o Nómina y gastos.
            </p>
            <DashboardPeriodDetailList theme={theme} items={currentMonthItems} />
          </section>

          <section className="tecos-dash-period-block" style={{ borderColor: theme.accent }}>
            <div className="tecos-dash-period-block__title">
              <DashboardPeriodBadge theme={theme} label="GRÁFICA INTERACTIVA" variant="chart" />
              <span style={{ color: theme.text, fontSize: 12, fontWeight: 600, fontFamily: 'Inter, sans-serif' }}>
                {chartLabel}
              </span>
            </div>
            <p className="tecos-dash-period-block__intro" style={{ color: theme.textDim }}>
              La gráfica y exportaciones usan solo los meses del filtro. Egresos por mes = misma suma que Nómina y gastos (nómina + gastos + retiros por fecha).
            </p>
            {!hasChart ? (
              <p style={{ fontSize: 13, color: theme.warning, fontFamily: 'Inter, sans-serif', margin: 0 }}>
                No hay meses en el filtro. En modo «Meses sueltos» marca al menos uno; en «Rango» ajusta del mes — al mes —.
              </p>
            ) : (
              <DashboardPeriodDetailList theme={theme} items={[
                {
                  k: 'Meses incluidos',
                  v: `${chartMonths.length} mes(es)`,
                  d: chartMonths.map((m) => `${m.label} ${m.year}`).join(' · '),
                },
                {
                  k: 'Ingresos en el periodo',
                  v: fmtMoney(bd.total),
                  c: theme.success,
                  d: `Mensualidades ${fmtMoney(bd.pagos)} + tienda ${fmtMoney(bd.tienda)} (año ${chartYear}).`,
                },
                {
                  k: 'Egresos en el periodo (Nómina y gastos)',
                  v: fmtMoney(bd.egresos),
                  c: theme.danger,
                  d: `Gastos ${fmtMoney(bd.egresosGastos)} + nómina ${fmtMoney(bd.egresosNomina)} + retiros ${fmtMoney(bd.egresosRetiros)} — cada mes del ${chartYear} por calendario.`,
                },
                {
                  k: 'Balance neto del periodo',
                  v: fmtMoney(bd.neto),
                  c: bd.neto >= 0 ? theme.success : theme.danger,
                  d: 'Por cada mes: (mensualidades + tienda) − egresos; luego se suman los meses visibles.',
                },
              ]} />
            )}
          </section>
        </div>
      )}
    </Card>
  );
};

function formatIngresoSplitPct (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`;
}

/** Tabla mes a mes del filtro de la gráfica (ingresos y egresos desglosados). */
const DashboardChartMonthTable = ({ theme, chartMonths, chartYear, chartFilter, fmtMoney, loading }) => {
  const chartLabel = typeof describeChartFilter === 'function'
    ? describeChartFilter(chartFilter, chartYear)
    : `Año ${chartYear}`;
  if (loading) {
    return <p style={{ fontSize: 13, color: theme.textDim, fontFamily: 'Inter, sans-serif' }}>Cargando desglose por mes…</p>;
  }
  if (!chartMonths.length) {
    return (
      <p style={{ fontSize: 13, color: theme.warning, fontFamily: 'Inter, sans-serif', margin: 0 }}>
        Sin meses en el filtro «{chartLabel}». Los totales de la gráfica están en $0 hasta que selecciones meses.
      </p>
    );
  }
  const bd = sumChartMonthsBreakdown(chartMonths);
  const fmt = (n) => fmtMoney(n);
  const thStyle = { color: theme.textMute, borderColor: theme.border };
  const cellBorder = { borderColor: theme.border };

  const renderIngresoCells = (mens, tienda, total, opts = {}) => (
    <>
      <td className="tecos-dash-month-table__mens" style={{ ...cellBorder, color: theme.primary }}>{fmt(mens)}</td>
      <td className="tecos-dash-month-table__tienda" style={{ ...cellBorder, color: theme.accent }}>{fmt(tienda)}</td>
      <td className="tecos-dash-month-table__ing-total" style={{ ...cellBorder, color: theme.success, fontWeight: opts.bold ? 700 : 600 }}>
        {fmt(total)}
      </td>
      <td className="tecos-dash-month-table__ing-pct" style={{ ...cellBorder, color: theme.textDim, fontSize: 11 }}>
        {formatIngresoSplitPct(mens, tienda, total)}
      </td>
    </>
  );

  const renderEgresoCells = (nomina, gastos, retiros, total, opts = {}) => (
    <>
      <td style={{ ...cellBorder, color: theme.text }}>{fmt(nomina)}</td>
      <td style={{ ...cellBorder, color: theme.text }}>{fmt(gastos)}</td>
      <td style={{ ...cellBorder, color: theme.text }}>{fmt(retiros)}</td>
      <td className="tecos-dash-month-table__egr-total" style={{ ...cellBorder, color: theme.danger, fontWeight: opts.bold ? 700 : 600 }}>
        {fmt(total)}
      </td>
    </>
  );

  return (
    <div className="tecos-dash-month-table-wrap tecos-dash-scroll-x">
      <p className="tecos-dash-scroll-hint" aria-hidden="true">← Desliza para ver toda la tabla →</p>
      <div className="tecos-dash-month-table-head" style={{ color: theme.textMute }}>
        <span>Desglose por mes · {chartLabel}</span>
        <span className="tecos-dash-month-table-head__sum" style={{ color: theme.success }}>
          Ingresos periodo: {fmt(bd.total)}
          <span style={{ color: theme.textDim, fontWeight: 500 }}>
            {' '}(mens. {fmt(bd.pagos)} + tienda {fmt(bd.tienda)})
          </span>
        </span>
      </div>
      <table className="tecos-dash-month-table tecos-dash-month-table--detailed">
        <thead>
          <tr className="tecos-dash-month-table__group-row" style={thStyle}>
            <th rowSpan={2}>Mes</th>
            <th colSpan={4} className="tecos-dash-month-table__group" style={{ color: theme.success, borderColor: theme.border }}>
              Ingresos
            </th>
            <th colSpan={4} className="tecos-dash-month-table__group" style={{ color: theme.danger, borderColor: theme.border }}>
              Egresos
            </th>
            <th rowSpan={2} className="tecos-dash-month-table__balance-h">Balance</th>
          </tr>
          <tr className="tecos-dash-month-table__subhead" style={thStyle}>
            <th style={{ color: theme.primary }}>Mensualidades</th>
            <th style={{ color: theme.accent }}>Tienda</th>
            <th>Total ingresos</th>
            <th>Partición</th>
            <th>Nómina</th>
            <th>Gastos</th>
            <th>Retiros</th>
            <th>Total egresos</th>
          </tr>
        </thead>
        <tbody>
          {chartMonths.map((m) => {
            const neto = Number(m.neto) || 0;
            return (
              <tr key={`${m.year}-${m.month}`} style={{ ...cellBorder, color: theme.text }}>
                <td className="tecos-dash-month-table__mes"><strong>{m.label} {m.year}</strong></td>
                {renderIngresoCells(m.pagos, m.tienda, m.total)}
                {renderEgresoCells(m.egresosNomina, m.egresosGastos, m.egresosRetiros, m.egresos)}
                <td className="tecos-dash-month-table__balance" style={{ color: neto >= 0 ? theme.success : theme.danger, fontWeight: 600 }}>
                  {fmt(neto)}
                </td>
              </tr>
            );
          })}
        </tbody>
        <tfoot>
          <tr style={{ ...cellBorder, color: theme.text, fontWeight: 700 }}>
            <td>Total periodo</td>
            {renderIngresoCells(bd.pagos, bd.tienda, bd.total, { bold: true })}
            {renderEgresoCells(bd.egresosNomina, bd.egresosGastos, bd.egresosRetiros, bd.egresos, { bold: true })}
            <td className="tecos-dash-month-table__balance" style={{ color: bd.neto >= 0 ? theme.success : theme.danger }}>
              {fmt(bd.neto)}
            </td>
          </tr>
        </tfoot>
      </table>
      <p className="tecos-dash-month-table-note" style={{ color: theme.textDim }}>
        <strong style={{ color: theme.primary }}>Mensualidades</strong>: pagos con estado «pagado» y due_date en ese mes.
        <strong style={{ color: theme.accent }}> Tienda</strong>: órdenes confirmadas o entregadas en ese mes.
        <strong> Total ingresos</strong> = mensualidades + tienda (misma suma que la gráfica).
        <strong style={{ color: theme.danger }}> Egresos</strong>: nómina (payroll_payments), gastos operativos y retiros con fecha en el mes.
        <strong> Balance</strong> = total ingresos − total egresos.
      </p>
    </div>
  );
};

const DashboardPortalSyncBanner = ({ theme, fin, meta, billingSettings, onNav }) => {
  const isMobile = useIsMobile();
  const fee = Number(billingSettings?.monthly_fee ?? fin?.monthlyFee) || 0;
  return (
  <div className="tecos-dash-sync" style={{
    ...dashPanelBox(isMobile),
    background: `linear-gradient(90deg, ${theme.primary}18, ${theme.success}12)`,
    border: `1px solid ${theme.primary}44`,
  }}>
    <div className="tecos-dash-sync__icon" style={{ background: `${theme.primary}22`, color: theme.primary }}>
      <Icon name="users" size={22} />
    </div>
    <div className="tecos-dash-sync__body" style={{ flex: 1, minWidth: 0 }}>
      <div className="tecos-dash-sync__title">Sincronizado con el portal del alumno</div>
      <p className="tecos-dash-sync__text">
        Misma lógica que <strong>Ventas y pagos</strong> y el calendario del alumno
        {fee > 0 && <> · Mensualidad base <strong>${fee.toLocaleString('es-MX')}</strong></>}.
        Tramo hoy: <strong>{fin?.tramoHoy || '—'}</strong>.
        {fin?.alumnosPagaronMes != null && (
          <span> · {fin.alumnosPagaronMes} de {fin.activos} activos pagaron el mes en curso.</span>
        )}
        {meta?.paymentsTotal > 0 && (
          <span> · Datos: {meta.paymentsPagado} pago(s) confirmados, {meta.paymentsEnRevision} en revisión, {meta.studentsLoaded} alumnos.</span>
        )}
      </p>
      {fin?.billingHint?.activeMonths && (
        <p className="tecos-dash-sync__text" style={{ marginTop: 6, fontSize: 11, opacity: 0.9 }}>
          {fin.billingHint.activeMonths}
        </p>
      )}
    </div>
    <button type="button" className="tecos-dash-sync__btn" onClick={() => onNav?.('pagos')} style={{
      border: `1px solid ${theme.primary}`, color: theme.primary, background: theme.bgCard,
    }}>Ver cobros</button>
  </div>
  );
};

/** Cuadrícula 4 columnas × 2 filas de KPIs financieros. */
const DashboardKpiGrid = ({
  theme, fin, fmtMoney, pct, currentMonth, loading, onNav,
}) => {
  const isMobile = useIsMobile();
  const hist = 'Histórico · todos los meses';
  const mes = currentMonth?.shortLabel || 'Mes en curso';
  const activos = Number(fin.activos) || 0;
  const conAdeudo = Number(fin.alumnosConAdeudo) || 0;
  const pctAdeudoActivos = activos > 0
    ? `${Math.round((conAdeudo / activos) * 1000) / 10}% de activos`
    : '—';
  const ingresoMes = Number(fin.ingresoTotalPagadoMes) || 0;

  const col = (cards) => (
    <div className="tecos-dash-kpi-col">{cards}</div>
  );

  if (typeof DashboardKpiCard !== 'function') return null;

  return (
    <div className={`tecos-dash-kpi-grid${isMobile ? ' tecos-dash-kpi-grid--mobile' : ''}`}>
      {col(
        <>
          <DashboardKpiCard theme={theme} period={hist} label="Histórico · mensualidades"
            value={loading ? '…' : fmtMoney(fin.ingresoAcumuladoMensualidades)}
            sub="Solo pagos de mensualidad confirmados, todos los meses"
            color={theme.primary} onClick={() => onNav?.('pagos')} />
          <DashboardKpiCard theme={theme} period={hist} label="Histórico · tienda en línea"
            value={loading ? '…' : fmtMoney(fin.ingresoAcumuladoTienda)}
            sub="Solo órdenes confirmadas o entregadas, histórico"
            color={theme.accent} onClick={() => onNav?.({ nav: 'comprobantes', comprobantesTab: 'tienda' })} />
        </>,
      )}
      {col(
        <>
          <DashboardKpiCard theme={theme} period={mes} label="Ingresos del mes en curso"
            value={loading ? '…' : fmtMoney(fin.ingresoTotalPagadoMes)}
            sub={loading ? '' : `Mens. ${fmtMoney(fin.ingresoMensualidadesMes)} + tienda ${fmtMoney(fin.ingresoTiendaMes)}`}
            color={theme.success} onClick={() => onNav?.('pagos')} />
          <DashboardKpiCard theme={theme} period={mes} label="Ingreso del mes · tienda"
            value={loading ? '…' : fmtMoney(fin.ingresoTiendaMes)}
            pct={loading ? '' : `${pct('ingresoTiendaMes')} del ingreso del mes`}
            sub={loading ? '' : `Total mes: ${fmtMoney(ingresoMes)}`}
            color={theme.accent} onClick={() => onNav?.({ nav: 'comprobantes', comprobantesTab: 'tienda' })} />
        </>,
      )}
      {col(
        <>
          <DashboardKpiCard theme={theme} period={mes} label="Ingreso del mes · mensualidad"
            value={loading ? '…' : fmtMoney(fin.ingresoMensualidadesMes)}
            pct={loading ? '' : `${pct('ingresoMensualidadesMes')} del ingreso del mes`}
            sub={loading ? '' : (
              `Solo mensualidades cobradas en ${mes}. Tienda: ${fmtMoney(fin.ingresoTiendaMes)}`
            )}
            color={theme.primary} onClick={() => onNav?.('pagos')} />
          <DashboardKpiCard theme={theme} period={mes} label="Alumnos activos"
            value={loading ? '…' : String(fin.activos)}
            sub={loading ? '' : (
              `${fin.alumnosPagaronMes} de ${activos} pagaron el mes · cobrado ${fmtMoney(fin.ingresoMensualidadesActivosPagaron ?? fin.ingresoMensualidadesMes)}`
            )}
            pct={loading ? '' : `${pct('alumnosPagaronMes')} pagaron`}
            color={theme.primary} onClick={() => onNav?.('alumnos')} />
        </>,
      )}
      {col(
        <>
          <DashboardKpiCard theme={theme} period={hist} label="Egresos históricos"
            value={loading ? '…' : fmtMoney(fin.egresoAcumuladoHistorico)}
            sub={loading ? '' : (
              `Nómina ${fmtMoney(fin.egresoAcumuladoNomina)} · gastos ${fmtMoney(fin.egresoAcumuladoGastos)} · retiros ${fmtMoney(fin.egresoAcumuladoRetiros)} (Nómina y gastos)`
            )}
            color={theme.danger} onClick={() => onNav?.('nomina-gastos')} />
          <DashboardKpiCard theme={theme} period={mes} label="Activos con adeudo"
            value={loading ? '…' : fmtMoney(fin.totalAdeudoMes)}
            sub={loading ? '' : `${conAdeudo} de ${activos} alumno(s) pendientes de pagar`}
            pct={loading ? '' : pctAdeudoActivos}
            color={conAdeudo ? theme.danger : theme.success}
            onClick={() => onNav?.({ nav: 'alumnos', alumnosFilter: 'adeudo' })} />
        </>,
      )}
    </div>
  );
};

const DashboardKpiCard = ({ theme, label, value, sub, pct, color, onClick, large, period }) => (
  <button type="button" className={`tecos-dash-kpi${large ? ' tecos-dash-kpi--lg' : ''}`} onClick={onClick} style={{
    borderColor: `${color}55`,
    background: `linear-gradient(145deg, ${color}14, ${theme.bgCard})`,
    cursor: onClick ? 'pointer' : 'default',
    textAlign: 'left',
  }}>
    {period && (
      <div className="tecos-dash-kpi__period" style={{ color, borderColor: `${color}44`, background: `${color}12` }}>
        {period}
      </div>
    )}
    <div className="tecos-dash-kpi__label" style={{ color: theme.textMute }}>{label}</div>
    <div className="tecos-dash-kpi__value" style={{ color: theme.text }}>{value}</div>
    {pct && <div className="tecos-dash-kpi__pct" style={{ color }}>{pct}</div>}
    {sub && <div className="tecos-dash-kpi__sub" style={{ color: theme.textDim }}>{sub}</div>}
  </button>
);

const DashboardSectionHead = ({ theme, title, sub, actions }) => (
  <div className="tecos-dash-section-head">
    <div>
      <h3 className="tecos-dash-section-head__title" style={{ color: theme.text }}>{title}</h3>
      {sub && <p className="tecos-dash-section-head__sub" style={{ color: theme.textDim }}>{sub}</p>}
    </div>
    {actions && <div className="tecos-dash-section-head__actions">{actions}</div>}
  </div>
);

const DashboardAlumnosCobranza = ({ theme, fin, fmtMoney, onNav, loading, currentMonth }) => {
  const isMobile = useIsMobile();
  const dash = (v) => (loading ? '…' : v);
  const rows = [
    { l: 'Activos', v: dash(fin.activos), c: theme.primary, nav: 'alumnos' },
    { l: 'Pagaron mensualidad', v: dash(fin.alumnosPagaronMes), p: fin.pct?.alumnosPagaronMes, d: fmtMoney(fin.ingresoMensualidadesActivosPagaron), c: theme.success, nav: 'pagos' },
    { l: 'Con adeudo (mes)', v: dash(fin.alumnosConAdeudo), d: fmtMoney(fin.totalAdeudoMes), c: theme.danger, nav: 'alumnos', filter: 'adeudo' },
    { l: 'Mens. atrasadas', v: dash(fin.mesesAtrasados), c: theme.warning, nav: 'pagos' },
    { l: 'Mens. urgentes', v: dash(fin.mesesUrgentes), c: theme.urgent, nav: 'pagos' },
    { l: 'Adeudo 1.er recargo', v: dash(fin.alumnosAdeudoRecargo1), p: fin.pct?.alumnosRecargo1, d: fmtMoney(fin.pendienteRecargo1), c: theme.warning, nav: 'alumnos', filter: 'adeudo' },
    { l: 'Adeudo 2.º recargo', v: dash(fin.alumnosAdeudoRecargo2), p: fin.pct?.alumnosRecargo2, d: fmtMoney(fin.pendienteRecargo2), c: theme.danger, nav: 'alumnos', filter: 'adeudo' },
    { l: 'Ambos recargos', v: dash(fin.alumnosAdeudoAmbosRecargos), p: fin.pct?.alumnosAmbosRecargos, d: fmtMoney(fin.pendienteAmbosRecargos), c: theme.urgent, nav: 'alumnos', filter: 'adeudo' },
  ];
  return (
    <Card theme={theme} style={dashPanelBox(isMobile)}>
      <DashboardSectionHead
        theme={theme}
        title="ALUMNOS Y COBRANZA"
        sub={currentMonth ? `Mes de facturación: ${currentMonth.label} · mismos datos que el portal` : 'Misma información que ve cada alumno en su portal'}
        actions={currentMonth ? <DashboardPeriodBadge theme={theme} label={currentMonth.shortLabel} /> : null}
      />
      <div className="tecos-dash-alumnos-grid">
        {rows.map((r) => (
          <button key={r.l} type="button" className="tecos-dash-alumno-stat" onClick={() => onNav?.(r.filter ? { nav: r.nav, alumnosFilter: r.filter } : r.nav)} style={{
            borderColor: `${r.c}44`, background: `${r.c}0c`,
          }}>
            <span className="tecos-dash-alumno-stat__n" style={{ color: theme.textMute }}>{r.l}</span>
            <span className="tecos-dash-alumno-stat__v" style={{ color: theme.text }}>{r.v}</span>
            {r.p != null && r.p !== '' && <span className="tecos-dash-alumno-stat__p" style={{ color: r.c }}>{r.p}% activos</span>}
            {r.d && <span className="tecos-dash-alumno-stat__d" style={{ color: theme.textDim }}>{r.d}</span>}
          </button>
        ))}
      </div>
    </Card>
  );
};

const DashboardIngresosEgresos = ({ theme, fin, fmtMoney, pct, onNav, loading, currentMonth }) => {
  const isMobile = useIsMobile();
  const m = (n) => (loading ? '…' : fmtMoney(n));
  const bal = Number(fin.balanceMes) || 0;
  const cm = currentMonth?.label || 'mes en curso';
  return (
  <Card theme={theme} style={dashPanelBox(isMobile)}>
    <DashboardSectionHead
      theme={theme}
      title="INGRESOS Y EGRESOS"
      sub={`Totales de ${cm} (calendario ${currentMonth?.calendarRange || ''})`}
      actions={currentMonth ? <DashboardPeriodBadge theme={theme} label={currentMonth.shortLabel} /> : null}
    />
    <div className="tecos-dash-flow">
      <div className="tecos-dash-flow__col">
        <div className="tecos-dash-flow__head" style={{ color: theme.success }}>Ingresos</div>
        <button type="button" className="tecos-dash-flow__row" onClick={() => onNav?.('pagos')}>
          <span>Mensualidades</span><strong style={{ color: theme.primary }}>{m(fin.ingresoMensualidadesMes)}</strong>
          <em>{pct('ingresoMensualidadesMes')}</em>
        </button>
        <button type="button" className="tecos-dash-flow__row" onClick={() => onNav?.({ nav: 'comprobantes', comprobantesTab: 'tienda' })}>
          <span>Tienda en línea</span><strong style={{ color: theme.accent }}>{m(fin.ingresoTiendaMes)}</strong>
          <em>{pct('ingresoTiendaMes')}</em>
        </button>
        <div className="tecos-dash-flow__row tecos-dash-flow__row--total">
          <span>Total mes</span><strong>{m(fin.ingresoTotalPagadoMes)}</strong>
        </div>
        <div className="tecos-dash-flow__row" style={{ opacity: 0.85 }}>
          <span>Acumulado histórico</span><strong style={{ color: theme.info }}>{m(fin.ingresoAcumuladoHistorico)}</strong>
        </div>
      </div>
      <div className="tecos-dash-flow__col">
        <div className="tecos-dash-flow__head" style={{ color: theme.danger }}>Egresos</div>
        <button type="button" className="tecos-dash-flow__row" onClick={() => onNav?.('nomina-gastos')}>
          <span>Nómina</span><strong>{m(fin.egresosMesNomina)}</strong>
        </button>
        <button type="button" className="tecos-dash-flow__row" onClick={() => onNav?.('nomina-gastos')}>
          <span>Gastos operativos</span><strong>{m(fin.egresosMesGastos)}</strong>
        </button>
        <button type="button" className="tecos-dash-flow__row" onClick={() => onNav?.('nomina-gastos')}>
          <span>Retiros</span><strong>{m(fin.egresosMesRetiros)}</strong>
        </button>
        <div className="tecos-dash-flow__row tecos-dash-flow__row--total">
          <span>Total egresos</span><strong style={{ color: theme.danger }}>{m(fin.egresosMes)}</strong>
        </div>
      </div>
      <div className="tecos-dash-flow__balance" style={{
        borderColor: bal >= 0 ? theme.success : theme.danger,
        background: `${bal >= 0 ? theme.success : theme.danger}12`,
      }}>
        <span>Balance del mes</span>
        <strong style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 36, color: theme.text }}>{m(fin.balanceMes)}</strong>
        <span style={{ fontSize: 11, color: theme.textDim, display: 'block', marginTop: 6 }}>{cm} − egresos {cm}</span>
      </div>
    </div>
    <p style={{ fontSize: 11, color: theme.textMute, margin: '14px 0 0', fontFamily: 'Inter, sans-serif', lineHeight: 1.5 }}>
      Acumulado histórico: todas las mensualidades y tienda confirmadas (sin filtro de mes).
    </p>
  </Card>
  );
};

const dispatchDashboardFinancialRefresh = () => {
  [
    'tecos:academy-data-sync',
    'tecos:payments-changed',
    'tecos:comprobantes-changed',
    'tecos:orders-changed',
    'tecos:finance-changed',
    'tecos:inventory-history-reset',
  ].forEach((name) => window.dispatchEvent(new CustomEvent(name)));
  if (typeof window.clearTecosHistorialDismissedStorage === 'function') {
    window.clearTecosHistorialDismissedStorage();
  }
};

/** Aviso cuando hay ingresos/órdenes en BD: el reinicio de inventario no los borra. */
const DashboardFinancialClearBanner = ({ theme, stats, onCleared }) => {
  const meta = stats?._meta || {};
  const tiendaHist = Number(stats?.ingresoAcumuladoTienda) || 0;
  const ingresoHist = Number(stats?.ingresoAcumuladoHistorico) || 0;
  const ordersConf = Number(meta.ordersConfirmadas) || 0;
  const recentN = (stats?.recentOrders || []).length;

  const hasData = ingresoHist > 0 || tiendaHist > 0 || ordersConf > 0 || recentN > 0;
  const [open, setOpen] = React.useState(false);
  const [password, setPassword] = React.useState('');
  const [confirmText, setConfirmText] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [ok, setOk] = React.useState('');

  if (!hasData) return null;

  const hasPassword = !!password.trim();
  const confirmOk = confirmText.trim().toUpperCase() === 'LIMPIAR HISTORIAL';
  const canRun = hasPassword && confirmOk;
  const clearHint = !hasPassword
    ? 'Falta la contraseña (campo 1).'
    : !confirmOk
      ? 'En el campo 2 escribe LIMPIAR HISTORIAL (no tu contraseña).'
      : '';

  const runClear = async () => {
    if (!canRun) return;
    if (!window.confirm(
      'Se borrarán pagos, órdenes confirmadas, nómina, gastos, retiros e historial de inventario. El dashboard quedará en ceros. ¿Continuar?'
    )) return;
    setBusy(true);
    setErr('');
    setOk('');
    try {
      if (typeof verifyAdminSessionPassword !== 'function') {
        throw new Error('Verificación no disponible. Recarga la página.');
      }
      await verifyAdminSessionPassword(password);
      if (typeof adminClearBillingStoreHistory !== 'function') {
        throw new Error('Función no disponible. En Supabase → SQL Editor ejecuta la migración 070.');
      }
      const res = await adminClearBillingStoreHistory(confirmText);
      const counts = res?.counts || {};
      const ordersDel = counts.orders ?? '?';
      dispatchDashboardFinancialRefresh();
      setPassword('');
      setConfirmText('');
      setOpen(false);
      setOk(`Listo: ${ordersDel} orden(es) y pagos borrados. Actualizando dashboard…`);
      if (typeof onCleared === 'function') onCleared();
    } catch (e) {
      setErr(e.message || 'No se pudo limpiar.');
    }
    setBusy(false);
  };

  return (
    <div style={{
      padding: '14px 16px', borderRadius: 12,
      border: `1px solid ${theme.warning}66`, background: `${theme.warning}14`,
      width: '100%', boxSizing: 'border-box',
    }}>
      <p style={{ margin: 0, fontSize: 13, fontFamily: 'Inter, sans-serif', color: theme.text, lineHeight: 1.55 }}>
        <strong>Hay datos de prueba en el dashboard</strong>
        {' '}(acumulado ${ingresoHist.toLocaleString('es-MX')}, tienda ${tiendaHist.toLocaleString('es-MX')}, {ordersConf} orden(es) confirmada(s)).
        {' '}El botón oculto de <em>inventario</em> no borra órdenes ni ingresos de tienda.
      </p>
      {!open ? (
        <Btn theme={theme} kind="danger" size="sm" style={{ marginTop: 12 }}
          onClick={() => { setErr(''); setOk(''); setOpen(true); }}>
          Vaciar historial financiero (dejar en ceros)
        </Btn>
      ) : (
        <div style={{ marginTop: 14, display: 'grid', gap: 10, maxWidth: 420 }}>
          <Input theme={theme} label="1. Contraseña de administrador" type="password" value={password}
            onChange={(e) => setPassword(e.target.value)} autoComplete="current-password"
            placeholder="Tu contraseña de sesión" />
          <Input theme={theme} label="2. Confirmación (frase fija)" value={confirmText}
            onChange={(e) => setConfirmText(e.target.value)} autoComplete="off"
            placeholder="LIMPIAR HISTORIAL" spellCheck={false} />
          {!canRun && clearHint && (
            <p style={{ margin: 0, fontSize: 12, color: theme.warning, fontFamily: 'Inter, sans-serif' }}>{clearHint}</p>
          )}
          {err && <p style={{ margin: 0, fontSize: 12, color: theme.danger, fontFamily: 'Inter, sans-serif' }}>{err}</p>}
          {ok && <p style={{ margin: 0, fontSize: 12, color: theme.success, fontFamily: 'Inter, sans-serif' }}>{ok}</p>}
          <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
            <Btn theme={theme} kind="ghost" size="sm" onClick={() => !busy && setOpen(false)} disabled={busy}>Cancelar</Btn>
            <Btn theme={theme} kind="danger" size="sm" onClick={runClear} disabled={!canRun || busy}>
              {busy ? 'Borrando…' : 'Confirmar limpieza'}
            </Btn>
          </div>
        </div>
      )}
    </div>
  );
};

Object.assign(window, {
  describeDashboardCurrentMonth,
  sumChartMonthsBreakdown,
  DashboardKpiGrid,
  DashboardPeriodBadge,
  DashboardPeriodOverview,
  DashboardChartMonthTable,
  DashboardPortalSyncBanner,
  DashboardKpiCard,
  DashboardSectionHead,
  DashboardAlumnosCobranza,
  DashboardIngresosEgresos,
  InventoryResetHistoryModal,
  InventoryResetPasswordModal,
  InventoryResetUnlockedButton,
  useInventoryResetUnlock,
  onInventoryResetRevealGesture,
  readInventoryResetUnlocked,
  clearInventoryResetUnlockedSession,
  DashboardFinancialClearBanner,
});
