/* ============================================================
   Lineage — History (commits) + Commit detail + manual linking
   A session is stored once; commits reference it. Detail resolves
   the shared session, shows sibling commits, and lets a human
   manually attribute an unlinked commit to an agent session.
   ============================================================ */
const { useState: useStateC, useEffect: useEffectC } = React;

/* ---- author chip ---- */
function CommitAuthor({ author, size = 18 }) {
  if (author.kind === "agent") {
    return (
      <span className="cm-author cm-author-agent">
        <span className="cm-agent-ic"><Icon name="spark" size={11} /></span>
        <span className="cm-author-name">{author.name}</span>
      </span>
    );
  }
  return (
    <span className="cm-author">
      <Avatar name={author.name} color={author.color} size={size} />
      <span className="cm-author-name">{author.name}</span>
    </span>
  );
}

function CIDot({ ci }) {
  if (!ci) return null;
  const ok = ci === "passed";
  return (
    <span className={"ci-dot " + (ok ? "ci-ok" : "ci-bad")} title={"checks " + ci}>
      <Icon name={ok ? "check" : "close"} size={11} />
    </span>
  );
}

/* ---- transcript step ---- */
function PromptBy({ by }) {
  if (!by) return null;
  if (by.system) {
    return (
      <span className="pby pby-system">
        <Icon name={by.name === "CI" ? "run" : "clock"} size={11} />{by.name}
      </span>
    );
  }
  return (
    <span className="pby">
      <Avatar name={by.name} color={by.color} size={17} />{by.name}
    </span>
  );
}

function StepNode({ step, turn }) {
  const t = step.t;
  if (t === "prompt") {
    return (
      <React.Fragment>
        {step.resume && (
          <div className="st-resume">
            <span className="st-resume-ic"><Icon name="branch" size={12} /></span>
            <span className="st-resume-text">
              <b>Resumed by {step.by.name}</b>
              {step.from && <span className="st-resume-from"> · picked up from {step.from.name}</span>}
            </span>
          </div>
        )}
        <div className="st st-prompt">
          <span className="st-dot st-dot-prompt"><Icon name="agent" size={11} /></span>
          <div className="st-body">
            <div className="st-prompt-head">
              <span className="st-kicker mono">{turn ? "TURN " + turn : "TASK"}</span>
              <PromptBy by={step.by} />
            </div>
            <div className="st-prompt-text">{step.text}</div>
          </div>
        </div>
      </React.Fragment>
    );
  }
  if (t === "think") {
    return (
      <div className="st st-think">
        <span className="st-dot st-dot-think"><Icon name="spark" size={10} /></span>
        <div className="st-body"><div className="st-think-text">{step.text}</div></div>
      </div>
    );
  }
  if (t === "commit") {
    return (
      <div className="st st-commit">
        <span className="st-dot st-dot-commit"><Icon name="commit" size={11} /></span>
        <div className="st-body">
          <div className="st-commit-row">
            <span className="st-commit-label">Committed</span>
            <span className="st-commit-hash mono">{step.hash}</span>
            {step.repo && <RepoBadge name={step.repo} />}
          </div>
          <div className="st-commit-msg mono">{step.text}</div>
        </div>
      </div>
    );
  }
  const status = step.status || "ok";
  const sc = status === "warn" ? "var(--cost)" : status === "err" ? "var(--danger)" : "var(--tx-3)";
  const icon = t === "test" ? "run" : (step.kind || "run");
  return (
    <div className={"st st-tool" + (step.flag ? " st-flag" : "")}>
      <span className="st-dot st-dot-tool"><Icon name={step.flag ? "flag" : icon} size={11} style={{ color: step.flag ? "var(--cost)" : "var(--tx-3)" }} /></span>
      <div className="st-body st-tool-body">
        <span className="st-tool-label mono">{step.label}</span>
        <span className="st-tool-detail mono">{step.detail}</span>
        <span className="st-tool-status" style={{ color: sc }}>
          {status === "ok" ? <Icon name="check" size={12} /> : status === "warn" ? "!" : "×"}
        </span>
      </div>
    </div>
  );
}

function DiffBlk({ block }) {
  return (
    <div className="diff-block">
      <div className="diff-path mono">{block.path}</div>
      <div className="diff-code mono">
        <div className="diff-hunk">{block.hunk}</div>
        {block.lines.map((ln, i) => (
          <div key={i} className={"diff-line dl-" + (ln.t === "+" ? "add" : ln.t === "-" ? "del" : "ctx")}>
            <span className="dl-gutter">{ln.t === " " ? "" : ln.t}</span>
            <span className="dl-text">{ln.s || "\u00A0"}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

/* Transcript + files + diff for a session (shared by commit & session detail) */
function SessionBody({ session }) {
  const [mode, setMode] = useStateC("full"); // full | prompts
  const turns = window.sessionTurns(session);

  // assign a turn number to each prompt step
  let tc = 0;
  const stepsWithTurn = session.steps.map(st => {
    if (st.t === "prompt") { tc += 1; return { st, turn: tc }; }
    return { st, turn: null };
  });
  const promptSteps = stepsWithTurn.filter(x => x.st.t === "prompt");

  return (
    <React.Fragment>
      <div className="cd-section-title tr-title">
        <span>What the agent did</span>
        <div className="tr-toggle">
          <button className={mode === "full" ? "on" : ""} onClick={() => setMode("full")}>Full transcript</button>
          <button className={mode === "prompts" ? "on" : ""} onClick={() => setMode("prompts")}>
            User messages <span className="trt-count mono">{turns}</span>
          </button>
        </div>
      </div>

      {mode === "full" ? (
        <div className="transcript">
          <span className="tr-rail" />
          {stepsWithTurn.map((x, i) => <StepNode key={i} step={x.st} turn={turns > 1 ? x.turn : null} />)}
        </div>
      ) : (
        <div className="prompts-only">
          {promptSteps.map((x, i) => (
            <div key={i} className={"po-row" + (x.st.resume ? " po-resume" : "")}>
              <div className="po-meta">
                <span className="po-turn mono">{turns > 1 ? "Turn " + x.turn : "Task"}</span>
                <PromptBy by={x.st.by} />
                {x.st.resume && <span className="po-resume-tag mono"><Icon name="branch" size={10} />resumed</span>}
              </div>
              <div className="po-text">{x.st.text}</div>
            </div>
          ))}
        </div>
      )}

      {session.files.length > 0 && (
        <React.Fragment>
          <div className="cd-section-title">
            <span>Files changed in this session</span>
            <span className="cd-section-meta mono">{session.files.length} files</span>
          </div>
          <div className="cd-files">
            {session.files.map((f, i) => (
              <div key={i} className="cd-file">
                <span className="cd-file-path mono">{f.path}</span>
                <span className="cd-file-stat mono">
                  {f.add > 0 && <span className="add">+{f.add}</span>}
                  {f.del > 0 && <span className="del">−{f.del}</span>}
                </span>
              </div>
            ))}
          </div>
          <div className="diff-wrap">
            {session.diff.map((b, i) => <DiffBlk key={i} block={b} />)}
          </div>
        </React.Fragment>
      )}
    </React.Fragment>
  );
}

Object.assign(window, { CommitAuthor, CIDot, StepNode, DiffBlk, SessionBody });

/* ====================  LINK PICKER (manual attribution)  ==================== */
function LinkPicker({ commit, links, onPick, onClose }) {
  useEffectC(() => {
    const h = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", h);
    return () => window.removeEventListener("keydown", h);
  }, [onClose]);

  const sessions = window.sessionList();
  // candidates: orphan sessions first (most likely the missing attribution), then the rest
  const ranked = sessions
    .map(s => ({ s, orphan: window.isOrphan(s.id, links) }))
    .sort((a, b) => (a.orphan === b.orphan ? 0 : a.orphan ? -1 : 1));

  return (
    <>
      <div className="lp-scrim" onClick={onClose} />
      <div className="lp" role="dialog" aria-modal="true">
        <div className="lp-head">
          <div>
            <div className="lp-title">Link an agent session</div>
            <div className="lp-sub mono">{commit.hash} · {commit.message}</div>
          </div>
          <button className="d-close" onClick={onClose}><Icon name="close" size={16} /></button>
        </div>
        <div className="lp-note">Attribute this commit to the agent session that produced the work. Orphan sessions — agent runs with no commit yet — are listed first.</div>
        <div className="lp-list">
          {ranked.map(({ s, orphan }) => (
            <button key={s.id} className="lp-row" onClick={() => onPick(s.id)}>
              <span className="lp-row-ic"><Icon name="spark" size={12} /></span>
              <div className="lp-row-body">
                <div className="lp-row-top">
                  <span className="lp-row-agent">{s.agent.name}</span>
                  <span className="model-chip mono">{s.agent.model}</span>
                  {orphan
                    ? <span className="lp-orphan-tag mono">orphan · no commit</span>
                    : <span className="lp-linked-tag mono">{window.commitsForSession(s.id, links).all.length} commit(s)</span>}
                </div>
                <div className="lp-row-task">{window.sessionTask(s)}</div>
              </div>
              <span className="lp-row-cost mono"><Icon name="coin" size={11} />{fmtUSD(s.cost)}</span>
            </button>
          ))}
        </div>
      </div>
    </>
  );
}

/* ====================  COMMIT LIST  ==================== */
function AttrTag({ commit, links }) {
  const attr = window.attributionOf(commit, links);
  if (attr === "auto") return <span className="cm-badge">agent session</span>;
  if (attr === "manual") {
    const s = window.sessionForCommit(commit, links);
    return <span className="cm-badge cm-badge-manual"><Icon name="link" size={10} />linked · {s.agent.name}</span>;
  }
  return null;
}

function CommitList({ commits, links, onOpen }) {
  const groups = [];
  let cur = null;
  commits.forEach(c => {
    if (!cur || cur.day !== c.day) { cur = { day: c.day, items: [] }; groups.push(cur); }
    cur.items.push(c);
  });
  return (
    <div className="commits">
      {groups.map((g, gi) => (
        <div className="cm-group" key={gi}>
          <div className="cm-day">
            <span className="cm-day-label">{g.day}</span>
            <span className="cm-day-rule" />
          </div>
          <div className="cm-list">
            {g.items.map(c => {
              const attr = window.attributionOf(c, links);
              return (
                <div key={c.hash} className="cm-row" onClick={() => onOpen(c.hash)}>
                  <span className="cm-rail-dot" data-kind={attr ? "agent" : c.author.kind} />
                  <div className="cm-body">
                    <div className="cm-msg">{c.message}</div>
                    <div className="cm-sub">
                      <RepoBadge name={c.repo} />
                      <CommitAuthor author={c.author} />
                      <AttrTag commit={c} links={links} />
                      {c.merge && <span className="cm-merge">merge</span>}
                    </div>
                  </div>
                  <div className="cm-right">
                    <CIDot ci={c.ci} />
                    {(c.add > 0 || c.del > 0) && (
                      <span className="cm-stat mono">
                        <span className="add">+{c.add}</span><span className="del">−{c.del}</span>
                      </span>
                    )}
                    <span className="cm-time mono">{fmtTime(c.ts)}</span>
                    <button className="cm-hash mono" onClick={(e) => { e.stopPropagation(); onOpen(c.hash); }}>
                      {c.hash}<Icon name="chevron" size={12} />
                    </button>
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      ))}
    </div>
  );
}

/* ====================  COMMIT DETAIL  ==================== */
function CommitDetail({ hash, links, linkActions, go, onBack }) {
  const [picking, setPicking] = useStateC(false);
  useEffectC(() => {
    const h = (e) => { if (e.key === "Escape" && !picking) onBack(); };
    window.addEventListener("keydown", h);
    return () => window.removeEventListener("keydown", h);
  }, [onBack, picking]);

  const c = window.LINEAGE_COMMITS.find(x => x.hash === hash);
  const session = window.sessionForCommit(c, links);
  const attr = window.attributionOf(c, links);
  const siblings = session
    ? window.commitsForSession(session.id, links).all.filter(x => x.hash !== c.hash)
    : [];

  return (
    <div className="cd">
      <div className="cd-topbar">
        <button className="cd-back" onClick={onBack}><Icon name="chevron" size={14} style={{ transform: "rotate(180deg)" }} /> History</button>
        <span className="cd-branch mono"><Icon name="branch" size={12} />{c.branch}</span>
      </div>

      <div className="cd-scroll">
        <div className="cd-inner">
          <header className="cd-head">
            <h1 className="cd-message">{c.message}</h1>
            <div className="cd-meta">
              <RepoBadge name={c.repo} />
              <CommitAuthor author={c.author} size={20} />
              <span className="cd-sep">committed {fmtTime(c.ts)}</span>
              <span className="cd-sep">·</span>
              <span className="cd-commit-hash mono">{c.hash}</span>
              <span className="cd-parent mono">parent {c.parent}</span>
              {c.pr && <span className="cd-pr mono"><Icon name="pr" size={12} />#{c.pr}</span>}
              {c.ci && <span className={"cd-ci " + (c.ci === "passed" ? "ok" : "bad")}><Icon name={c.ci === "passed" ? "check" : "close"} size={12} />checks {c.ci}</span>}
              {(c.add > 0 || c.del > 0) && <span className="cd-stat mono"><span className="add">+{c.add}</span> <span className="del">−{c.del}</span></span>}
            </div>
          </header>

          {/* manual-attribution status line */}
          {attr === "manual" && (
            <div className="attr-line attr-manual">
              <Icon name="link" size={14} />
              <span>Manually linked to the <b>{session.agent.name}</b> session by a maintainer.</span>
              <button className="attr-unlink" onClick={() => linkActions.unlink(c.hash)}>Unlink</button>
            </div>
          )}

          {session ? (
            <>
              {/* session summary bar */}
              <div className="cd-session-bar">
                <div className="csb-left">
                  <span className="csb-label mono">AGENT SESSION{attr === "manual" ? " · LINKED" : ""}</span>
                  <span className="csb-trigger">{session.trigger}</span>
                </div>
                <div className="csb-stats mono">
                  <span title="model">{session.agent.model}</span>
                  <span className="csb-div" />
                  <span title="wall time"><Icon name="clock" size={12} />{fmtDur(session.duration)}</span>
                  <span title="tokens"><Icon name="spark" size={12} />{fmtTok(session.tokens.in + session.tokens.out)} tok</span>
                  <span title="cost" className="csb-cost"><Icon name="coin" size={12} />{fmtUSD(session.cost)}</span>
                </div>
              </div>

              {/* sibling commits from the same session — proves no duplication */}
              {siblings.length > 0 && (
                <div className="sib">
                  <div className="sib-head">
                    <Icon name="commit" size={13} style={{ color: "var(--agent-2)" }} />
                    This session produced {siblings.length + 1} commits
                    {window.sessionRepos(session.id, links).length > 1 && <span className="sib-xrepo mono">across {window.sessionRepos(session.id, links).length} repos</span>}
                  </div>
                  <div className="sib-list">
                    {siblings.map(s => (
                      <button key={s.hash} className="sib-row" onClick={() => go.commit(s.hash)}>
                        <RepoBadge name={s.repo} />
                        <span className="sib-hash mono">{s.hash}</span>
                        <span className="sib-msg">{s.message}</span>
                        <span className="sib-stat mono"><span className="add">+{s.add}</span><span className="del">−{s.del}</span></span>
                        <Icon name="arrow" size={13} style={{ color: "var(--tx-4)" }} />
                      </button>
                    ))}
                  </div>
                </div>
              )}

              <div className="cd-viewfull">
                <button className="viewfull-btn" onClick={() => go.session(session.id)}>
                  <Icon name="agent" size={13} /> Open full session
                </button>
                <span className="viewfull-hint mono">the same session, viewed agent-first</span>
              </div>

              <SessionBody session={session} />
            </>
          ) : (
            <div className="cd-unattr">
              <div className="cd-human">
                <Icon name="dot" size={16} style={{ color: "var(--tx-3)" }} />
                <div>
                  <div className="cd-human-title">Authored directly by {c.author.name}</div>
                  <div className="cd-human-sub">No agent session is attributed to this commit.</div>
                </div>
              </div>
              <button className="attr-link-btn" onClick={() => setPicking(true)}>
                <Icon name="link" size={14} /> Link an agent session
              </button>
            </div>
          )}
        </div>
      </div>

      {picking && (
        <LinkPicker
          commit={c}
          links={links}
          onClose={() => setPicking(false)}
          onPick={(sid) => { linkActions.link(c.hash, sid); setPicking(false); }}
        />
      )}
    </div>
  );
}
window.CommitDetail = CommitDetail;

/* ====================  HISTORY VIEW  ==================== */
function HistoryView({ links, onOpen, go, repo, setRepo }) {
  const allCommits = window.LINEAGE_COMMITS;
  const commits = repo === "all" ? allCommits : allCommits.filter(c => c.repo === repo);
  const agentCount = commits.filter(c => window.attributionOf(c, links)).length;
  const repoCount = Object.keys(commits.reduce((m, c) => { m[c.repo] = 1; return m; }, {})).length;
  const sessions = window.sessionList();
  const totalSpend = sessions.reduce((n, s) => n + s.cost, 0);

  return (
    <div className="history">
      <div className="history-inner">
        <div className="history-head">
          <div className="history-title">
            <h2>Commits</h2>
            <span className="history-sub mono">
              {commits.length} commits · {agentCount} from agent sessions · {repo === "all" ? repoCount + " repositories" : <span className="hs-branch">{repo}</span>}
            </span>
          </div>
          <button className="history-spend mono" onClick={() => go.section("agents")} title="see every agent session, including orphans">
            <Icon name="coin" size={13} />{fmtUSD0(totalSpend)} agent spend<Icon name="chevron" size={12} style={{ opacity: 0.5 }} />
          </button>
        </div>
        {repo !== "all" && (
          <div className="scope-note">
            <RepoBadge name={repo} />
            <span>Showing one repository.</span>
            <button className="scope-clear" onClick={() => setRepo("all")}>View all repositories</button>
          </div>
        )}
        <CommitList commits={commits} links={links} onOpen={onOpen} />
      </div>
    </div>
  );
}
window.HistoryView = HistoryView;
