/* global React */
// Shared atoms & molecules for Finaz C04-A

const { useState, useEffect, useRef, useMemo, createContext, useContext } = React;

// ============================================================================
//  GLOSSARY — data
// ============================================================================
const GLOSSARY = {
  multiplo_aislado: {
    term: 'Múltiplo aislado',
    category: 'concepto',
    definition: 'Múltiplo leído sin descomponerlo en drivers fundamentales; síntoma, no diagnóstico.',
    formula: 'Riesgo señal = (P/E observado / P/E teórico Gordon) sin contexto',
    example: 'Empresa con P/E 14× sin drivers asociados: el dato es titular, no análisis. Lo informativo es el gap vs teórico.',
    why_matters: 'El múltiplo aislado induce a tomar decisiones sobre apariencia de baratura/caridad sin comprobar los drivers que lo sostienen.',
    misconception: 'No: "P/E bajo = oportunidad". Sí: "P/E bajo requiere descomponer ROE, g y riesgo antes de llamarlo oportunidad o trampa".',
  },
  pe_gordon: {
    term: 'P/E teórico Gordon',
    category: 'fórmula',
    definition: 'P/E justificable por fundamentales (g, ROE, Ke) en modelo de crecimiento estable.',
    formula: 'P/E_teórico = (1 − g/ROE) / (Ke − g)',
    example: 'ROE 20%, g 5%, Ke 9% → P/E_teórico = (1 − 0,25)/(0,04) = 18,75×. Si cotiza 14× hay descuento aparente.',
    why_matters: 'Es el ancla cuantitativa que distingue múltiplo aparente de múltiplo justificado.',
    misconception: 'No es predicción de precio; es referencia de coherencia entre drivers y precio.',
  },
  driver_fundamental: {
    term: 'Driver fundamental',
    category: 'concepto',
    definition: 'Variable económica que explica el múltiplo: crecimiento sostenible, ROE, coste de capital, calidad.',
    formula: 'Drivers = { g_sostenible, ROE, Ke, calidad_earnings }',
    example: 'Sin los 4 drivers explicitados, el múltiplo no se interpreta. Damodaran: el múltiplo es output, no input.',
    why_matters: 'Disciplina el análisis: cada múltiplo tiene que poder mapearse de vuelta a una causa económica.',
    misconception: 'No basta con "creo que la empresa crece"; el driver requiere número observable o derivado.',
  },
  pe_trailing: {
    term: 'P/E trailing',
    category: 'métrica',
    definition: 'P/E sobre EPS últimos 12 meses reportados; memoria contable con todos los one-offs.',
    formula: 'P/E_TTM = Price / EPS_últimos_12m',
    example: 'Precio 80€, EPS LTM 5€ → P/E 16×. Si EPS LTM contiene plusvalía one-off 1€, el P/E_clean real es 20×.',
    why_matters: 'Es objetivo (reportado) pero arrastra ruido contable de los últimos 12 meses.',
    misconception: 'No: "trailing es más fiable porque es real". Sí: "trailing es histórico, no necesariamente representativo".',
  },
  pe_forward: {
    term: 'P/E forward',
    category: 'métrica',
    definition: 'P/E sobre EPS esperado próximos 12 meses (consenso sell-side).',
    formula: 'P/E_fwd = Price / EPS_NTM_consenso',
    example: 'Precio 80€, EPS forward 5,5€ → P/E 14,5×. Si consenso resulta 4,8€, el P/E real fue 16,7×.',
    why_matters: 'Refleja expectativas, no realidad. Necesita ser calibrado por track record del consenso.',
    misconception: 'No: "forward es más útil porque mira al futuro". Sí: "forward depende de un consenso que puede estar sistemáticamente sesgado".',
  },
  consenso_calibration: {
    term: 'Consenso sell-side calibration',
    category: 'concepto',
    definition: 'Track record histórico de cuán optimista o pesimista resulta el consenso sell-side vs realidad.',
    formula: 'Calibration = mediana(EPS_realizado / EPS_consenso) en 5+ años',
    example: 'Tech growth: consenso típicamente sobre-optimista ~10–15%. Banca: relativamente calibrado. Cíclicas: pésima calibración.',
    why_matters: 'Sin calibrar el consenso, un P/E forward 14× puede ser en realidad un P/E forward 17×.',
    misconception: 'El consenso no es "la verdad descontada"; es una distribución con sesgo sectorial.',
  },
  earnings_depurados: {
    term: 'Earnings depurados',
    category: 'cálculo',
    definition: 'Beneficio neto ajustado simétricamente por one-offs, restructuring permanente y SBC abusivo, después de impuestos.',
    formula: 'EPS_clean = (NI − one-offs_after_tax) / acciones_diluidas',
    example: 'NI 1.000M€ con plusvalía 200M€ a tasa 25% → EPS_clean usa 1.000 − 150 = 850M€.',
    why_matters: 'Comparar P/E con earnings reportados es comparar manzanas con peras maquilladas.',
    misconception: 'Ajustar solo lo que perjudica; el ajuste tiene que ser simétrico.',
  },
  sbc_abusivo: {
    term: 'SBC abusivo',
    category: 'concepto',
    definition: 'Stock-based compensation que excede ~10% revenue sostenido; coste real disfrazado de non-cash.',
    formula: 'SBC abusivo = SBC / Revenue > 10% sostenido 3 años',
    example: 'Tech con SBC 22% Revenue sostenido: dilution masiva. Restar para EPS comparable con empresa cash-comp.',
    why_matters: 'El SBC es coste económico aunque no sea coste de caja; ignorarlo infravalora el coste real del equity.',
    misconception: 'No es "non-cash neutral"; es dilución más coste de servicios.',
  },
  peg_ratio: {
    term: 'PEG ratio',
    category: 'métrica',
    definition: 'P/E dividido por tasa de crecimiento esperada de earnings en porcentaje sin signo.',
    formula: 'PEG = (P/E) / g_%',
    example: 'P/E 18× con g 15% → PEG = 1,2. Lynch llamaba oportunidad a PEG < 1 con earnings depurados y g defendible.',
    why_matters: 'Único múltiplo que normaliza precio por crecimiento — pero solo si el g del denominador es defendible.',
    misconception: 'PEG < 1 no es luz verde automática; depende del g que pongas en el denominador.',
  },
  cash_conversion: {
    term: 'Cash conversion',
    category: 'métrica',
    definition: 'Porcentaje del beneficio que se materializa en caja operativa; mide quality of earnings.',
    formula: 'Cash conversion = CFO / Net Income',
    example: 'NI 1.000M€ y CFO 950M€ → 95% (excelente). Bajo 80% sostenido invita a investigar working capital.',
    why_matters: 'Distingue beneficio contable de beneficio económico realizable.',
    misconception: 'No es "lo mismo que margen FCF"; usa CFO antes de capex.',
  },
  ev_completo: {
    term: 'Enterprise Value completo',
    category: 'fórmula',
    definition: 'Valor económico total: lo que costaría adquirir la empresa cubriendo deuda y obligaciones contractuales.',
    formula: 'EV = MktCap + Deuda neta + Minoritarios + Pensiones no fondeadas + Leases capitalizados',
    example: 'Cap 1.000M€ + deuda neta 400 + minoritarios 50 + leases 150 + pensiones 20 → EV = 1.620M€.',
    why_matters: 'Sin leases (IFRS 16) ni minoritarios subestima 5–20%; en industriales con pensiones, aún más.',
    misconception: 'No: "EV = MktCap + deuda neta". Faltan tres partidas que pueden ser materiales.',
  },
  ebitda_depurado: {
    term: 'EBITDA depurado',
    category: 'cálculo',
    definition: 'EBITDA ajustado eliminando add-backs abusivos y rentas de leases ya capitalizados.',
    formula: 'EBITDA_clean = EBITDA − add-backs_abusivos − lease_rents_doble_conteo',
    example: 'EBITDA reportado 800M€ con add-back de restructuring recurrente 80M€ → EBITDA_clean = 720M€.',
    why_matters: 'EBITDA es la palabra favorita del que vende. Si no se depura, infla la valoración.',
    misconception: 'No todos los add-backs son legítimos; el que aparece 3 años seguidos es operativo.',
  },
};

const GlossaryTooltip = ({ termId, children, displayInline = true }) => {
  const [open, setOpen] = useState(false);
  const entry = GLOSSARY[termId];
  if (!entry) return <span>{children || '???'}</span>;

  return (
    <span
      style={{ position: 'relative', display: displayInline ? 'inline-flex' : 'block' }}
      onMouseEnter={() => setOpen(true)}
      onMouseLeave={() => setOpen(false)}
    >
      <button
        className="glossary-trigger"
        aria-describedby={`tt-${termId}`}
        onClick={() => setOpen(o => !o)}
        style={{ display: 'inline-flex', alignItems: 'center', gap: 3, lineHeight: 1.2 }}
      >
        {children || entry.term}
        <span className="i-icon" aria-hidden="true">i</span>
      </button>
      {open && (
        <span
          id={`tt-${termId}`}
          role="tooltip"
          style={{
            position: 'absolute',
            top: 'calc(100% + 6px)',
            left: 0,
            zIndex: 50,
            width: 320,
            background: 'var(--surface-2)',
            border: '1px solid var(--border-strong)',
            borderRadius: 'var(--r-md)',
            padding: '12px 14px',
            boxShadow: '0 8px 24px rgba(0,0,0,0.4)',
            fontFamily: 'var(--font-text)',
            fontSize: 'var(--size-sm)',
            color: 'var(--text-primary)',
            lineHeight: 1.45,
            textAlign: 'left',
          }}
        >
          <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, marginBottom: 6 }}>
            <span style={{ fontWeight: 600 }}>{entry.term}</span>
            <span className="badge" style={{ alignSelf: 'flex-start' }}>{entry.category}</span>
          </div>
          <div style={{ color: 'var(--text-secondary)', marginBottom: 8 }}>{entry.definition}</div>
          {entry.formula && (
            <div className="mono" style={{ fontSize: 11, color: 'var(--signal-driver)', padding: '6px 8px', background: 'var(--surface-1)', borderRadius: 4, marginBottom: 6 }}>
              {entry.formula}
            </div>
          )}
          <div style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>
            Pulsa para ver flashcard completa
          </div>
        </span>
      )}
    </span>
  );
};

// ============================================================================
//  VOICE CARD
// ============================================================================
const VOICE_META = {
  sofia: { name: 'Sofía',        role: 'Narradora',   short: 'SO' },
  mateo: { name: 'Mateo',        role: 'Cuestionamiento', short: 'MA' },
  tutor: { name: 'Tutor Finaz',  role: 'Evaluación',  short: 'TF' },
};

const VoiceCard = ({ speaker = 'sofia', cue, text, playing = false }) => {
  const m = VOICE_META[speaker];
  return (
    <div className={`voice-card voice-${speaker} ${playing ? 'playing' : ''}`}>
      <div className="voice-avatar" aria-label={m.name}>
        <span className={`voice-glyph ${speaker}`} aria-hidden="true" />
      </div>
      <div>
        <div className="voice-meta">
          <span className="voice-name">
            {m.name}
            <span className="txt-ter" style={{ fontWeight: 400, marginLeft: 6, fontSize: 11 }}>· {m.role}</span>
          </span>
          {cue && <span className="voice-cue">{cue}</span>}
        </div>
        <div className="voice-text">{text}</div>
      </div>
    </div>
  );
};

// ============================================================================
//  RUBRIC METER
// ============================================================================
const RubricMeter = ({ rubric, threshold = 75 }) => (
  <div>
    {rubric.map((r, i) => {
      const pct = Math.max(0, Math.min(100, r.score));
      const tone = pct >= threshold ? 'pass' : pct >= 50 ? 'warn' : 'fail';
      return (
        <div className="rubric-row" key={i}>
          <div className="rubric-label">
            <span>{r.label}</span>
            <span className="rubric-value">{Math.round(pct)} / 100</span>
          </div>
          <div className="rubric-bar">
            <div className={`rubric-fill ${tone}`} style={{ width: `${pct}%` }} />
            <div className="rubric-threshold" style={{ left: `${threshold}%` }} title={`Threshold ${threshold}`} />
          </div>
        </div>
      );
    })}
  </div>
);

// ============================================================================
//  WATERMARK
// ============================================================================
const Watermark = ({ label = 'Dataset sintético v2.0' }) => (
  <span className="watermark">{label}</span>
);

// ============================================================================
//  BANNER
// ============================================================================
const Banner = ({ tone = 'driver', icon = 'i', title, text, action }) => (
  <div className={`banner banner-${tone}`}>
    <span className="banner-icon">{icon}</span>
    <div>
      {title && <div className="banner-title">{title}</div>}
      <div className="banner-text">{text}</div>
    </div>
    {action && <div>{action}</div>}
  </div>
);

// ============================================================================
//  DISCLAIMER FOOTER
// ============================================================================
const DisclaimerFooter = ({ expanded = false }) => (
  <div className="disclaimer">
    {expanded
      ? "Material educativo. No constituye recomendación de inversión, asesoramiento financiero, ni invitación a comprar o vender ningún instrumento. Datasets sintéticos. Las decisiones de inversión son responsabilidad de quien las toma."
      : "Material educativo · No es recomendación de inversión · Datasets sintéticos"
    }
  </div>
);

// ============================================================================
//  EPISODE HEADER (sticky)
// ============================================================================
const EpisodeHeader = ({ breadcrumb, title, bigQuestion, durationDone = 14, durationTotal = 30, onPause }) => (
  <div className="sticky-header" style={{ padding: '12px 24px' }}>
    <div style={{ display: 'grid', gridTemplateColumns: '1fr auto', alignItems: 'center', gap: 16 }}>
      <div>
        <div className="row" style={{ gap: 8, marginBottom: 4 }}>
          <span className="txt-xs txt-ter mono">{breadcrumb}</span>
          <span className="txt-ter">·</span>
          <span className="chip chip-pass" style={{ fontSize: 10 }}>S1 ✓</span>
          <span className="chip chip-pass" style={{ fontSize: 10 }}>S2 ✓</span>
          <span className="chip chip-active" style={{ fontSize: 10 }}>S3 actual</span>
        </div>
        <h1 className="serif" style={{ fontSize: 'var(--size-xl)', margin: 0, fontWeight: 500, lineHeight: 1.2 }}>
          {title}
        </h1>
        {bigQuestion && (
          <p className="serif italic txt-sec" style={{ margin: '2px 0 0 0', fontSize: 'var(--size-sm)' }}>
            “{bigQuestion}”
          </p>
        )}
      </div>
      <div className="row" style={{ gap: 12 }}>
        <div className="row mono txt-xs txt-sec" style={{ gap: 6 }}>
          <svg width="14" height="14" viewBox="0 0 16 16" fill="none">
            <circle cx="8" cy="8" r="6.5" stroke="currentColor"/>
            <path d="M8 4v4l3 2" stroke="currentColor" strokeLinecap="round"/>
          </svg>
          <span>{durationDone}:00 / {durationTotal}:00</span>
        </div>
        <button className="btn btn-ghost" aria-label="Pausa" onClick={onPause}>
          <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><rect x="4" y="3" width="3" height="10" rx="1"/><rect x="9" y="3" width="3" height="10" rx="1"/></svg>
          Pausar
        </button>
        <button className="btn btn-ghost" aria-label="Tema">
          <svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M8 2a6 6 0 1 0 5.2 9A6 6 0 0 1 8 2z" stroke="currentColor" strokeLinejoin="round"/></svg>
        </button>
        <div style={{ width: 28, height: 28, borderRadius: '50%', background: 'linear-gradient(135deg, var(--voice-mateo), var(--voice-sofia))', display: 'grid', placeItems: 'center', fontSize: 11, fontWeight: 600 }}>JM</div>
      </div>
    </div>
  </div>
);

// ============================================================================
//  STEP RAIL
// ============================================================================
const STEPS = [
  { id: 'S1_hook',    label: 'S1 · Hook',        cue: 'story_panel',     mins: '0:00–4:00' },
  { id: 'S2_concept', label: 'S2 · Concepto',    cue: 'formula anotada', mins: '4:00–10:00' },
  { id: 'S3_lab',     label: 'S3 · Lab',         cue: 'CALC',            mins: '10:00–24:00' },
  { id: 'S4_transfer',label: 'S4 · Transferencia', cue: 'mini-memo',     mins: '24:00–30:00' },
];

const StepRail = ({ current = 'S3_lab', completed = ['S1_hook','S2_concept'], onChange }) => (
  <ol className="step-rail" role="list" aria-label="Pasos del episodio">
    {STEPS.map((s, i) => {
      const isDone = completed.includes(s.id);
      const isCurrent = s.id === current;
      const isLocked = !isDone && !isCurrent;
      return (
        <li
          key={s.id}
          className={`step-rail-item ${isDone ? 'done' : ''} ${isCurrent ? 'current' : ''} ${isLocked ? 'locked' : ''}`}
          aria-current={isCurrent ? 'step' : undefined}
          onClick={() => !isLocked && onChange && onChange(s.id)}
        >
          <span className="step-rail-marker">{isDone ? '✓' : i + 1}</span>
          <span>
            <div className="step-rail-label">{s.label}</div>
            <div className="step-rail-meta">{s.cue} · {s.mins}</div>
          </span>
        </li>
      );
    })}
  </ol>
);

// ============================================================================
//  GLOSSARY RAIL
// ============================================================================
const GlossaryRail = ({ terms = [], active }) => (
  <div className="col" style={{ gap: 4 }}>
    <div className="txt-xs txt-ter" style={{ textTransform: 'uppercase', letterSpacing: '0.05em', padding: '0 8px', marginBottom: 6 }}>Glosario</div>
    {terms.map(t => {
      const isActive = t === active;
      const entry = GLOSSARY[t];
      return (
        <button
          key={t}
          className="row"
          style={{
            gap: 10, padding: '8px 10px', borderRadius: 'var(--r-md)',
            background: isActive ? 'var(--accent-muted)' : 'transparent',
            border: `1px solid ${isActive ? 'rgba(107,163,255,0.35)' : 'transparent'}`,
            textAlign: 'left',
            transition: 'background .15s',
          }}
        >
          <span className="i-icon" style={{ display: 'inline-grid', placeItems: 'center', width: 20, height: 20, borderRadius: '50%', background: isActive ? 'var(--accent)' : 'var(--surface-3)', color: isActive ? 'var(--surface-0)' : 'var(--text-tertiary)', fontFamily: 'var(--font-mono)', fontSize: 11 }}>i</span>
          <span style={{ flex: 1, fontSize: 'var(--size-sm)', color: isActive ? 'var(--text-primary)' : 'var(--text-secondary)' }}>
            {entry?.term || t}
          </span>
          {isActive && <span className="dot-pulse" />}
        </button>
      );
    })}
  </div>
);

// ============================================================================
//  AUDIO PLAYER (visual mock)
// ============================================================================
const AudioPlayer = ({ played = 0.42, duration = '04:12', playing = false }) => {
  const bars = 60;
  return (
    <div className="audio-player">
      <button className="audio-play-btn" aria-label={playing ? 'Pausar' : 'Reproducir'}>
        {playing
          ? <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><rect x="4" y="3" width="3" height="10" rx="1"/><rect x="9" y="3" width="3" height="10" rx="1"/></svg>
          : <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M4 3l9 5-9 5V3z"/></svg>
        }
      </button>
      <div className="audio-waveform" aria-hidden="true">
        {Array.from({ length: bars }, (_, i) => {
          const h = 5 + Math.abs(Math.sin(i * 0.7) * 18) + (i % 3) * 2;
          return (
            <span
              key={i}
              className={`bar ${i / bars < played ? 'played' : ''}`}
              style={{ height: `${h}px` }}
            />
          );
        })}
      </div>
      <span className="audio-time">{Math.round(played * 60 * 4)}s / {duration}</span>
    </div>
  );
};

// ============================================================================
//  EXPOSE
// ============================================================================
Object.assign(window, {
  GLOSSARY,
  VOICE_META,
  STEPS,
  GlossaryTooltip,
  VoiceCard,
  RubricMeter,
  Watermark,
  Banner,
  DisclaimerFooter,
  EpisodeHeader,
  StepRail,
  GlossaryRail,
  AudioPlayer,
});
