/* global React, ReactDOM, window */
/* ============================================================
   INTAKE LOADING — EMOTIVE
   Replaces the three-agent venn that ran before the intake page.

   Dan, on the version this replaces: "feels way too technical! ... think of
   this as an advertisement to a creative ... numbers and AI-feeling interfaces
   are not getting their heart fluttering" — and then, on the deck's closing
   stats frame, "the updated sales deck attempts to get across the emotive,
   visual resonance of our history." Misaki: "i wonder if we can just recycle
   this for the loading..." So that frame is what this is.

   Gone: the persona venn, the per-specialist tool tallies, "Synthesizing",
   the shimmering skeleton page and the DECISION CONTEXT rail. Every one of
   them was the machine describing itself.

   Three corrections from Misaki, on watching it run:
     1. It lives in the MAIN PANEL, not over the whole screen. The chat stays
        on the left, so this reads as the page working rather than a takeover.
     2. Light ground, not the deck's ink. Cutting to black between two light
        screens was too cinematic.
     3. The figures do not count up. The walls are already moving, and
        animating the numbers on top of that made the frame restless.

   The agent's train of thought moved into the chat, where it belongs. What is
   left in the panel is the deck's composition: walls left and right, the two
   figures stacked and centred between them.

   Top-level names are uniquely prefixed on purpose: the standalone export
   transpiles all 34 files with Babel's `env` preset, which lowers `const` to
   `var` and makes any repeated top-level name collide across files.
   ============================================================ */
const { useState: emS, useRef: emR, useEffect: emE, useCallback: emCB } = React;

const EM_RUN_AT = 60;          /* walls start moving */

/* One thought every 2.6s. Three lines cover ~7.8s, against a `designSeconds`
   that defaults to 9 — so the thread finishes just before intake arrives
   rather than being cut off mid-thought. If `designSeconds` is dragged shorter
   in the tweaks panel the last line simply doesn't land; nothing breaks. */
const EM_STATUS_EVERY = 2600;

/* The wall columns are no longer computed here. `em-assets.js` ships two
   explicit, disjoint, tone-scattered lists per wall, because the two things
   Misaki asked for cannot be decided in the browser:

     - the two columns of a wall must never hold the same image, and for faces
       that means the same PERSON, which needs identity tags;
     - neighbouring cards must contrast in tone, which needs the pixels.

   Three earlier attempts tried to get there with modular arithmetic over one
   shared pool. Every one of them put duplicates on screen, because offsetting
   a shared sequence only makes a collision unlikely — and the offset drifts,
   since the columns run at different speeds. Disjoint lists make it
   impossible instead of unlikely.
*/

/* Every timer this screen opens is owned here and torn down on unmount, so a
   re-entry can never leave a stale callback running. */
function emTimeline() {
  const timers = emR([]);
  const at = emCB((ms, fn) => { timers.current.push(setTimeout(fn, ms)); }, []);
  emE(() => () => {
    timers.current.forEach(clearTimeout);
    timers.current = [];
  }, []);
  return { at };
}

function emReduced() {
  const [r, setR] = emS(false);
  emE(() => {
    const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
    const sync = () => setR(mq.matches);
    sync();
    mq.addEventListener("change", sync);
    return () => mq.removeEventListener("change", sync);
  }, []);
  return r;
}

/* `ar` is the image's own width/height, from the build. Applied as an aspect
   ratio rather than a pixel height, since a wall is a share of the panel
   rather than the deck's fixed 262px. */
function EMConceptCard({ card }) {
  return (
    <div className="em-kcard">
      <img src={card.src} alt="" style={{ aspectRatio: card.ar }} />
    </div>
  );
}
function EMPersonCard({ card, quoteIndex }) {
  const q = window.EM.quotes[quoteIndex % window.EM.quotes.length];
  return (
    <div className="em-pcard">
      <img src={card.src} alt="" style={{ aspectRatio: card.ar }} />
      <div className="em-say">
        <p>“{q[0]}”</p>
        <span>{q[1]}</span>
      </div>
    </div>
  );
}
function EMWall({ side, cols, Card, base }) {
  return (
    <div className={"em-wall " + side}>
      {cols.map((items, c) => (
        <div className="em-col" key={c}>
          {/* doubled so translateY(-50%) loops seamlessly */}
          <div className={"em-roll s" + (base + c)}>
            {items.concat(items).map((card, n) => (
              /* Quotes step by two from a per-column start, so one column
                 takes the even entries and the other the odd — disjoint, like
                 the images. Indexed modulo the run so both copies of a card
                 carry the same verbatim and the loop stays seamless. */
              <Card key={n} card={card} quoteIndex={c + (n % items.length) * 2} />
            ))}
          </div>
        </div>
      ))}
    </div>
  );
}

/* ---------------- the chat, open ---------------- */
/* Misaki, reversing the earlier "close the chat during the wait": "I also want
   to maintain the current chat interaction on the left so the people can
   actually see the progress on the left side." So the thread is open for the
   whole wait and the progress is legible in it.

   Two things this deliberately does NOT bring back, both cut on Misaki's
   instruction and still cut:
     - the hairline progress bar. The rows arriving one at a time are the
       progress; a bar next to them is the same claim twice, and a percentage
       is exactly the machine register Dan objected to.
     - the "Designing" tag in the header. The breadcrumb above already says it.

   It renders `.i3-chat` — the SAME class and width the intake screen's chat
   uses (`clamp(248px, 23cqw, 340px)` in ei4.css) — so crossing from this
   screen into intake does not move the panel. A chat that jumps width at the
   handoff reads as two different chats. `.i3-log` / `.i3-msg` / `.i3-comp`
   come from ei3.css for the same reason. */
function EMChatOpen({ brief, lines, current }) {
  return (
    <div className="i3-chat em-chat">
      <div className="i3-chat__h">
        <i className="ti ti-sparkles"></i><b>MakerChat</b>
      </div>
      <div className="i3-log">
        {brief && <div className="i3-msg me">{brief}</div>}
        {lines.slice(0, current + 1).map((l, k) => (
          <div className={"em-chatline" + (k === current ? " live" : "")} key={l}>
            {k === current
              ? <span className="em-dots"><i></i><i></i><i></i></span>
              : <i className="ti ti-check"></i>}
            <span>{l}</span>
          </div>
        ))}
      </div>
      {/* Present but inert: the composer is what makes this read as the same
          chat you just typed into, rather than a status panel wearing its
          chrome. It accepts nothing until intake mounts the live thread. */}
      <div className="i3-comp">
        <input placeholder="Add something while it works…" readOnly />
        <button tabIndex={-1} aria-hidden="true"><i className="ti ti-arrow-up"></i></button>
      </div>
    </div>
  );
}

/* ---------------- the screen ---------------- */
function EmotiveLoading({ running, bare, brief, onSkip }) {
  const EM = window.EM;
  const reduced = emReduced();
  const { at } = emTimeline();

  const [run, setRun] = emS(false);
  const [si, setSi] = emS(0);

  emE(() => {
    if (!running) return;
    if (reduced) { setRun(true); return; }
    at(EM_RUN_AT, () => setRun(true));
  }, [running, reduced, at]);

  /* The line of thought advances on an interval, not a rAF clock: without the
     hairline there is nothing here that needs sub-frame resolution, and an
     interval stops cleanly on unmount. It parks on the last line rather than
     looping — the wait can outrun `designSeconds`, and a thought that starts
     over reads as a stall. Reduced motion shows the thread already complete. */
  emE(() => {
    if (!running) return;
    const n = (window.EM.status || []).length;
    if (reduced) { setSi(Math.max(0, n - 1)); return; }
    setSi(0);
    const id = setInterval(() => {
      setSi(p => (p + 1 >= n ? (clearInterval(id), n - 1) : p + 1));
    }, EM_STATUS_EVERY);
    return () => clearInterval(id);
  }, [running, reduced]);

  const chat = (
    <EMChatOpen brief={brief} lines={window.EM.status || []} current={si} />
  );

  const panel = (
    <div className={"em-panel" + (run ? " run" : "")}>
      <div className="em-lightwash" />
      <EMWall side="l" cols={EM.concept} Card={EMConceptCard} base={0} />
      <div className="em-mid">
        <div className="em-eyebrow">{EM.eyebrow}</div>
        {EM.figs.map(fig => (
          <div className="em-fig" key={fig.cap.join(" ")}>
            <div className="em-num">{fig.display}</div>
            <div className="em-cap">{fig.cap.join(" ")}</div>
          </div>
        ))}
      </div>
      <EMWall side="r" cols={EM.people} Card={EMPersonCard} base={2} />
    </div>
  );

  if (bare) return (
    <div className="sf-work">
      {chat}
      {panel}
      {onSkip && <button className="sf-skip" onClick={onSkip}>Skip ahead<i className="ti ti-arrow-right" style={{ marginLeft: 6 }}></i></button>}
    </div>
  );

  /* Standalone / flow-board use keeps the platform frame around it, so the
     screen is read in the chrome it actually appears in. */
  return (
    <div className="i3-frame" style={{ width: 1440, height: 840 }} data-screen-label="Intake · designing">
      <div className="i3-top">
        <span className="i3-top__logo"><img className="ua-mark" src="brand/ua-mark.svg" alt="Under Armour" /></span>
        <span className="i3-top__crumb">New study / Designing</span>
        <span className="i3-top__sp"></span>
      </div>
      <div className="i3-work">
        <div className="i3-nav">{["home", "clipboard-list", "shirt", "books"].map((ic, k) => <span key={ic} className={"i3-nav__b" + (k === 1 ? " on" : "")}><i className={"ti ti-" + ic}></i></span>)}</div>
        {chat}
        {panel}
      </div>
    </div>
  );
}

/* ---------------- standalone artboard ---------------- */
const EM_TW = /*EDITMODE-BEGIN*/{
  "zoom": 100,
  "play": true
}/*EDITMODE-END*/;

function EMApp() {
  const [tweaks, setTweak] = window.useTweaks(EM_TW);
  const ref = emR(null);
  const [fit, setFit] = emS(1);
  const [runId, setRunId] = emS(0);
  emE(() => {
    const f = () => { if (ref.current) setFit(Math.min(1, (ref.current.clientWidth - 44) / 1440)); };
    f();
    window.addEventListener("resize", f);
    return () => window.removeEventListener("resize", f);
  }, []);
  const z = fit * tweaks.zoom / 100;
  return (
    <React.Fragment>
      <div className="em-artstage">
        <div className="em-artbar">
          <span className="em-artbar__t">Intake — designing</span>
          <span className="em-artbar__n">The deck's f2 stack in the main panel; the thinking stays in the chat</span>
          <span className="em-artbar__sp"></span>
          <button onClick={() => setRunId(n => n + 1)}>Replay</button>
          <button onClick={() => setTweak("play", !tweaks.play)}>{tweaks.play ? "Pause" : "Play"}</button>
          <label>Zoom<input type="range" min="50" max="100" step="5" value={tweaks.zoom} onChange={e => setTweak("zoom", +e.target.value)} /><span style={{ width: 32, color: "#fff" }}>{tweaks.zoom}%</span></label>
        </div>
        <div className="em-artbody" ref={ref}>
          <div className="em-artfit" style={{ transform: "scale(" + z + ")", width: 1440, height: 840 }}>
            <EmotiveLoading key={runId} running={tweaks.play} brief={window.EI3 && window.EI3.chat[0].text} />
          </div>
        </div>
      </div>
      <window.TweaksPanel title="Intake — designing">
        <window.TweakSection label="Playback" />
        <window.TweakToggle label="Play" value={tweaks.play} onChange={v => setTweak("play", v)} />
        <window.TweakSlider label="Zoom" value={tweaks.zoom} min={50} max={100} step={5} unit="%" onChange={v => setTweak("zoom", v)} />
      </window.TweaksPanel>
    </React.Fragment>
  );
}

Object.assign(window, { IntakeLoading: EmotiveLoading });
if (!window.__COMBINED) ReactDOM.createRoot(document.getElementById("root")).render(<EMApp />);
