/* global React */
const { useState, useEffect, useRef } = React;

/* ============================================================
   CS-11 · Outcome stat  —  hero number with source
   On enter: reveal + count-up animation (800ms).
   ============================================================ */
function CSOutcome({ label, value, unit, sub, sources }) {
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el || inView) return;
    const rect = el.getBoundingClientRect();
    const vh = window.innerHeight || document.documentElement.clientHeight;
    if (rect.top < vh && rect.bottom > 0) { setInView(true); return; }
    if (typeof IntersectionObserver === "undefined") { setInView(true); return; }
    let obs;
    try {
      obs = new IntersectionObserver(
        (entries) => entries.forEach(e => { if (e.isIntersecting) { setInView(true); obs.disconnect(); clearTimeout(timer); } }),
        { threshold: 0.25 }
      );
      obs.observe(el);
    } catch (err) { setInView(true); return; }
    const timer = setTimeout(() => { setInView(true); obs && obs.disconnect(); }, 600);
    return () => { obs && obs.disconnect(); clearTimeout(timer); };
  }, [inView]);

  // Inline count-up — kept here to avoid cross-file hook dependency.
  const raw = String(value);
  const numeric = parseFloat(raw.replace(/,/g, ""));
  const decimals = (raw.split(".")[1] || "").length;
  const isNum = !Number.isNaN(numeric) && /^-?[0-9.,]+$/.test(raw);
  const [n, setN] = useState(isNum ? 0 : numeric);
  useEffect(() => {
    if (!isNum || !inView) return;
    let raf, start;
    const ease = (t) => 1 - Math.pow(1 - t, 3);
    const step = (ts) => {
      if (!start) start = ts;
      const t = Math.min(1, (ts - start) / 1000);
      setN(numeric * ease(t));
      if (t < 1) raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    return () => raf && cancelAnimationFrame(raf);
  }, [isNum, inView, numeric]);
  const rendered = isNum
    ? n.toLocaleString(undefined, { minimumFractionDigits: decimals, maximumFractionDigits: decimals })
    : raw;

  return (
    <div className={`cs-outcome ${inView ? "cs-reveal is-in" : "cs-reveal"}`} ref={ref}>
      <div className="o-label">{label}</div>
      <div className="o-value">
        <span>{rendered}</span>
        {unit && <span className="o-unit">{unit}</span>}
      </div>
      {sub && <div className="o-sub">{sub}</div>}
      {sources && sources.length > 0 && (
        <div className="o-source">
          {sources.map((s, i) => (
            <span key={i}>{s.label}<strong>{s.value}</strong></span>
          ))}
        </div>
      )}
    </div>
  );
}

/* ============================================================
   CS-12 · Provenance line
   ============================================================ */
function CSProv({ source, date, kind = "Source" }) {
  return (
    <span className="cs-prov">
      <span className="cs-prov-mark"></span>
      <span>{kind}</span>
      <span className="cs-prov-sep">·</span>
      <span className="cs-prov-source">{source}</span>
      <span className="cs-prov-sep">·</span>
      <span className="cs-prov-date">{date}</span>
    </span>
  );
}

/* ============================================================
   CS-13 · Artifact link
   ============================================================ */
function CSArtifact({ kind, name, desc, size, href = "#" }) {
  return (
    <a className="cs-artifact" href={href}>
      <span className="ar-glyph" aria-hidden="true">
        <span className="gl-line l"></span>
        <span className="gl-line m"></span>
        <span className="gl-line s"></span>
        <span className="gl-line m"></span>
        <span className="gl-line l"></span>
        <span className="gl-line m"></span>
        <span className="gl-line s"></span>
      </span>
      <span className="ar-meta">
        <span className="ar-kind">{kind}</span>
        <span className="ar-name">{name}</span>
        <span className="ar-desc">{desc}</span>
      </span>
      <span className="ar-size">{size}</span>
    </a>
  );
}

/* ============================================================
   CS-15 · Operator quote
   ============================================================ */
function CSQuote({ initials, who, role, recorded, children }) {
  const ref = useRef(null);
  const [seen, setSeen] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el || seen) return;
    const rect = el.getBoundingClientRect();
    const vh = window.innerHeight || document.documentElement.clientHeight;
    if (rect.top < vh && rect.bottom > 0) { setSeen(true); return; }
    if (typeof IntersectionObserver === "undefined") { setSeen(true); return; }
    let obs;
    try {
      obs = new IntersectionObserver(
        (entries) => entries.forEach(e => { if (e.isIntersecting) { setSeen(true); obs.disconnect(); clearTimeout(timer); } }),
        { threshold: 0.2 }
      );
      obs.observe(el);
    } catch (err) { setSeen(true); return; }
    const timer = setTimeout(() => { setSeen(true); obs && obs.disconnect(); }, 600);
    return () => { obs && obs.disconnect(); clearTimeout(timer); };
  }, [seen]);
  return (
    <div className={`cs-quote ${seen ? "cs-reveal is-in" : "cs-reveal"}`} ref={ref}>
      <div className="cq-avatar">{initials}</div>
      <div className="cq-body">
        <p className="cq-text">{children}</p>
        <div className="cq-attr">
          <span className="cq-who">{who}</span>
          <span className="cq-role">{role}</span>
          <span className="cq-rec">Recorded {recorded}</span>
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   CS-16 · Transcript moment
   ============================================================ */
function CSTrans({ source, timestamp, lines }) {
  return (
    <div className="cs-trans">
      <div className="cs-trans-bar">
        <span className="ct-source">{source}</span>
        <span className="ct-ts">▸ {timestamp}</span>
      </div>
      <div className="cs-trans-body">
        {lines.map((l, i) => (
          <div className="cs-trans-line" data-who={l.who} key={i}>
            <span className="ct-spk">{l.spk}</span>
            <span className="ct-text">{l.text}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

/* ============================================================
   CS-17 · Two-side dialogue
   ============================================================ */
function CSDialog({ turns }) {
  return (
    <div className="cs-dialog">
      {turns.map((t, i) => (
        <div className="cs-dialog-turn" data-side={t.side} key={i}>
          <div className="cd-who">
            <span className="cd-role">{t.role}</span>
            <span>{t.who}</span>
            {t.stamp && <span className="cd-stamp">{t.stamp}</span>}
          </div>
          <div className="cd-text">{t.text}</div>
        </div>
      ))}
    </div>
  );
}

/* ============================================================
   CS-18 · Pull quote (social)
   ============================================================ */
function CSPull({ eyebrow, who, role, children }) {
  return (
    <div className="cs-pull">
      <span className="cp-mark">LVRD</span>
      <div className="cp-eyebrow">{eyebrow}</div>
      <blockquote>{children}</blockquote>
      <div className="cp-foot">
        <span className="cp-who">{who}</span>
        <span>{role}</span>
      </div>
    </div>
  );
}

/* ============================================================
   CS-19 · Share tile (1:1 social card)
   ============================================================ */
function CSShare({ caseId, sector, stat, unit, body, who }) {
  return (
    <div className="cs-share">
      <div className="csh-bar">
        <span className="csh-lvrd">LVRD</span>
        <span>{caseId} · {sector}</span>
      </div>
      <div className="csh-stat">
        {stat}
        <span className="csh-unit">{unit}</span>
      </div>
      <p className="csh-body">{body}</p>
      <div className="csh-foot">
        <span className="csh-who">{who}</span>
        <span>Case study →</span>
      </div>
    </div>
  );
}

/* ============================================================
   CS-20 · Next case
   ============================================================ */
function CSNext({ kind, title, sector, weeks, stat, statUnit, href = "#" }) {
  return (
    <a className="cs-next" href={href}>
      <div>
        <div className="csn-head">
          <span className="csn-label">Next case study →</span>
          <span>{kind}</span>
        </div>
        <h3 className="csn-title">{title}</h3>
        <div className="csn-meta">
          <span className="csn-sector">{sector}</span>
          <span>{weeks}</span>
        </div>
      </div>
      <div className="csn-stat">
        {stat}
        <span className="csn-unit">{statUnit}</span>
      </div>
    </a>
  );
}

/* ============================================================
   CS-21 · Canon ledger  —  the ratified file set as a quiet list
   Sister to P-03 Canon bundle (the 4-file Paperclip default).
   CanonList stands for any ratified file set with N entries —
   ORIGIN.md, THESIS.md, VOICE.md, etc. Renders as a 5-column
   ledger: mono index · mono filename · sans desc · mono size ·
   accent "Ratified" pip.
   ============================================================ */
function CanonList({ files }) {
  return (
    <div className="prim">
      <div className="prim-head">
        <span className="prim-label"><span className="n">CS-21</span> · Canon ledger</span>
        <span className="prim-caption">{files.length} files · ratified</span>
      </div>
      <div className="canon-list">
        {files.map((f, i) => (
          <div className="canon-row" key={f.name}>
            <div className="cl-idx">{String(i + 1).padStart(2, "0")}</div>
            <div className="cl-name">{f.name}</div>
            <div className="cl-desc">{f.desc}</div>
            <div className="cl-size">{f.size}</div>
            <div className="cl-status">
              <span className="mark"></span>Ratified
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

Object.assign(window, {
  CSOutcome, CSProv, CSArtifact,
  CSQuote, CSTrans, CSDialog,
  CSPull, CSShare, CSNext, CanonList
});
