/* eslint-disable */
/* Finaz · C03-A Tendencia — course player (final)
 * Dark chart theme. Reuses the chart engine + lab screens from C03-UI-ideas.
 * Hash routing: #/ , #/m/M01 , #/m/M01/e/E01 , #/m/M01/e/E01/s/S2 , #/glossary
 */

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

const STORAGE_KEY = "__fz_progress_tendencia";

/* ── progress ─────────────────────────────────────────────── */
function useProgress() {
  const [p, setP] = useState(() => {
    try { return JSON.parse(localStorage.getItem(STORAGE_KEY) || "{}"); } catch { return {}; }
  });
  useEffect(() => { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(p)); } catch {} }, [p]);
  const mark = useCallback((epKey, status, extra = {}) => {
    setP(prev => ({ ...prev, [epKey]: { ...(prev[epKey] || {}), status, last: Date.now(), ...extra } }));
  }, []);
  return [p, mark];
}

/* ── hash router ──────────────────────────────────────────── */
function parseHash() {
  const h = (location.hash || "#/").replace(/^#/, "");
  const parts = h.split("/").filter(Boolean);
  if (parts[0] === "m" && parts[2] === "e")
    return { route: "episode", modId: parts[1], epId: parts[3], stepId: parts[5] || "S1" };
  if (parts[0] === "m") return { route: "module", modId: parts[1] };
  if (parts[0] === "glossary") return { route: "glossary" };
  return { route: "home" };
}
function useHashRoute() {
  const [r, setR] = useState(parseHash());
  useEffect(() => {
    const onH = () => { setR(parseHash()); window.scrollTo(0, 0); };
    window.addEventListener("hashchange", onH);
    return () => window.removeEventListener("hashchange", onH);
  }, []);
  return r;
}
function go(path) { location.hash = "#" + path; }

/* ── glossary popover ─────────────────────────────────────── */
const GlossaryCtx = createContext(null);

function GlossaryProvider({ glossary, children }) {
  const [target, setTarget] = useState(null);
  const byId = useMemo(() => { const m = {}; (glossary || []).forEach(g => m[g.id] = g); return m; }, [glossary]);
  const open = useCallback((id, el) => setTarget({ id, el }), []);
  const close = useCallback(() => setTarget(null), []);
  useEffect(() => {
    if (!target) return;
    const onKey = e => { if (e.key === "Escape") close(); };
    const onClick = e => {
      const pop = document.getElementById("__tc-gloss-pop");
      if (pop && pop.contains(e.target)) return;
      if (target.el && target.el.contains(e.target)) return;
      close();
    };
    document.addEventListener("keydown", onKey);
    document.addEventListener("pointerdown", onClick);
    window.addEventListener("hashchange", close);
    return () => {
      document.removeEventListener("keydown", onKey);
      document.removeEventListener("pointerdown", onClick);
      window.removeEventListener("hashchange", close);
    };
  }, [target, close]);
  return (
    <GlossaryCtx.Provider value={{ open, close, byId }}>
      {children}
      {target && byId[target.id] && <GlossPopover entry={byId[target.id]} anchor={target.el} onClose={close}/>}
    </GlossaryCtx.Provider>
  );
}

function GlossPopover({ entry, anchor, onClose }) {
  const ref = useRef(null);
  const [pos, setPos] = useState({ left: -9999, top: -9999 });
  useEffect(() => {
    if (!anchor || !ref.current) return;
    const r = anchor.getBoundingClientRect();
    const pw = ref.current.offsetWidth, ph = ref.current.offsetHeight;
    let left = r.left + r.width / 2 - pw / 2;
    let top = r.bottom + 8;
    left = Math.max(12, Math.min(left, window.innerWidth - pw - 12));
    if (top + ph + 12 > window.innerHeight) top = Math.max(12, r.top - ph - 8);
    setPos({ left, top });
  }, [anchor]);
  return (
    <div id="__tc-gloss-pop" className="tc-gloss-pop" ref={ref} style={{ left: pos.left, top: pos.top }}>
      <div className="tc-gloss-pop-head">
        <h4>{entry.term}</h4>
        <button onClick={onClose} aria-label="Cerrar">×</button>
      </div>
      {entry.category && <div className="tc-gloss-pop-cat">{entry.category.replace(/_/g, " ")}</div>}
      <p className="tc-gloss-pop-def">{entry.definition}</p>
      {entry.why && <div className="tc-gloss-pop-row"><span className="k">Por qué importa</span><div className="v">{entry.why}</div></div>}
      {entry.miscon && <div className="tc-gloss-pop-row"><span className="k">Cuidado</span><div className="v">{entry.miscon}</div></div>}
    </div>
  );
}

function GlossTerm({ id, children }) {
  const ctx = useContext(GlossaryCtx);
  const ref = useRef(null);
  if (!ctx || !ctx.byId[id]) return <span>{children}</span>;
  return (
    <span ref={ref} className="tc-gloss" tabIndex={0} role="button"
          onMouseEnter={e => ctx.open(id, e.currentTarget)}
          onClick={e => { e.stopPropagation(); ctx.open(id, e.currentTarget); }}
          onFocus={e => ctx.open(id, e.currentTarget)}>
      {children}
    </span>
  );
}

function linkGlossary(text, triggers, keyPrefix) {
  // Wrap glossary terms in a plain text segment.
  if (!triggers.length) return [<Fragment key={keyPrefix}>{text}</Fragment>];
  const esc = s => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  const alts = [];
  triggers.forEach(t => {
    alts.push("\\b" + esc(t.term) + "\\b");
    const paren = t.term.match(/\(([^)]+)\)/);
    if (paren) alts.push("\\b" + esc(paren[1]) + "\\b");
  });
  const pattern = new RegExp("(" + alts.join("|") + ")", "gi");
  return String(text).split(pattern).map((p, i) => {
    const match = triggers.find(t => {
      if (t.term.toLowerCase() === p.toLowerCase()) return true;
      const paren = t.term.match(/\(([^)]+)\)/);
      return paren && paren[1].toLowerCase() === p.toLowerCase();
    });
    return match
      ? <GlossTerm key={`${keyPrefix}-${i}`} id={match.id}>{p}</GlossTerm>
      : <Fragment key={`${keyPrefix}-${i}`}>{p}</Fragment>;
  });
}

function AutoGloss({ text, triggerIds }) {
  const ctx = useContext(GlossaryCtx);
  if (!text) return null;
  const triggers = ctx ? (triggerIds || []).map(id => ctx.byId[id]).filter(Boolean) : [];
  triggers.sort((a, b) => b.term.length - a.term.length);
  // First handle markdown **bold**; render bold segments as <strong>,
  // apply glossary linking inside every non-bold segment.
  const out = [];
  String(text).split(/(\*\*[^*]+\*\*)/).forEach((seg, i) => {
    if (/^\*\*[^*]+\*\*$/.test(seg)) {
      out.push(<strong key={`b-${i}`}>{seg.slice(2, -2)}</strong>);
    } else if (seg) {
      out.push(...linkGlossary(seg, triggers, `s-${i}`));
    }
  });
  return out;
}

/* ── progress helpers ─────────────────────────────────────── */
function modProgress(progress, mod) {
  let done = 0;
  mod.episodes.forEach(e => { if ((progress[`${mod.id}.${e.id}`] || {}).status === "done") done++; });
  return { done, total: mod.episodes.length, pct: mod.episodes.length ? Math.round(done / mod.episodes.length * 100) : 0 };
}
function courseProgress(progress, course) {
  let done = 0, total = 0;
  course.modules.forEach(m => m.episodes.forEach(e => {
    total++; if ((progress[`${m.id}.${e.id}`] || {}).status === "done") done++;
  }));
  return { done, total, pct: total ? Math.round(done / total * 100) : 0 };
}

/* ── lab dispatcher: resource_type/module → chart lab ──────── */
function pickLab(mod, ep) {
  const rt = ep.resource_type || "";
  // Capstone & integral case
  if (rt.includes("CAPSTONE")) return { Comp: window.P24_Capstone, name: "Capstone Multi-TF" };
  if (mod.id === "M08") return { Comp: window.P20i_MultiTF, name: "Análisis Multi-TF" };
  // M00 diagnostic → gallery
  if (rt.includes("CASE-X")) return { Comp: window.P20a_Gallery, name: "Galería diagnóstico" };
  // Bar replay (S1 simulations)
  if (rt.includes("S1")) return { Comp: window.P20g_BarReplay, name: "Bar Replay" };
  // Trendline / channels / S-R drawing modules
  if (mod.id === "M02" || mod.id === "M03" || mod.id === "M04") {
    const seedMap = { M02: 7, M03: 19, M04: 31 };
    const props = { seed: seedMap[mod.id] + (ep.global_idx || 0), goal: 6, showPivotGuides: mod.id !== "M02" };
    if (mod.id === "M03") props.unlockedToolsBeyond = { M03: true };
    if (mod.id === "M04") props.unlockedToolsBeyond = { M03: true, M04: true };
    return { Comp: window.P20c_TrendlineLab, name: mod.id === "M02" ? "Trendline Lab" : mod.id === "M03" ? "Canales Lab" : "S/R Lab", props };
  }
  // M07 retrocesos → paper trading (confluence) or trendline lab
  if (mod.id === "M07") {
    if (rt.includes("S2")) return { Comp: window.P20h_PaperTrading, name: "Paper Trading" };
    return { Comp: window.P20c_TrendlineLab, name: "Retrocesos Lab", props: { seed: 44 + (ep.global_idx || 0), goal: 5, unlockedToolsBeyond: { M03: true, M04: true, M07: true } } };
  }
  // M01, M05, M06 → classification / breakout spotting
  if (mod.id === "M05") return { Comp: window.P20g_BarReplay, name: "Bar Replay · rupturas", props: { seed: 60 + (ep.global_idx || 0) } };
  // default → rapid classifier
  return { Comp: window.P20b_RapidClassifier, name: "Clasificador rápido", props: { total: 12 } };
}

/* ── TopBar ───────────────────────────────────────────────── */
function TopBar({ course, route, progress }) {
  const cp = courseProgress(progress, course);
  const crumbs = [{ label: "C03-A", href: "#/" }];
  if (route.route === "module" || route.route === "episode") {
    const m = course.modules.find(x => x.id === route.modId);
    if (m) crumbs.push({ label: `${m.id} · ${m.title}`, href: `#/m/${m.id}` });
    if (route.route === "episode" && m) {
      const e = m.episodes.find(x => x.id === route.epId);
      if (e) crumbs.push({ label: `${e.id} · ${e.title}`, href: `#/m/${m.id}/e/${e.id}` });
    }
  } else if (route.route === "glossary") {
    crumbs.push({ label: "Glosario", href: "#/glossary" });
  }
  return (
    <header className="tc-topbar">
      <a className="tc-brand" href="https://cursos2.iort.io/" aria-label="cursos2.iort.io">
        <span className="tc-brand-mark">F</span>
        <span className="tc-brand-name">Finaz</span>
        <span className="tc-brand-code">C03-A · Análisis Técnico</span>
      </a>
      <div className="tc-crumbs">
        {crumbs.map((c, i) => (
          <Fragment key={i}>
            <span className="sep">/</span>
            {i === crumbs.length - 1 ? <span className="now">{c.label}</span> : <a href={c.href}>{c.label}</a>}
          </Fragment>
        ))}
      </div>
      <div className="tc-topbar-right">
        <div className="tc-progress" title={`${cp.done}/${cp.total} episodios`}>
          <span className="tc-kicker">Mastery</span>
          <div className="tc-progress-bar"><div className="fill" style={{ width: `${cp.pct}%` }}/></div>
          <span className="mono">{cp.pct}%</span>
        </div>
        <button className="tc-btn" onClick={() => go("/glossary")}>Glosario · {course.glossary.length}</button>
      </div>
    </header>
  );
}

/* ── Course Home ──────────────────────────────────────────── */
function CourseHome({ course, progress }) {
  const cp = courseProgress(progress, course);
  let cont = null;
  for (const m of course.modules) {
    for (const e of m.episodes) {
      if ((progress[`${m.id}.${e.id}`] || {}).status !== "done") { cont = { m, e }; break; }
    }
    if (cont) break;
  }
  return (
    <>
      <section className="tc-hero">
        <span className="tc-eyebrow"><span className="dot"/> {course.code} · {course.level} · {course.duration}</span>
        <h1 className="tc-h1">{course.title}</h1>
        <p className="tc-lede">{course.subtitle}</p>
        <div className="tc-promise">{course.promise}</div>
        {course.prerequisites && course.prerequisites.length > 0 && (
          <div className="tc-prereq">Prerrequisito · {course.prerequisites.join(" · ")}</div>
        )}
        <div className="tc-stats">
          <div className="tc-stat"><span className="n">{course.n_modules}</span><span className="l">Módulos</span></div>
          <div className="tc-stat"><span className="n">{course.n_episodes}</span><span className="l">Episodios</span></div>
          <div className="tc-stat"><span className="n">{course.n_glossary}</span><span className="l">Términos</span></div>
          <div className="tc-stat"><span className="n">{cp.pct}%</span><span className="l">Completado</span></div>
        </div>
        <div className="tc-cta">
          {cont && (
            <button className="tc-btn-primary" onClick={() => go(`/m/${cont.m.id}/e/${cont.e.id}`)}>
              {cp.done > 0 ? "Continuar" : "Empezar diagnóstico"} · {cont.m.id}·{cont.e.id} <span className="arr">→</span>
            </button>
          )}
          <button className="tc-btn" onClick={() => go("/glossary")}>Glosario técnico</button>
        </div>
      </section>

      <section className="tc-section">
        <div className="tc-section-head">
          <h2>Ruta del curso</h2>
          <span className="meta">{course.n_modules} módulos · {course.n_episodes} episodios</span>
        </div>
        <div className="tc-mod-grid">
          {course.modules.map((m, idx) => {
            const mp = modProgress(progress, m);
            const prevDone = idx === 0 || modProgress(progress, course.modules[idx - 1]).done > 0;
            const lock = !prevDone && mp.done === 0;
            let status = mp.pct === 100 ? "done" : mp.done > 0 ? "now" : lock ? "lock" : "next";
            return (
              <div key={m.id} className={`tc-mod-card ${status}`}
                   onClick={() => !lock && go(`/m/${m.id}`)} role="button" tabIndex={lock ? -1 : 0}>
                <div className="tc-mod-head">
                  <span className="tc-mod-id">{m.id} · {m.episodes.length} ep.</span>
                  <span className={`tc-mod-status ${status}`}>
                    <span className="dot"/>
                    {status === "done" ? "Completo" : status === "now" ? "En curso" : status === "lock" ? "Bloqueado" : "Disponible"}
                  </span>
                </div>
                <h3>{m.title}</h3>
                <p>{m.subtitle}</p>
                <div className="tc-mod-bar"><div className={`fill ${status}`} style={{ width: `${mp.pct}%` }}/></div>
                <div className="tc-mod-meta"><span>{mp.done} / {mp.total} ep.</span><span>{mp.pct}%</span></div>
              </div>
            );
          })}
        </div>
      </section>
    </>
  );
}

/* ── Module Home ──────────────────────────────────────────── */
function ModuleHome({ course, modId, progress }) {
  const mod = course.modules.find(m => m.id === modId);
  if (!mod) return <div className="tc-empty">Módulo no encontrado.</div>;
  const mp = modProgress(progress, mod);
  const rtClass = rt => rt.includes("GATE") ? "gate" : rt.includes("CAPSTONE") ? "capstone" : rt.includes("CALC") ? "calc" : "";
  return (
    <>
      <section className="tc-hero">
        <span className="tc-eyebrow">Módulo {mod.id} · {course.code}</span>
        <h1 className="tc-h1">{mod.title}</h1>
        <p className="tc-lede">{mod.subtitle}</p>
        <div className="tc-stats">
          <div className="tc-stat"><span className="n">{mod.episodes.length}</span><span className="l">Episodios</span></div>
          <div className="tc-stat"><span className="n">{mp.done}</span><span className="l">Completados</span></div>
          <div className="tc-stat"><span className="n">{mp.pct}%</span><span className="l">Mastery módulo</span></div>
        </div>
        <div className="tc-cta"><button className="tc-btn" onClick={() => go("/")}>← Ruta del curso</button></div>
      </section>
      <section className="tc-section">
        <div className="tc-section-head"><h2>Episodios</h2><span className="meta">{mod.episodes.length} episodios</span></div>
        <div className="tc-ep-list">
          {mod.episodes.map((ep, idx) => {
            const st = (progress[`${mod.id}.${ep.id}`] || {}).status;
            const prev = idx === 0 ? null : mod.episodes[idx - 1];
            const unlocked = idx === 0 || (prev && progress[`${mod.id}.${prev.id}`]?.status === "done");
            const lock = !unlocked && st !== "done";
            const cls = st === "done" ? "done" : (unlocked ? "now" : "next");
            return (
              <div key={ep.id} className={`tc-ep-row ${cls}${lock ? " lock" : ""}`}
                   onClick={() => !lock && go(`/m/${mod.id}/e/${ep.id}`)}>
                <div className={`tc-ep-icon ${st === "done" ? "done" : lock ? "lock" : "now"}`}>
                  {st === "done" ? "✓" : lock ? "🔒" : ep.id}
                </div>
                <div className="tc-ep-info">
                  <h4>{ep.title}</h4>
                  <div className="tc-ep-meta">
                    <span className={`tc-rt ${rtClass(ep.resource_type)}`}>{ep.resource_type}</span>
                    <span>{ep.duration_min} min</span>
                    {ep.glossary_triggers.length > 0 && <span>{ep.glossary_triggers.length} términos</span>}
                  </div>
                </div>
                <div className="tc-ep-cta">{lock ? "Bloqueado" : st === "done" ? "Repasar" : "Abrir"} →</div>
              </div>
            );
          })}
        </div>
      </section>
    </>
  );
}

/* ── Episode Player ───────────────────────────────────────── */
const STEPS = [
  { id: "S1", label: "Hook", desc: "Story panel" },
  { id: "S2", label: "Concepto", desc: "Regla anotada" },
  { id: "S3", label: "Lab", desc: "Práctica con gráfico" },
  { id: "S4", label: "Transfer", desc: "Entregable" },
];

function EpisodePlayer({ course, modId, epId, stepId, progress, mark }) {
  const mod = course.modules.find(m => m.id === modId);
  const ep = mod && mod.episodes.find(e => e.id === epId);
  if (!ep) return <div className="tc-empty">Episodio no encontrado.</div>;
  const epKey = `${mod.id}.${ep.id}`;
  const epProg = progress[epKey] || {};
  const completed = epProg.steps || {};
  const stepIdx = Math.max(0, STEPS.findIndex(s => s.id === stepId));
  const step = STEPS[stepIdx];

  const goStep = s => go(`/m/${mod.id}/e/${ep.id}/s/${s}`);
  const markStep = useCallback(() => {
    if (!completed[step.id])
      mark(epKey, epProg.status || "now", { steps: { ...completed, [step.id]: true } });
  }, [step.id, completed, epKey, epProg.status, mark]);

  const next = () => {
    markStep();
    if (stepIdx < STEPS.length - 1) { goStep(STEPS[stepIdx + 1].id); return; }
    mark(epKey, "done", { steps: { ...completed, S4: true } });
    const i = mod.episodes.findIndex(e => e.id === ep.id) + 1;
    if (i < mod.episodes.length) go(`/m/${mod.id}/e/${mod.episodes[i].id}`);
    else go(`/m/${mod.id}`);
  };
  const prev = () => { if (stepIdx > 0) goStep(STEPS[stepIdx - 1].id); else go(`/m/${mod.id}`); };

  return (
    <>
      <section className="tc-hero tc-hero-ep">
        <span className="tc-eyebrow">{mod.id} · {ep.id} · {ep.resource_type}</span>
        <h1 className="tc-h1 tc-h1-ep">{ep.title}</h1>
        <p className="tc-lede">{ep.objective}</p>
      </section>

      <div className="tc-ep-shell">
        <aside className="tc-rail">
          <h5>Flujo del episodio</h5>
          <div className="tc-rail-meta">{ep.duration_min} min · {ep.resource_type}</div>
          {STEPS.map(s => {
            const done = !!completed[s.id];
            const active = s.id === step.id;
            return (
              <div key={s.id} className={`tc-step${active ? " active" : ""}${done ? " done" : ""}`}
                   onClick={() => goStep(s.id)} role="button" tabIndex={0}>
                <span className="marker">{done ? "✓" : s.id}</span>
                <span className="lbl">
                  <span className="t">{s.label}</span>
                  <span className="d">{s.desc}</span>
                </span>
              </div>
            );
          })}
          <button className="tc-rail-back" onClick={() => go(`/m/${mod.id}`)}>← Volver al módulo</button>
        </aside>

        <main className="tc-ep-main">
          {step.id === "S1" && <StageHook ep={ep} markStep={markStep}/>}
          {step.id === "S2" && <StageConcept ep={ep} markStep={markStep}/>}
          {step.id === "S3" && <StageLab mod={mod} ep={ep} markStep={markStep}/>}
          {step.id === "S4" && <StageTransfer ep={ep} epKey={epKey} progress={progress} mark={mark} markStep={markStep}/>}
          <div className="tc-stage-nav">
            <button className="tc-btn" onClick={prev}>← {stepIdx === 0 ? "Módulo" : STEPS[stepIdx - 1].label}</button>
            <button className="tc-btn-primary" onClick={next}>
              {stepIdx === STEPS.length - 1 ? "Completar episodio" : `Continuar → ${STEPS[stepIdx + 1].label}`}
            </button>
          </div>
        </main>
      </div>
    </>
  );
}

/* ── S1 Hook ──────────────────────────────────────────────── */
function StageHook({ ep, markStep }) {
  const lines = ep.voice_script || [];
  const [idx, setIdx] = useState(0);
  useEffect(() => { if (idx >= lines.length - 1) markStep(); }, [idx, lines.length, markStep]);
  const initials = s => (s || "").split(/\s+/).map(w => w[0]).join("").slice(0, 2).toUpperCase();
  return (
    <div className="tc-stage">
      <div className="tc-stage-head"><h2>S1 · Hook</h2><span className="tc-stage-kick">Story panel</span></div>
      {ep.objective && <div className="tc-objective"><b>Objetivo:</b> {ep.objective}</div>}
      <div className="tc-voices">
        {lines.length === 0 && <div className="tc-tip">Este episodio no tiene guion de locución.</div>}
        {lines.slice(0, idx + 1).map((l, i) => (
          <div key={i} className={`tc-voice${i === idx ? " current" : " played"}`}>
            <div className={`tc-voice-av ${l.key}`}>{initials(l.speaker)}</div>
            <div className="tc-voice-body">
              <div className="tc-voice-name">{l.speaker}</div>
              <p className="tc-voice-text"><AutoGloss text={l.text} triggerIds={ep.glossary_triggers}/></p>
              {l.cue && <span className="tc-voice-cue">[ {l.cue} ]</span>}
            </div>
          </div>
        ))}
      </div>
      {idx < lines.length - 1 ? (
        <button className="tc-btn" onClick={() => setIdx(i => Math.min(i + 1, lines.length - 1))}>
          Siguiente intervención ({idx + 1}/{lines.length}) →
        </button>
      ) : lines.length > 0 && (
        <div className="tc-tip">Guion completo. Continúa al S2 para ver la regla operativa en frío.</div>
      )}
    </div>
  );
}

/* ── S2 Concept ───────────────────────────────────────────── */
function StageConcept({ ep, markStep }) {
  useEffect(() => { markStep(); }, [markStep]);
  const ctx = useContext(GlossaryCtx);
  return (
    <div className="tc-stage">
      <div className="tc-stage-head"><h2>S2 · Concepto</h2><span className="tc-stage-kick">Regla operativa</span></div>
      {ep.objectives && ep.objectives.length > 0 && (
        <div className="tc-block-list">
          <span className="tc-kicker">Objetivos de aprendizaje</span>
          <ul>{ep.objectives.map((o, i) => <li key={i}><AutoGloss text={o} triggerIds={ep.glossary_triggers}/></li>)}</ul>
        </div>
      )}
      {/* The tutor's voice line usually carries the "regla del episodio" */}
      {(() => {
        const tutorLine = (ep.voice_script || []).find(l => l.key === "tutor");
        return tutorLine ? (
          <div className="tc-rule">
            <span className="tc-kicker">Regla del episodio · Tutor Finaz</span>
            <p><AutoGloss text={tutorLine.text} triggerIds={ep.glossary_triggers}/></p>
          </div>
        ) : null;
      })()}
      {ep.glossary_triggers && ep.glossary_triggers.length > 0 && (
        <div style={{ marginTop: 18 }}>
          <span className="tc-kicker">Términos del episodio · pulsa para ver tarjeta</span>
          <div className="tc-chip-row">
            {ep.glossary_triggers.map(gid => {
              const e = ctx && ctx.byId[gid];
              return e ? <GlossTerm key={gid} id={gid}>{e.term}</GlossTerm> : null;
            })}
          </div>
        </div>
      )}
      {ep.success_criteria && (
        <div className="tc-tip tc-tip-accent" style={{ marginTop: 18 }}>
          <b>Mastery del episodio:</b> {ep.success_criteria}
        </div>
      )}
    </div>
  );
}

/* ── S3 Lab ───────────────────────────────────────────────── */
function StageLab({ mod, ep, markStep }) {
  useEffect(() => { markStep(); }, [markStep]);
  const { Comp, name, props } = useMemo(() => pickLab(mod, ep), [mod.id, ep.id]);
  return (
    <div className="tc-stage tc-stage-lab">
      <div className="tc-stage-head">
        <h2>S3 · Lab</h2>
        <span className="tc-stage-kick">{name} · {ep.resource_type}</span>
      </div>
      {ep.exercise && ep.exercise.prompt && (
        <div className="tc-objective" style={{ marginBottom: 14 }}>
          <b>Tarea:</b> <AutoGloss text={ep.exercise.prompt} triggerIds={ep.glossary_triggers}/>
        </div>
      )}
      <div className="tc-lab-frame">
        <div className="finaz-root tc-lab-mount">
          {Comp ? <Comp {...(props || {})}/> : <div className="tc-tip">Lab no disponible.</div>}
        </div>
      </div>
      <div className="tc-tip" style={{ marginTop: 12 }}>
        Lab interactivo con datos OHLCV sintéticos y deterministas. El motor detecta pivots y valida tus trazados — practica sin riesgo y sin "trendline shopping".
      </div>
    </div>
  );
}

/* ── S4 Transfer ──────────────────────────────────────────── */
function StageTransfer({ ep, epKey, progress, mark, markStep }) {
  const ex = ep.exercise || {};
  const saved = (progress[epKey] && progress[epKey].memo) || "";
  const [text, setText] = useState(saved);
  const [submitted, setSubmitted] = useState(false);
  const words = text.trim().split(/\s+/).filter(Boolean).length;
  // Probabilistic-language linter: TA must avoid certainty words
  const banned = ["seguro", "garantizado", "siempre sube", "nunca falla", "100%", "imposible que"];
  const found = banned.filter(w => new RegExp(w.replace(/ /g, "\\s+"), "i").test(text));
  useEffect(() => {
    const t = setTimeout(() => mark(epKey, progress[epKey]?.status || "now", { memo: text }), 600);
    return () => clearTimeout(t);
  }, [text, epKey, mark, progress]);
  const submit = () => { setSubmitted(true); markStep(); };

  return (
    <div className="tc-stage">
      <div className="tc-stage-head"><h2>S4 · Transfer</h2><span className="tc-stage-kick">Entregable observable</span></div>
      {ex.prompt && <div className="tc-rule"><span className="tc-kicker">Consigna del ejercicio</span><p><AutoGloss text={ex.prompt} triggerIds={ep.glossary_triggers}/></p></div>}

      <div className="tc-memo">
        <span className="tc-kicker">Tu análisis · lenguaje probabilístico, sin certezas absolutas</span>
        <textarea className="tc-textarea" value={text} onChange={e => setText(e.target.value)}
          placeholder="Describe la estructura de tendencia que ves: HH/HL o LH/LL, qué trendline trazarías y con cuántos toques, dónde está el siguiente nivel de decisión. Usa probabilidades, no certezas."/>
        <div className="tc-memo-meta">
          <span className={words < 40 ? "warn" : "ok"}>{words} palabras (objetivo ≥ 60)</span>
          <span className={found.length ? "bad" : "ok"}>{found.length ? `Lenguaje no probabilístico: ${found.join(", ")}` : "Lenguaje probabilístico ✓"}</span>
        </div>
        {found.length > 0 && (
          <div className="tc-linter">
            <b>Disciplina del curso:</b> el análisis técnico habla de <em>probabilidades y escenarios</em>, no de certezas. Reformula con "es probable que", "el escenario base es", "invalidaría esta lectura si…".
          </div>
        )}
      </div>

      {!submitted ? (
        <button className="tc-btn-primary" disabled={words < 20} onClick={submit} style={{ marginTop: 16 }}>Enviar entregable</button>
      ) : (
        <div className={`tc-feedback ${found.length || words < 60 ? "partial" : "ok"}`}>
          <h5>{found.length || words < 60 ? "Entregable registrado · revisa la disciplina" : "Entregable sólido"}</h5>
          <p>
            {found.length
              ? "Has usado lenguaje de certeza. En análisis técnico nada es seguro: trabajamos con escenarios y convicción graduada."
              : words < 60
                ? "Desarrolla más tu lectura: estructura de pivotes, validez de la trendline y nivel de invalidación."
                : "Tu análisis distingue estructura, trazado y nivel de invalidación. Esa es la forma profesional de leer tendencia."}
          </p>
          {Object.keys(ex.scoring_rubric || {}).length > 0 && (
            <div className="tc-rubric">
              <span className="tc-kicker">Rúbrica del capstone</span>
              <div className="tc-chip-row">
                {Object.entries(ex.scoring_rubric).map(([k, v]) => (
                  <span key={k} className="tc-rt">{k.replace(/_/g, " ")} · {v}</span>
                ))}
              </div>
            </div>
          )}
        </div>
      )}
      {ep.success_criteria && (
        <div className="tc-tip tc-tip-accent" style={{ marginTop: 16 }}><b>Mastery requiere:</b> {ep.success_criteria}</div>
      )}
    </div>
  );
}

/* ── Glossary view ────────────────────────────────────────── */
function GlossaryView({ course }) {
  const [q, setQ] = useState("");
  const filtered = useMemo(() => {
    const qq = q.trim().toLowerCase();
    if (!qq) return course.glossary;
    return course.glossary.filter(g =>
      g.term.toLowerCase().includes(qq) || (g.definition || "").toLowerCase().includes(qq) || (g.category || "").toLowerCase().includes(qq));
  }, [q, course.glossary]);
  const groups = {};
  filtered.forEach(g => { const c = g.category || "general"; (groups[c] = groups[c] || []).push(g); });
  const catLabel = c => c.replace(/_/g, " ").replace(/\b\w/g, ch => ch.toUpperCase());
  return (
    <>
      <section className="tc-hero">
        <span className="tc-eyebrow">Glosario técnico · {course.glossary.length} términos</span>
        <h1 className="tc-h1">Glosario · C03-A Tendencia</h1>
        <p className="tc-lede">Vocabulario operativo de análisis técnico. Pulsa cualquier término en el flujo del episodio para ver su tarjeta.</p>
        <div style={{ marginTop: 18, maxWidth: 460 }}>
          <input className="tc-search" placeholder="Buscar término o definición…" value={q} onChange={e => setQ(e.target.value)} autoFocus/>
        </div>
        <div className="tc-cta" style={{ marginTop: 16 }}><button className="tc-btn" onClick={() => go("/")}>← Ruta del curso</button></div>
      </section>
      {Object.keys(groups).sort().map(cat => (
        <section className="tc-section" key={cat}>
          <div className="tc-section-head"><h2>{catLabel(cat)}</h2><span className="meta">{groups[cat].length} términos</span></div>
          <div className="tc-mod-grid">
            {groups[cat].map(g => (
              <div key={g.id} className="tc-mod-card" style={{ cursor: "default" }}>
                <div className="tc-mod-head"><span className="tc-mod-id">{g.id.replace("GLOSS.AT.", "")}</span></div>
                <h3>{g.term}</h3>
                <p>{g.definition}</p>
                {g.why && <p style={{ color: "var(--text-muted)", fontSize: 12, marginTop: 6 }}><b>Por qué importa:</b> {g.why}</p>}
                {g.miscon && <p style={{ color: "var(--signal-warning)", fontSize: 12, marginTop: 4 }}><b>Cuidado:</b> {g.miscon}</p>}
              </div>
            ))}
          </div>
        </section>
      ))}
      {filtered.length === 0 && <div className="tc-tip">Sin resultados para "{q}".</div>}
    </>
  );
}

/* ── App root ─────────────────────────────────────────────── */
function App({ course }) {
  const route = useHashRoute();
  const [progress, mark] = useProgress();
  return (
    <GlossaryProvider glossary={course.glossary}>
      <div className="tc-app">
        <TopBar course={course} route={route} progress={progress}/>
        <div className="tc-content">
          {route.route === "home" && <CourseHome course={course} progress={progress}/>}
          {route.route === "module" && <ModuleHome course={course} modId={route.modId} progress={progress}/>}
          {route.route === "episode" && <EpisodePlayer course={course} modId={route.modId} epId={route.epId} stepId={route.stepId} progress={progress} mark={mark}/>}
          {route.route === "glossary" && <GlossaryView course={course}/>}
        </div>
        <footer className="tc-foot">
          <span>Finaz · C03-A Tendencia · {course.n_modules} módulos · {course.n_episodes} episodios · análisis técnico</span>
          <span>cursos2.iort.io</span>
        </footer>
      </div>
    </GlossaryProvider>
  );
}

(async function boot() {
  try {
    const res = await fetch("data.json");
    if (!res.ok) throw new Error("HTTP " + res.status);
    const course = await res.json();
    ReactDOM.createRoot(document.getElementById("root")).render(<App course={course}/>);
  } catch (e) {
    document.getElementById("root").innerHTML =
      `<div style="padding:60px 28px;color:#A2A9B4;font-family:sans-serif;max-width:600px;margin:0 auto">
        <h1 style="color:#E8E9ED">No se pudo cargar el curso</h1><p>Error: ${e.message}</p>
        <p><a href="https://cursos2.iort.io/" style="color:#6BA3FF">← cursos2.iort.io</a></p></div>`;
    console.error(e);
  }
})();
