/* FINAZ — Tramo de drawdown.
   Datos ficticios de una cartera (capital invertido) a lo largo de ~2 años,
   con su máximo (high-water mark), la región "bajo agua" y la tira de
   drawdown %. Mismo lenguaje Orgánico Verde, narrativa y hover pedagógico. */
(function () {
  "use strict";
  const { useState, useEffect, useRef, useMemo, useCallback } = React;
  const e = React.createElement;
  const UI = window.FinazUI;

  // ---------- generación determinista del dataset ----------
  const MESES = ["ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sep", "oct", "nov", "dic"];
  function mulberry32(s) { return function () { s |= 0; s = (s + 0x6D2B79F5) | 0; let t = Math.imul(s ^ (s >>> 15), 1 | s); t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; }

  function buildDD() {
    const rnd = mulberry32(77123);
    const anchors = [[0, 10000], [12, 11200], [28, 13180], [40, 11620], [52, 9520], [60, 9980], [76, 11820], [92, 13190], [104, 13760]];
    const N = 105;
    function interp(w) {
      for (let i = 0; i < anchors.length - 1; i++) {
        const [aw, av] = anchors[i], [bw, bv] = anchors[i + 1];
        if (w >= aw && w <= bw) { const k = (w - aw) / (bw - aw); const eK = k < 0.5 ? 2 * k * k : 1 - Math.pow(-2 * k + 2, 2) / 2; return av + (bv - av) * eK; }
      }
      return anchors[anchors.length - 1][1];
    }
    const start = new Date(2024, 0, 5);
    const points = [];
    let drift = 0;
    for (let w = 0; w < N; w++) {
      const base = interp(w);
      drift = drift * 0.55 + (rnd() - 0.5) * (w > 46 && w < 58 ? 150 : 70);
      const v = Math.max(8800, base + drift);
      const d = new Date(start.getTime() + w * 7 * 864e5);
      const label = d.getDate() + " " + MESES[d.getMonth()] + " '" + String(d.getFullYear()).slice(2);
      points.push({ w, t: label, v: Math.round(v) });
    }
    // máximo móvil (high-water mark) y drawdown
    let peak = -Infinity;
    points.forEach((p) => { peak = Math.max(peak, p.v); p.peak = peak; p.dd = (p.v - peak) / peak; });

    let peakIdx = 0, troughIdx = 0, maxDD = 0;
    points.forEach((p, i) => { if (p.dd < maxDD) { maxDD = p.dd; troughIdx = i; } });
    // el máximo que origina el drawdown máximo
    for (let i = troughIdx; i >= 0; i--) { if (points[i].v === points[i].peak) { peakIdx = i; break; } }
    const peakVal = points[peakIdx].v;
    let recoveryIdx = points.length - 1;
    for (let i = peakIdx + 1; i < points.length; i++) { if (points[i].v >= peakVal) { recoveryIdx = i; break; } }

    const glossary = {
      capital: { term: "Capital invertido", text: "El valor de la cartera a lo largo del tiempo. Aquí parte de 10.000 € simulados." },
      hwm: { term: "Máximo (high-water mark)", text: "El valor más alto alcanzado hasta la fecha. El drawdown siempre se mide como la caída desde este punto." },
      drawdown: { term: "Drawdown", text: "Caída porcentual desde el último máximo. Mide cuánto se aleja la cartera de su mejor momento: el 'dolor' de la inversión." },
      maxdd: { term: "Máximo drawdown", text: "La mayor caída pico-valle del periodo. Es la peor racha que habría sufrido quien entró en el máximo." },
      recuperacion: { term: "Recuperación", text: "Volver al máximo previo. Cuando ocurre, el drawdown se cierra y empieza a contar uno nuevo desde el siguiente máximo." },
      bajoagua: { term: "Periodo bajo agua", text: "El tiempo que pasa desde un máximo hasta recuperar ese mismo nivel. Cuanto más largo, más paciencia exige." },
    };

    const beats = [
      { i0: 0, i1: 26, kind: "up", tag: "Crecimiento", title: "El capital crece y marca máximos", body: "La cartera sube de forma sostenida. Cada nuevo máximo eleva el high-water mark: la referencia desde la que se medirá cualquier caída futura.", glossary: "capital" },
      { i0: 26, i1: 31, kind: "calm", tag: "Máximo", title: "Se fija el máximo histórico", body: "La cartera toca su punto más alto. A partir de aquí, todo lo que baje será drawdown medido desde este nivel.", glossary: "hwm" },
      { i0: 31, i1: 50, kind: "down", tag: "Caída", title: "Empieza el drawdown", body: "El valor retrocede y se aleja del máximo. La región sombreada muestra la distancia hasta el high-water mark: el terreno perdido.", glossary: "drawdown" },
      { i0: 50, i1: 60, kind: "down", tag: "Suelo", title: "Máximo drawdown", body: "La caída toca fondo. Es la peor racha del periodo y el punto que más pone a prueba la paciencia del inversor.", glossary: "maxdd" },
      { i0: 60, i1: 90, kind: "recover", tag: "Recuperación", title: "La cartera escala de vuelta", body: "El capital remonta hacia el máximo previo. El drawdown se va estrechando, pero sigue abierto hasta tocar el high-water mark.", glossary: "recuperacion" },
      { i0: 90, i1: 104, kind: "up", tag: "Nuevo máximo", title: "Drawdown cerrado", body: "La cartera supera el máximo anterior: el drawdown se cierra y arranca un periodo nuevo. El tiempo total bajo agua mide cuánto costó.", glossary: "bajoagua" },
    ];
    const markers = [
      { idx: peakIdx, ref: "hwm", label: "Máximo" },
      { idx: troughIdx, ref: "maxdd", label: "Mínimo" },
      { idx: recoveryIdx, ref: "recuperacion", label: "Recuperación" },
    ];
    const weeksUnder = recoveryIdx - peakIdx;
    return {
      points, beats, markers, glossary, peakIdx, troughIdx, recoveryIdx,
      meta: {
        symbol: "Cartera FINAZ", name: "Capital invertido simulado", date: "104 semanas",
        invested: 10000, last: points[points.length - 1].v,
        maxDDpct: +(maxDD * 100).toFixed(1), maxDDval: Math.round(points[troughIdx].v - peakVal),
        weeksUnder, monthsUnder: Math.round(weeksUnder / 4.33),
      },
      source: "Datos simulados por FINAZ con fines formativos. No representan una cartera real.",
      disclaimer: "FINAZ es una plataforma de formación e información. No ofrece asesoramiento de inversión, ni recomendaciones de compra/venta, ni predicciones accionables.",
    };
  }
  const DD = buildDD();
  window.FINAZ_DD = DD;

  function beatAt(beats, idx) { if (idx == null) return null; for (const b of beats) if (idx >= b.i0 && idx <= b.i1) return b; return beats[beats.length - 1]; }
  function smoothPath(pts, sm) {
    if (pts.length < 2) return "";
    if (sm <= 0.001) return "M" + pts.map((p) => p.x.toFixed(2) + "," + p.y.toFixed(2)).join(" L");
    const k = sm * 0.33; let d = "M" + pts[0].x.toFixed(2) + "," + pts[0].y.toFixed(2);
    for (let i = 0; i < pts.length - 1; i++) {
      const p0 = pts[i - 1] || pts[i], p1 = pts[i], p2 = pts[i + 1], p3 = pts[i + 2] || p2;
      d += " C" + (p1.x + (p2.x - p0.x) * k).toFixed(2) + "," + (p1.y + (p2.y - p0.y) * k).toFixed(2) + " " + (p2.x - (p3.x - p1.x) * k).toFixed(2) + "," + (p2.y - (p3.y - p1.y) * k).toFixed(2) + " " + p2.x.toFixed(2) + "," + p2.y.toFixed(2);
    }
    return d;
  }

  /* ───────────────── gráfico de drawdown ───────────────── */
  function DrawdownChart({ theme, data, width, height, layers, focusIdx, activeBeat, onFocus, onMarker }) {
    const T = theme.tokens, C = theme.chart;
    const pts = data.points, n = pts.length;
    const svgRef = useRef(null);
    const [intro, setIntro] = useState(0);
    useEffect(() => { setIntro(0); const t0 = performance.now(); const iv = setInterval(() => { const k = Math.min(1, (performance.now() - t0) / 1100); setIntro(1 - Math.pow(1 - k, 3)); if (k >= 1) clearInterval(iv); }, 25); return () => clearInterval(iv); }, [theme.family]);

    const padL = 16, padR = 18, padT = 24;
    const eqH = 250, gap = 40, uwH = 84;
    const plotW = width - padL - padR;
    const uwTop = padT + eqH + gap;
    const vals = pts.map((p) => p.v);
    let yMin = Math.min(...vals), yMax = Math.max(...pts.map((p) => p.peak));
    const padV = (yMax - yMin) * 0.12 || 1; yMin -= padV; yMax += padV * 0.5;
    const xFor = (i) => padL + (i / (n - 1)) * plotW;
    const yFor = (v) => padT + (1 - (v - yMin) / (yMax - yMin)) * eqH;
    const maxDD = Math.min(...pts.map((p) => p.dd)) || -0.01;
    const ddFor = (dd) => uwTop + (dd / maxDD) * uwH;

    const eqGeom = useMemo(() => pts.map((p, i) => ({ x: xFor(i), y: yFor(p.v) })), [width, height, n]);
    const peakGeom = useMemo(() => pts.map((p, i) => ({ x: xFor(i), y: yFor(p.peak) })), [width, height, n]);
    const eqPath = useMemo(() => smoothPath(eqGeom, C.smooth), [eqGeom, C.smooth]);
    const peakPath = useMemo(() => "M" + peakGeom.map((p) => p.x.toFixed(1) + "," + p.y.toFixed(1)).join(" L"), [peakGeom]);
    // región bajo agua (entre equity y peak)
    const gapArea = useMemo(() => {
      if (!eqPath) return "";
      const fwd = eqGeom.map((p) => p.x.toFixed(1) + "," + p.y.toFixed(1)).join(" L");
      const back = peakGeom.slice().reverse().map((p) => p.x.toFixed(1) + "," + p.y.toFixed(1)).join(" L");
      return "M" + fwd + " L" + back + " Z";
    }, [eqGeom, peakGeom]);
    // tira de drawdown %
    const uwArea = useMemo(() => {
      const fwd = pts.map((p, i) => xFor(i).toFixed(1) + "," + ddFor(p.dd).toFixed(1)).join(" L");
      return "M" + padL + "," + uwTop + " L" + fwd + " L" + (padL + plotW) + "," + uwTop + " Z";
    }, [width, height, n]);
    const segPath = useMemo(() => { if (!activeBeat) return ""; return smoothPath(eqGeom.slice(activeBeat.i0, activeBeat.i1 + 1), C.smooth); }, [activeBeat, eqGeom, C.smooth]);

    const fIdx = focusIdx == null ? null : Math.max(0, Math.min(n - 1, focusIdx));
    const fp = fIdx == null ? null : pts[fIdx];

    const idxFromEvent = useCallback((ev) => { const r = svgRef.current.getBoundingClientRect(); const sx = (ev.clientX - r.left) * (width / r.width); return Math.max(0, Math.min(n - 1, Math.round(((sx - padL) / plotW) * (n - 1)))); }, [width, plotW, n]);
    const handleMove = useCallback((ev) => onFocus(idxFromEvent(ev)), [idxFromEvent, onFocus]);

    const grad = "ddg-" + theme.id, glowId = "ddglow-" + theme.id, clipId = "ddclip-" + theme.id;
    const reveal = padL + intro * plotW + 2;

    return e("svg", { ref: svgRef, width: "100%", viewBox: "0 0 " + width + " " + height, style: { display: "block", touchAction: "none", cursor: "crosshair" },
      onPointerMove: handleMove, onPointerLeave: () => { onFocus(null); onMarker(null); },
      onPointerDown: (ev) => { try { ev.currentTarget.setPointerCapture(ev.pointerId); } catch (e2) {} handleMove(ev); } },
      e("defs", null,
        e("linearGradient", { id: grad, x1: "0", y1: "0", x2: "0", y2: "1" },
          e("stop", { offset: "0%", stopColor: T.accent, stopOpacity: C.areaTop }),
          e("stop", { offset: "100%", stopColor: T.accent, stopOpacity: Math.max(C.areaBottom, 0.02) })),
        e("filter", { id: glowId, x: "-20%", y: "-40%", width: "140%", height: "180%" },
          e("feGaussianBlur", { stdDeviation: C.glow ? 3 : 0, result: "b" }),
          e("feMerge", null, e("feMergeNode", { in: "b" }), e("feMergeNode", { in: "SourceGraphic" }))),
        e("clipPath", { id: clipId }, e("rect", { x: 0, y: 0, width: reveal, height: height }))),

      // rejilla equity
      [0, 1, 2, 3].map((r) => e("line", { key: "g" + r, x1: padL, x2: padL + plotW, y1: padT + (r / 3) * eqH, y2: padT + (r / 3) * eqH, stroke: T.grid, strokeWidth: 1 })),

      e("g", { clipPath: "url(#" + clipId + ")" },
        // equity area
        e("path", { d: eqPath + " L" + eqGeom[n - 1].x.toFixed(1) + "," + (padT + eqH) + " L" + eqGeom[0].x.toFixed(1) + "," + (padT + eqH) + " Z", fill: "url(#" + grad + ")", opacity: activeBeat ? 0.5 : 0.9 }),
        // región bajo agua (gap a HWM)
        layers.hwm && e("path", { d: gapArea, fill: T.neg, opacity: 0.14 }),
        // línea HWM
        layers.hwm && e("path", { d: peakPath, fill: "none", stroke: T.neg, strokeWidth: 1.2, strokeDasharray: "3 4", opacity: 0.7 }),
        // equity line
        e("path", { d: eqPath, fill: "none", stroke: T.accent, strokeWidth: C.lineWidth, strokeLinecap: "round", strokeLinejoin: "round", opacity: activeBeat ? 0.4 : 1, filter: C.glow ? "url(#" + glowId + ")" : "none" }),
        activeBeat && e("path", { d: segPath, fill: "none", stroke: T.accent, strokeWidth: C.lineWidth + 1.4, strokeLinecap: "round", strokeLinejoin: "round", filter: C.glow ? "url(#" + glowId + ")" : "none" })
      ),

      // tira de drawdown %
      layers.bajoagua && e("g", { opacity: intro > 0.5 ? 1 : 0 },
        e("line", { x1: padL, x2: padL + plotW, y1: uwTop, y2: uwTop, stroke: T.sub, strokeWidth: 1, opacity: 0.4 }),
        e("path", { d: uwArea, fill: T.neg, opacity: 0.5 }),
        e("text", { x: padL, y: uwTop - 8, fontFamily: theme.fonts.mono, fontSize: 9.5, fill: T.sub, letterSpacing: ".06em" }, "DRAWDOWN %  ·  bajo el máximo"),
        e("text", { x: padL + plotW, y: ddFor(maxDD) + 3, textAnchor: "end", fontFamily: theme.fonts.mono, fontSize: 9.5, fill: T.neg }, (maxDD * 100).toFixed(1) + "%")
      ),

      // marcadores
      layers.anotaciones && intro > 0.85 && data.markers.map((mk, i) => {
        const g = eqGeom[mk.idx];
        return e("g", { key: i, style: { cursor: "help" }, onPointerEnter: (ev) => { ev.stopPropagation(); const gl = data.glossary[mk.ref]; onMarker({ kind: "term", x: g.x, y: g.y, width, height, label: mk.label, term: gl.term, text: gl.text }); onFocus(mk.idx); } },
          e("circle", { cx: g.x, cy: g.y, r: 11, fill: "transparent" }),
          e("circle", { cx: g.x, cy: g.y, r: C.dotR + 3, fill: "none", stroke: mk.ref === "maxdd" ? T.neg : T.accent2, strokeWidth: 1.2, opacity: 0.5, className: "finaz-pulse", style: { transformOrigin: g.x + "px " + g.y + "px" } }),
          e("circle", { cx: g.x, cy: g.y, r: 3.2, fill: mk.ref === "maxdd" ? T.neg : T.accent2, stroke: T.bg, strokeWidth: 1.4 }));
      }),

      // scrubber + readout
      fp != null && intro > 0.95 && e("g", { style: { pointerEvents: "none" } },
        e("line", { x1: xFor(fIdx), x2: xFor(fIdx), y1: padT, y2: uwTop + uwH, stroke: T.ink, strokeWidth: 1, opacity: 0.22, strokeDasharray: "1 4" }),
        e("circle", { cx: xFor(fIdx), cy: yFor(fp.v), r: C.dotR, fill: T.accent, stroke: T.bg, strokeWidth: 2 }),
        e("circle", { cx: xFor(fIdx), cy: ddFor(fp.dd), r: 3.5, fill: T.neg, stroke: T.bg, strokeWidth: 1.5 }),
        ddReadout(fp, xFor(fIdx), yFor(fp.v), width, theme))
    );
  }

  function ddReadout(p, x, y, width, theme) {
    const T = theme.tokens; const dd = p.dd * 100; const w = 132, h = 46;
    const left = x + w + 16 > width ? x - w - 12 : x + 14; const top = Math.max(2, y - h - 14);
    return e("g", { transform: "translate(" + left + "," + top + ")" },
      e("rect", { width: w, height: h, rx: theme.chart.radius ? 8 : 0, fill: T.panel, stroke: T.panelEdge, strokeWidth: 1 }),
      e("text", { x: 11, y: 17, fontFamily: theme.fonts.mono, fontSize: 10, fill: T.sub }, p.t),
      e("text", { x: w - 11, y: 17, textAnchor: "end", fontFamily: theme.fonts.mono, fontSize: 11, fontWeight: 600, fill: dd < -0.05 ? T.neg : T.sub }, dd.toFixed(1) + "%"),
      e("text", { x: 11, y: 36, fontFamily: theme.fonts.mono, fontSize: 15, fontWeight: 600, fill: T.ink }, p.v.toLocaleString("es-ES") + " €"));
  }

  /* ───────────────── vista drawdown ───────────────── */
  function FinazDrawdownView({ theme }) {
    const data = DD, n = data.points.length;
    const [focusIdx, setFocusIdx] = useState(null);
    const [playing, setPlaying] = useState(false);
    const [marker, setMarker] = useState(null);
    const [layers, setLayers] = useState({ anotaciones: true, hwm: true, bajoagua: true, fuentes: true });
    const activeBeat = beatAt(data.beats, focusIdx);

    useEffect(() => { if (!window.__fzNarr) window.__fzNarr = {}; if (!window.__fzNarr.drawdown) { window.__fzNarr.drawdown = true; const t = setTimeout(() => setPlaying(true), 1400); return () => clearTimeout(t); } }, []);
    useEffect(() => { setFocusIdx(null); setPlaying(false); setMarker(null); }, [theme.family]);
    useEffect(() => {
      if (!playing) return;
      const startP = (focusIdx == null || focusIdx >= n - 1) ? 0 : focusIdx / (n - 1);
      const t0 = performance.now();
      const iv = setInterval(() => { const p = Math.min(1, startP + (performance.now() - t0) / 24000); setFocusIdx(Math.round(p * (n - 1))); if (p >= 1) { clearInterval(iv); setPlaying(false); } }, 33);
      return () => clearInterval(iv);
    }, [playing]); // eslint-disable-line

    const userFocus = useCallback((idx) => { setPlaying(false); setFocusIdx(idx); }, []);
    const togglePlay = () => { if (playing) setPlaying(false); else { if (focusIdx == null || focusIdx >= n - 1) setFocusIdx(0); setPlaying(true); } };
    const tlRef = useRef(null);
    const tlScrub = useCallback((ev) => { const r = tlRef.current.getBoundingClientRect(); const k = Math.max(0, Math.min(1, (ev.clientX - r.left) / r.width)); userFocus(Math.round(k * (n - 1))); }, [userFocus, n]);

    const m = data.meta;
    const curDD = focusIdx == null ? data.points[data.troughIdx].dd : data.points[focusIdx].dd;
    const curVal = focusIdx == null ? m.last : data.points[focusIdx].v;
    const cards = [];
    if (activeBeat && activeBeat.glossary) { const g = data.glossary[activeBeat.glossary]; cards.push({ type: "term", label: "Glosario", head: g.term, text: g.text }); }

    return e("div", { className: "fz-stage" },
      e("section", { className: "fz-left" },
        e("div", { className: "fz-symrow" },
          e("div", { className: "fz-sym" },
            e("div", { className: "fz-symname" }, e("strong", null, m.symbol), e("span", null, m.name + " · " + m.date)),
            e("div", { className: "fz-price" }, e("span", { className: "fz-pv" }, curVal.toLocaleString("es-ES")), e("span", { className: "fz-unit" }, "€")),
            e("div", { className: "fz-chg neg" }, "Drawdown " + (curDD * 100).toFixed(1) + "%")
          ),
          UI.e(UI.LayerChips, { layers, onToggle: (k) => setLayers((s) => ({ ...s, [k]: !s[k] })), defs: [["anotaciones", "Anotaciones"], ["hwm", "Máximo"], ["bajoagua", "Bajo agua"], ["fuentes", "Fuentes"]] })
        ),
        e("div", { className: "fz-chartcard" },
          e(DrawdownChart, { theme, data, width: 880, height: 440, layers, focusIdx, activeBeat, onFocus: userFocus, onMarker: setMarker }),
          marker && e(UI.Popover, { marker })),
        e("div", { className: "fz-timeline", ref: tlRef,
          onPointerDown: (ev) => { try { ev.currentTarget.setPointerCapture(ev.pointerId); } catch (e2) {} tlScrub(ev); },
          onPointerMove: (ev) => { if (ev.buttons) tlScrub(ev); } },
          data.beats.map((b, i) => e("div", { key: i, className: "fz-tlseg" + (activeBeat === b ? " on" : ""), style: { flex: (b.i1 - b.i0) }, title: b.title }, e("span", { className: "fz-tltag" }, b.tag))),
          focusIdx != null && e("div", { className: "fz-playhead", style: { left: (focusIdx / (n - 1) * 100) + "%" } })),
        e(UI.SourcesFooter, { on: layers.fuentes, source: data.source, disclaimer: data.disclaimer })
      ),
      e("aside", { className: "fz-right" },
        e(UI.PlayBar, { playing, idle: focusIdx == null, progress: focusIdx == null ? 0 : focusIdx / (n - 1), onToggle: togglePlay }),
        activeBeat
          ? e(UI.NarrativeCard, { anim: activeBeat.tag, tag: activeBeat.tag, kind: activeBeat.kind, time: data.points[activeBeat.i0].t + " – " + data.points[activeBeat.i1].t, title: activeBeat.title, body: activeBeat.body })
          : e(UI.NarrativeCard, { summary: true, tag: "Tramo de drawdown", title: "El viaje del capital, pico a valle",
              body: e(React.Fragment, null, "Reproduce la explicación o ", e("strong", null, "arrastra"), " por el gráfico. La tira inferior mide la caída desde el último máximo en cada instante."),
              children: e(UI.StatGrid, { stats: [["Invertido", m.invested + " €"], ["Valor final", m.last + " €"], ["Máx. drawdown", m.maxDDpct + "%"], ["Bajo agua", "≈ " + m.monthsUnder + " meses"]] }) }),
        e(UI.ExtraCards, { cards })
      )
    );
  }

  window.FinazDrawdownView = FinazDrawdownView;
})();
