/* thread.jsx — a forked Yoshi chat tied to a single "needs you" proposal.
   When you ask Yoshi about a proposal from its review screen, a thread is
   created and kept for context: the proposal tile is pinned at the top, you
   and Yoshi go back and forth, and when Yoshi revises the proposal the
   original greys out (expires) right here and the new one drops in inline.
   The revised proposal's review screen links back to this same thread. */

/* canned replies + the trigger that makes Yoshi re-propose */
const THREAD_MODIFY_RE = /(make it|smaller|less|half|reduce|lower|safer|trim|only the|wait|push|friday|monday|instead|resize|re-?size|revise|adjust|change it)/i;

const threadReply = (text) => {
  const t = (text || "").toLowerCase();
  if (/risk|why|explain|safe|exposure/.test(t))
  return { t: "Evening it out means trimming your two largest positions (NVDA and Bitcoin) into a broad fund. Less single-name risk, a small realized gain, and you keep most of the upside. The trade-off is you'd hold a bit less of the names that ran up the most." };
  if (/tax|gain|cost basis/.test(t))
  return { t: "The sale realizes about $312 in short-term gains. Small, but it's taxable this year. I can sell the highest-cost lots first to keep it lower if you'd like." };
  if (/when|timing|now|today/.test(t))
  return { t: "I'd do it at the open tomorrow, when spreads are tightest. Nothing moves until you approve, so there's no rush on your end." };
  return { t: "Got it. I can resize it, push the timing, or keep only the safest leg. Tell me which and I'll re-propose it right here." };
};
window.threadReply = threadReply;

/* a proposal tile inside the thread — live (accent, reviewable) or expired (greyed) */
const ThreadProposalTile = ({ p, expired, pinned, onReview }) => {
  if (!p) return null;
  return (
    <div style={{
      marginTop: pinned ? 0 : 7, alignSelf: "flex-start", maxWidth: "76%",
      border: `1px solid ${expired ? "var(--rule)" : "var(--accent)"}`,
      background: "var(--bg-card)", borderRadius: 12, position: "relative",
      opacity: expired ? 0.6 : 1, transition: "opacity 280ms ease, border-color 280ms ease"
    }}>
      <div style={{ padding: "11px 13px 12px" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 7 }}>
          <Eyebrow color={expired ? "var(--ink-3)" : "var(--accent)"}>{expired ? "Expired" : "Proposal"}</Eyebrow>
          {pinned && !expired && <span style={{ marginLeft: "auto", fontFamily: "var(--f-display)", fontSize: 9.5, fontWeight: 600, letterSpacing: "0.06em", textTransform: "uppercase", color: "var(--ink-3)" }}>Pinned</span>}
        </div>
        <div style={{ fontFamily: "var(--f-display)", fontSize: 14.5, fontWeight: 600, letterSpacing: "-0.012em", marginTop: 5, textDecorationLine: expired ? "line-through" : "none", textDecorationColor: "var(--rule-2)" }}>{p.title}</div>
        <div style={{ display: "flex", alignItems: "baseline", gap: 8, marginTop: 7 }}>
          <Money value={p.net} size={12.5} sign color={expired ? "var(--ink-3)" : p.net >= 0 ? "var(--accent-pos)" : "var(--ink)"} dim="var(--ink-3)" />
          <span style={{ fontFamily: "var(--f-mono)", fontSize: 10.5, color: "var(--ink-3)", marginLeft: "auto" }}>{p.settlesVerb || "settles"} {p.settles}</span>
        </div>
        {expired ?
        <div style={{ fontFamily: "var(--f-display)", fontSize: 11, color: "var(--ink-3)", marginTop: 9 }}>Expired. Revised below.</div> :
        <Btn full onClick={onReview} style={{ marginTop: 11, padding: "10px" }}>Review <Icon name="arrow" size={16} color="var(--accent-ink)" /></Btn>}
      </div>
    </div>);

};

const ThreadBubble = ({ m }) => {
  const isUser = m.from === "user";
  return (
    <div className="yo-enter" style={{ display: "flex", justifyContent: isUser ? "flex-end" : "flex-start" }}>
      <div style={{
        maxWidth: "82%", padding: "10px 13px",
        borderRadius: isUser ? "14px 14px 4px 14px" : "14px 14px 14px 4px",
        background: isUser ? "var(--accent)" : "var(--bg-card)",
        color: isUser ? "var(--accent-ink)" : "var(--ink)",
        border: isUser ? "none" : "1px solid var(--rule)",
        fontFamily: "var(--f-display)", fontSize: 14, lineHeight: 1.45, letterSpacing: "-0.003em"
      }}>{m.t}</div>
    </div>);

};

const ThreadTyping = () =>
<div style={{ display: "flex", justifyContent: "flex-start" }}>
    <div style={{ padding: "12px 14px", background: "var(--bg-card)", border: "1px solid var(--rule)", borderRadius: "14px 14px 14px 4px", display: "flex", gap: 4 }}>
      {[0, 1, 2].map((i) => <span key={i} className="yo-pulse" style={{ width: 6, height: 6, borderRadius: 999, background: "var(--ink-3)", animationDelay: `${i * 200}ms` }} />)}
    </div>
  </div>;


const THREAD_CHIPS = ["Make it smaller", "Only the safest leg", "Explain the risk"];

const ProposalThread = ({ tid, thread, findProp, isExpired, onReview, onModify, onPersist, onClose }) => {
  const seed = (thread && thread.msgs) || [];
  const [msgs, setMsgs] = useState(seed);
  const [draft, setDraft] = useState("");
  const [typing, setTyping] = useState(false);
  const scrollRef = useRef(null);
  // tracks whether the live proposal tile is currently within the scroll
  // viewport. After a long chat scrolls it away, the header offers a Review
  // shortcut back to it.
  const liveTileRef = useRef(null);
  const [tileVisible, setTileVisible] = useState(true);

  // the proposal currently live in this thread (the newest non-expired tile)
  const liveId = (() => {
    const tiles = msgs.filter((m) => m.kind === "proposal");
    for (let i = tiles.length - 1; i >= 0; i--) if (!isExpired(tiles[i].id)) return tiles[i].id;
    return tiles.length ? tiles[tiles.length - 1].id : null;
  })();

  const rootId = msgs.find((m) => m.kind === "proposal")?.id;
  const rootP = rootId ? findProp(rootId) : null;

  useEffect(() => { const el = scrollRef.current; if (el) el.scrollTop = el.scrollHeight; }, [msgs, typing]);
  useEffect(() => { onPersist(tid, msgs); }, [msgs]);
  // observe the live proposal tile against the scroll container; hide the
  // header Review shortcut while it's on screen, show it once it scrolls off.
  useEffect(() => {
    const el = liveTileRef.current;
    const root = scrollRef.current;
    if (!el || !root) { setTileVisible(true); return; }
    const io = new IntersectionObserver((entries) => setTileVisible(entries[0].isIntersecting), { root, threshold: 0.01 });
    io.observe(el);
    return () => io.disconnect();
  }, [liveId, msgs.length]);

  const send = (text) => {
    const body = (text != null ? text : draft).trim();
    if (!body) return;
    setDraft("");
    setMsgs((m) => [...m, { from: "user", t: body, time: "now" }]);
    setTyping(true);
    setTimeout(() => {
      setTyping(false);
      const canModify = THREAD_MODIFY_RE.test(body) && liveId && !isExpired(liveId);
      if (canModify) {
        const newId = onModify(liveId); // expires liveId, creates the revised proposal
        setMsgs((m) => [...m,
        { from: "agent", t: "Done. I trimmed it to about half the size, same direction. Here's the revised proposal, ready for you to review.", time: "now" },
        { kind: "proposal", id: newId }]);
      } else {
        setMsgs((m) => [...m, { from: "agent", time: "now", ...threadReply(body) }]);
      }
    }, 1100);
  };

  const attachDoc = (doc) => {
    setMsgs((m) => [...m, { from: "user", t: `Attached ${doc.name}`, time: "now" }]);
    setTyping(true);
    setTimeout(() => { setTyping(false); setMsgs((m) => [...m, { from: "agent", t: attachReplyFor(doc), time: "now" }]); }, 1100);
  };

  return (
    <div className="push-enter" data-screen-label="Proposal thread" style={{ position: "absolute", inset: 0, zIndex: 322, background: "var(--bg)", display: "flex", flexDirection: "column" }}>
      {window.StatusBar && <window.StatusBar />}
      {/* header */}
      <div style={{ flex: "none", padding: window.__YOSHI_WEB ? "18px 16px 12px" : "4px 16px 10px", borderBottom: "1px solid var(--rule)", display: "flex", alignItems: "center", gap: 10 }}>
        <button className="press" onClick={() => liveId ? onReview(liveId) : onClose()} aria-label="Back" style={{ background: "none", border: "none", padding: 6, margin: "0 -6px", display: "grid", placeItems: "center", color: "var(--ink)", cursor: "pointer", flex: "none" }}>
          <Icon name="back" size={22} stroke={1.7} />
        </button>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontFamily: "var(--f-display)", fontSize: 15, fontWeight: 600, letterSpacing: "-0.015em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{rootP ? rootP.title : "Proposal thread"}</div>
          <div style={{ fontFamily: "var(--f-display)", fontSize: 10.5, color: "var(--ink-3)", letterSpacing: "0.05em", textTransform: "uppercase", marginTop: 1 }}>Yoshi · proposal thread</div>
        </div>
        {liveId && !isExpired(liveId) && !tileVisible && (
          <button className="press" onClick={() => onReview(liveId)} aria-label="Review the proposal" title="Review the proposal"
            style={{ flex: "none", display: "inline-flex", alignItems: "center", gap: 5, height: 30, padding: "0 12px", borderRadius: 999, background: "var(--accent)", color: "var(--accent-ink)", border: "none", fontFamily: "var(--f-display)", fontSize: 12.5, fontWeight: 600, letterSpacing: "0.01em", cursor: "pointer", animation: "count-up 200ms ease both" }}>
            Review <Icon name="arrow" size={14} color="var(--accent-ink)" />
          </button>
        )}
        <button className="press" onClick={onClose} aria-label="Close" style={{ background: "none", border: "none", padding: 6, margin: "0 -6px", display: "grid", placeItems: "center", color: "var(--ink-3)", cursor: "pointer", flex: "none" }}>
          <Icon name="close" size={21} stroke={1.7} />
        </button>
      </div>

      {/* thread body */}
      <div className="scroll" ref={scrollRef} style={{ padding: "14px 16px 8px", display: "flex", flexDirection: "column", gap: 10 }}>
        {msgs.map((m, i) =>
        m.kind === "proposal" ?
        <div key={i} ref={m.id === liveId ? liveTileRef : undefined}
          style={i === 0
            ? { display: "flex", position: "sticky", top: 0, zIndex: 5, background: "var(--bg)", paddingBottom: 10 }
            : { display: "flex" }}>
          <ThreadProposalTile p={findProp(m.id)} expired={isExpired(m.id)} pinned={i === 0} onReview={() => onReview(m.id)} />
        </div> :
        <ThreadBubble key={i} m={m} />
        )}
        {typing && <ThreadTyping />}
      </div>

      {/* composer */}
      <div style={{ flex: "none", borderTop: "1px solid var(--rule)" }}>
        <div style={{ display: "flex", gap: 7, overflowX: "auto", padding: "10px 16px 6px" }}>
          {THREAD_CHIPS.map((q) =>
          <button key={q} className="press" onClick={() => send(q)} style={{ flex: "none", padding: "7px 12px", borderRadius: 999, background: "color-mix(in srgb, var(--ink) 8%, var(--bg-2))", border: "none", fontFamily: "var(--f-display)", fontSize: 12, fontWeight: 500, color: "var(--ink)", whiteSpace: "nowrap", cursor: "pointer" }}>{q}</button>
          )}
        </div>
        <div style={{ padding: "4px 14px 16px" }}>
          <YoshiComposer value={draft} onChange={setDraft} onSend={() => send()} placeholder="Message Yoshi about this…" onAttach={attachDoc} />
        </div>
      </div>
    </div>);

};

Object.assign(window, { ProposalThread });
