// enhance.jsx — v2 additions for the git-worktree cheatsheet.
//
// Adds three composable concepts on top of the existing lane content:
//   1. Situation picker            <SituationPicker/>     (+ <TwoEntryPoints/>)
//   2. Live variable substitution  <VarsBuilder/>         (+ useTemplate hook)
//   3. Error/symptom search        <ErrorSearch/>
//
// Nothing here mutates data.jsx or parts.jsx. The bridge to app.jsx is:
//   - the <VarsProvider> wraps the whole app; useTemplate(raw) returns the
//     interpolated string for any code snippet.
//   - tiles dispatch a CustomEvent("cheat:goto", { laneId, modeId? }); the new
//     app.jsx listens and scrolls + switches the lane's mode.
//   - <ErrorSearch> indexes window.CHEAT.LANES[*]…errorTable on mount.

const { useState: uS, useEffect: uE, useMemo: uM, useRef: uR, useContext: uC, createContext } = React;

// =============================================================================
// Variable store
// =============================================================================
const VARS_DEFAULTS = {
  branch:        "feat/swipe-export",
  name:          "swipe-export",
  base:          "dev",
  claudeBranch:  "claude/gallant-wozniak-340807",
};
// derived: <path> = .claude/worktrees/<name>; <claudePath> = same for the
// claude branch (computed from the trailing segment of <claudeBranch>).
const deriveVars = (v) => {
  const claudeLeaf = (v.claudeBranch || "").split("/").filter(Boolean).pop() || "claude-branch";
  return {
    ...v,
    path:       `.claude/worktrees/${v.name || "<name>"}`,
    claudePath: `.claude/worktrees/${claudeLeaf}`,
  };
};

const VarsContext = createContext({ vars: deriveVars(VARS_DEFAULTS), setVars: () => {} });

function VarsProvider({ children }) {
  const [vars, setRaw] = uS(() => {
    try {
      const stored = JSON.parse(localStorage.getItem("cheat-vars") || "{}");
      return { ...VARS_DEFAULTS, ...stored };
    } catch { return { ...VARS_DEFAULTS }; }
  });
  const setVars = (patch) => setRaw((v) => {
    const next = { ...v, ...patch };
    try { localStorage.setItem("cheat-vars", JSON.stringify(next)); } catch {}
    return next;
  });
  const resolved = uM(() => deriveVars(vars), [vars]);
  return <VarsContext.Provider value={{ vars: resolved, setVars, reset: () => setVars(VARS_DEFAULTS) }}>{children}</VarsContext.Provider>;
}

// Token regex matches the placeholders already used throughout data.jsx:
// <branch>, <name>, <path>, <PR#>, <base>, <claudeBranch>. Unknown tokens
// pass through unchanged so existing snippets don't break.
const TOKEN_RE = /<(branch|name|path|base|claudeBranch|claude-branch|claudePath|claude-path)>/g;
function interpolate(raw, vars) {
  if (!raw) return raw;
  return raw.replace(TOKEN_RE, (m, key) => {
    const k = (key === "claude-branch") ? "claudeBranch"
            : (key === "claude-path")   ? "claudePath"
            : key;
    const v = vars[k];
    return v && String(v).trim() ? v : m; // keep the <placeholder> if blank
  });
}
function useTemplate(raw) {
  const { vars } = uC(VarsContext);
  return uM(() => interpolate(raw, vars), [raw, vars]);
}

// =============================================================================
// VarsBuilder — sticky toolbar
// =============================================================================
function VarsBuilder() {
  const { vars, setVars, reset } = uC(VarsContext);
  const [open, setOpen] = uS(true);

  const fields = [
    { k: "branch",       label: "your branch",        ph: "feat/…" },
    { k: "name",         label: "worktree name",      ph: "swipe-export" },
    { k: "base",         label: "base branch",        ph: "dev" },
    { k: "claudeBranch", label: "claude branch",      ph: "claude/…" },
  ];

  return (
    <div style={{
      position: "sticky", top: 0, zIndex: 30,
      background: "var(--cream)",
      borderBottom: open ? "1px solid var(--rule)" : "1px solid transparent",
      margin: "0 -28px", padding: "0 28px",
      backdropFilter: "saturate(140%) blur(6px)",
    }}>
      <div style={{
        maxWidth: 1240, margin: "0 auto",
        display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap",
        padding: open ? "10px 0 12px" : "8px 0",
      }}>
        <button
          onClick={() => setOpen(o => !o)}
          style={{
            display: "inline-flex", alignItems: "center", gap: 8,
            border: "1px solid var(--rule-strong)", background: "var(--paper)",
            borderRadius: 999, padding: "6px 12px 6px 10px",
            cursor: "pointer", color: "var(--ink-soft)",
            fontSize: 12, letterSpacing: "0.08em", textTransform: "uppercase", fontWeight: 600,
          }}
          aria-expanded={open}
          title={open ? "Hide variables" : "Show variables"}
        >
          <span style={{
            width: 18, height: 18, borderRadius: "50%",
            background: "var(--ink)", color: "var(--cream)",
            display: "grid", placeItems: "center",
            fontFamily: "Bricolage Grotesque, sans-serif", fontSize: 11, fontWeight: 800,
          }}>v</span>
          your variables
          <span style={{ marginLeft: 4, transition: "transform .2s", transform: open ? "rotate(180deg)" : "" }}>▾</span>
        </button>
        {open && fields.map(f => (
          <label key={f.k} style={{
            display: "inline-flex", alignItems: "center", gap: 8,
            background: "var(--paper)", border: "1px solid var(--rule)",
            borderRadius: 10, padding: "4px 10px 4px 12px",
            minWidth: 0,
          }}>
            <span style={{
              fontFamily: "Geist Mono, monospace", fontSize: 10,
              letterSpacing: "0.1em", textTransform: "uppercase",
              color: "var(--ink-faint)", whiteSpace: "nowrap",
            }}>&lt;{f.k === "claudeBranch" ? "claude" : f.k}&gt;</span>
            <input
              value={vars[f.k] || ""}
              placeholder={f.ph}
              onChange={(e) => setVars({ [f.k]: e.target.value })}
              style={{
                border: 0, outline: 0, background: "transparent",
                fontFamily: "Geist Mono, monospace", fontSize: 13,
                color: "var(--l2-ink)", width: f.k === "claudeBranch" ? 220 : 150,
                padding: "4px 0",
              }}
              spellCheck={false}
            />
          </label>
        ))}
        {open && (
          <button onClick={reset} style={{
            marginLeft: "auto",
            background: "transparent", border: 0,
            color: "var(--ink-faint)", fontSize: 12, cursor: "pointer",
            fontFamily: "Geist Mono, monospace", letterSpacing: "0.06em", textTransform: "uppercase",
          }} title="Restore defaults">reset</button>
        )}
      </div>
    </div>
  );
}

// =============================================================================
// TwoEntryPoints — the most important axis: who started the work?
// =============================================================================
function TwoEntryPoints({ goto }) {
  return (
    <section style={{ padding: "8px 0 28px" }}>
      <div style={{
        fontFamily: "Geist Mono, monospace", fontSize: 11,
        letterSpacing: "0.14em", textTransform: "uppercase",
        color: "var(--ink-faint)", marginBottom: 8,
      }}>The axis that matters most</div>
      <h2 style={{
        margin: "0 0 14px",
        fontFamily: "Bricolage Grotesque, sans-serif", fontWeight: 700,
        fontSize: "clamp(24px, 3vw, 32px)", letterSpacing: "-0.015em",
        lineHeight: 1.1,
      }}>
        Who started the work? <span style={{
          fontFamily: "Caveat, cursive", color: "var(--l3)", fontWeight: 700, fontSize: "0.82em",
        }}>— first command depends on the answer.</span>
      </h2>
      <div style={{
        display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14,
      }} className="cheat-twoentries">
        <EntryCard
          accent="var(--l2)" accentBg="var(--l2-bg)" accentInk="var(--l2-ink)"
          tag="path A · you"
          title="Without Claude — DIY worktree"
          body="You're starting parallel work yourself. Nothing exists yet — no branch, no worktree, no PR."
          first={`git worktree add <path> -b <branch> <base>`}
          chips={["pnpm install", "git push", "gh pr create"]}
          onClick={() => goto({ laneId: "lane-2", modeId: "manual" })}
          cta="Lane 2 · manual →"
        />
        <EntryCard
          accent="var(--l3)" accentBg="var(--l3-bg)" accentInk="var(--l3-ink)"
          tag="path B · claude"
          title="With Claude — verify & merge"
          body="Claude already pushed a branch (and maybe left a worktree). You're inheriting work, not creating it."
          first={`git fetch origin && git worktree list`}
          chips={["cd .claude/worktrees", "verify", "gh pr merge", "lane 3 cleanup"]}
          onClick={() => goto({ laneId: "lane-2", modeId: "claude" })}
          cta="Lane 2 · with Claude →"
        />
      </div>
    </section>
  );
}

function EntryCard({ accent, accentBg, accentInk, tag, title, body, first, chips, onClick, cta }) {
  const code = useTemplate(first);
  return (
    <button onClick={onClick} style={{
      textAlign: "left", cursor: "pointer",
      background: "var(--paper)", border: "1px solid var(--rule)",
      borderLeft: `4px solid ${accent}`,
      borderRadius: 16, padding: "20px 22px",
      display: "flex", flexDirection: "column", gap: 10,
      transition: "transform .15s, box-shadow .15s",
    }}
    onMouseEnter={(e) => { e.currentTarget.style.transform = "translateY(-2px)"; e.currentTarget.style.boxShadow = `0 14px 32px -22px ${accent}`; }}
    onMouseLeave={(e) => { e.currentTarget.style.transform = ""; e.currentTarget.style.boxShadow = ""; }}
    >
      <div style={{
        fontFamily: "Geist Mono, monospace", fontSize: 10.5,
        letterSpacing: "0.14em", textTransform: "uppercase", color: accentInk,
      }}>{tag}</div>
      <div style={{
        fontFamily: "Bricolage Grotesque, sans-serif", fontWeight: 700,
        fontSize: 20, letterSpacing: "-0.01em", color: "var(--ink)",
      }}>{title}</div>
      <div style={{ color: "var(--ink-soft)", fontSize: 14, lineHeight: 1.5 }}>{body}</div>
      <div style={{
        fontFamily: "Geist Mono, monospace", fontSize: 12.5,
        background: "#1c1814", color: "#e8dfce",
        padding: "10px 12px", borderRadius: 8, overflowX: "auto",
        whiteSpace: "pre",
      }}>
        <span style={{ color: "#7a705c" }}>$ </span>{code}
      </div>
      <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginTop: 4 }}>
        {chips.map((c, i) => (
          <span key={i} style={{
            fontFamily: "Geist Mono, monospace", fontSize: 11,
            background: accentBg, color: accentInk,
            padding: "3px 8px", borderRadius: 999,
          }}>{c}</span>
        ))}
      </div>
      <div style={{
        marginTop: 8, color: accentInk, fontWeight: 600, fontSize: 13,
        fontFamily: "Geist Mono, monospace", letterSpacing: "0.04em",
      }}>{cta}</div>
    </button>
  );
}

// =============================================================================
// SituationPicker — six tiles keyed to "what just happened to me"
// =============================================================================
const SITUATIONS = [
  { id: "fix-main",   icon: "✎",  h: "One-line fix on main",              b: "Branch off · commit · merge back",     goto: { laneId: "lane-1" } },
  { id: "start-par",  icon: "⌥",  h: "Starting parallel work",            b: "New worktree · push · open a PR",      goto: { laneId: "lane-2", modeId: "manual" } },
  { id: "claude-pr",  icon: "★",  h: "Claude opened a PR",                b: "Preflight · verify · squash · sync",   goto: { laneId: "lane-2", modeId: "claude" }, hot: true },
  { id: "stuck",      icon: "✕",  h: "A worktree is stuck",               b: "Unlock · force-remove · prune",        goto: { laneId: "lane-3" } },
  { id: "error",      icon: "!",  h: "I got an error message",            b: "Paste it · find the fix",              action: "focus-error" },
  { id: "dunno",      icon: "?",  h: "I don't know which lane",           b: "Two questions · pick for me",          action: "scroll-flowchart" },
];

function SituationPicker({ onGoto, onAction }) {
  return (
    <section style={{ padding: "8px 0 28px" }}>
      <div style={{
        display: "flex", alignItems: "baseline", justifyContent: "space-between",
        gap: 16, flexWrap: "wrap", marginBottom: 12,
      }}>
        <div>
          <div style={{
            fontFamily: "Geist Mono, monospace", fontSize: 11,
            letterSpacing: "0.14em", textTransform: "uppercase",
            color: "var(--ink-faint)", marginBottom: 4,
          }}>Pick by situation</div>
          <h2 style={{
            margin: 0, fontFamily: "Bricolage Grotesque, sans-serif",
            fontWeight: 700, fontSize: "clamp(24px, 3vw, 32px)",
            letterSpacing: "-0.015em", lineHeight: 1.1,
          }}>What just happened to&nbsp;you?</h2>
        </div>
        <div style={{ color: "var(--ink-faint)", fontSize: 13 }}>
          One tap → straight to the commands. Reference lives below.
        </div>
      </div>
      <div style={{
        display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 10,
      }} className="cheat-sitgrid">
        {SITUATIONS.map(s => (
          <button key={s.id}
            onClick={() => s.goto ? onGoto(s.goto) : onAction(s.action)}
            style={{
              textAlign: "left", cursor: "pointer",
              background: s.hot ? "var(--ink)" : "var(--paper)",
              color: s.hot ? "var(--cream)" : "var(--ink)",
              border: s.hot ? "1px solid var(--ink)" : "1px solid var(--rule)",
              borderRadius: 14, padding: "14px 16px",
              display: "flex", flexDirection: "column", gap: 6,
              transition: "transform .12s, box-shadow .12s, border-color .12s",
              position: "relative",
            }}
            onMouseEnter={(e) => {
              e.currentTarget.style.transform = "translateY(-1px)";
              e.currentTarget.style.boxShadow = "0 10px 24px -16px rgba(0,0,0,0.25)";
              if (!s.hot) e.currentTarget.style.borderColor = "var(--ink)";
            }}
            onMouseLeave={(e) => {
              e.currentTarget.style.transform = "";
              e.currentTarget.style.boxShadow = "";
              if (!s.hot) e.currentTarget.style.borderColor = "var(--rule)";
            }}
          >
            <div style={{
              width: 30, height: 30, borderRadius: 8,
              background: s.hot ? "var(--l3)" : "var(--cream-deep)",
              color: s.hot ? "white" : "var(--ink)",
              display: "grid", placeItems: "center",
              fontFamily: "Geist Mono, monospace", fontWeight: 700, fontSize: 14,
            }}>{s.icon}</div>
            <div style={{
              fontFamily: "Bricolage Grotesque, sans-serif", fontWeight: 700,
              fontSize: 15, letterSpacing: "-0.005em",
            }}>{s.h}</div>
            <div style={{
              color: s.hot ? "rgba(239,233,223,0.7)" : "var(--ink-faint)", fontSize: 12.5,
              fontFamily: "Geist Mono, monospace",
            }}>{s.b}</div>
            <div style={{
              position: "absolute", right: 14, bottom: 12,
              color: s.hot ? "rgba(239,233,223,0.7)" : "var(--ink-faint)",
              fontSize: 14,
            }}>→</div>
          </button>
        ))}
      </div>
    </section>
  );
}

// =============================================================================
// ErrorSearch — fuzzy search across every errorTable entry
// =============================================================================
function buildErrorIndex() {
  const out = [];
  const visit = (entries, where) => {
    if (!entries) return;
    entries.forEach(e => out.push({ ...e, where }));
  };
  const { LANES } = window.CHEAT;
  LANES.forEach(L => {
    visit(L.errorTable, { laneId: L.id, modeId: null, lane: L.n });
    if (L.modes) L.modes.forEach(m => visit(m.errorTable, { laneId: L.id, modeId: m.id, lane: L.n }));
  });
  // a few hand-curated aliases — symptoms not literally in errorTable
  out.push({
    sym: "permission denied · EACCES",
    cause: "You're running as a different user, or the file is owned by root from a previous sudo.",
    fix: "Drop sudo, or fix ownership: sudo chown -R $(whoami) <path>. Never sudo npm.",
    where: { laneId: "lane-2", modeId: "manual", lane: 2 },
  });
  out.push({
    sym: "fast-forward only · not possible",
    cause: "Your local dev moved on (or someone else's commits landed). Merge won't be a fast-forward.",
    fix: "git pull --rebase origin dev — or move to Lane 2 (PR-based).",
    where: { laneId: "lane-1", modeId: null, lane: 1 },
  });
  return out;
}

function ErrorSearch({ onGoto, mountRef }) {
  const [q, setQ] = uS("");
  const [focused, setFocused] = uS(false);
  const ref = uR(null);
  const index = uM(() => buildErrorIndex(), []);

  // expose focus() to parent via ref
  uE(() => {
    if (mountRef) mountRef.current = { focus: () => { ref.current && ref.current.focus(); } };
  }, [mountRef]);

  // global "/" shortcut
  uE(() => {
    const onKey = (e) => {
      const tag = (e.target.tagName || "").toLowerCase();
      if (e.key === "/" && tag !== "input" && tag !== "textarea") {
        e.preventDefault();
        ref.current && ref.current.focus();
      } else if (e.key === "Escape" && document.activeElement === ref.current) {
        setQ("");
        ref.current && ref.current.blur();
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  const matches = uM(() => {
    const needle = q.trim().toLowerCase();
    if (!needle) return [];
    const score = (e) => {
      const hay = `${e.sym} ${e.cause} ${e.fix}`.toLowerCase();
      if (hay.includes(needle)) {
        // boost matches that hit the symptom directly
        return e.sym.toLowerCase().includes(needle) ? 2 : 1;
      }
      // simple word-overlap fallback
      const tokens = needle.split(/\s+/).filter(Boolean);
      const hits = tokens.filter(t => hay.includes(t)).length;
      return hits / tokens.length >= 0.5 ? 0.4 : 0;
    };
    return index
      .map(e => ({ e, s: score(e) }))
      .filter(x => x.s > 0)
      .sort((a, b) => b.s - a.s)
      .slice(0, 6)
      .map(x => x.e);
  }, [q, index]);

  return (
    <section id="error-search" style={{ padding: "8px 0 36px" }}>
      <div style={{
        background: "var(--paper)", border: "1px solid var(--rule)",
        borderRadius: 16, padding: "16px 18px",
        boxShadow: focused ? "0 0 0 3px rgba(232,99,43,0.18)" : "none",
        transition: "box-shadow .15s, border-color .15s",
        borderColor: focused ? "var(--ink)" : "var(--rule)",
      }}>
        <div style={{
          display: "flex", alignItems: "center", gap: 10,
          fontFamily: "Geist Mono, monospace", fontSize: 14,
        }}>
          <span style={{ color: "var(--ink-faint)" }}>⌕</span>
          <input
            ref={ref}
            value={q}
            onFocus={() => setFocused(true)}
            onBlur={() => setFocused(false)}
            onChange={(e) => setQ(e.target.value)}
            placeholder="Paste your error, or describe what broke…"
            style={{
              flex: 1, border: 0, outline: 0, background: "transparent",
              fontFamily: "Geist Mono, monospace", fontSize: 14, color: "var(--ink)",
              padding: "6px 0",
            }}
            spellCheck={false}
          />
          <span style={{
            fontSize: 11, letterSpacing: "0.1em", textTransform: "uppercase",
            color: "var(--ink-faint)", border: "1px solid var(--rule-strong)",
            borderRadius: 6, padding: "3px 6px",
          }}>{focused ? "esc" : "/"}</span>
        </div>
      </div>
      {q.trim() && (
        <div style={{ marginTop: 10, display: "grid", gap: 8 }}>
          {matches.length === 0 && (
            <div style={{
              padding: "14px 16px", borderRadius: 12,
              background: "var(--paper)", border: "1px dashed var(--rule)",
              color: "var(--ink-faint)", fontSize: 13.5,
            }}>
              No matches. Try a fragment of the error message — e.g. <code>already used by worktree</code>, <code>Cannot find module</code>, <code>is locked</code>.
            </div>
          )}
          {matches.map((m, i) => <ErrorResult key={i} match={m} onGoto={onGoto} />)}
        </div>
      )}
    </section>
  );
}

function ErrorResult({ match, onGoto }) {
  const accent =
    match.where.lane === 1 ? "var(--l1)" :
    match.where.lane === 2 ? "var(--l2)" : "var(--l3)";
  const accentBg =
    match.where.lane === 1 ? "var(--l1-bg)" :
    match.where.lane === 2 ? "var(--l2-bg)" : "var(--l3-bg)";
  const accentInk =
    match.where.lane === 1 ? "var(--l1-ink)" :
    match.where.lane === 2 ? "var(--l2-ink)" : "var(--l3-ink)";
  return (
    <div style={{
      background: "var(--paper)", border: "1px solid var(--rule)",
      borderLeft: `3px solid ${accent}`, borderRadius: 12,
      padding: "12px 14px",
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 4 }}>
        <span style={{
          fontFamily: "Geist Mono, monospace", fontSize: 10,
          letterSpacing: "0.12em", textTransform: "uppercase",
          color: accentInk, background: accentBg,
          padding: "2px 8px", borderRadius: 999,
        }}>Lane {match.where.lane}{match.where.modeId ? ` · ${match.where.modeId}` : ""}</span>
        <button onClick={() => onGoto(match.where)} style={{
          marginLeft: "auto", fontSize: 11, fontWeight: 600,
          color: accentInk, background: "transparent", border: 0,
          fontFamily: "Geist Mono, monospace", letterSpacing: "0.06em",
          textTransform: "uppercase", cursor: "pointer",
        }}>open lane →</button>
      </div>
      <div style={{
        fontFamily: "Geist Mono, monospace", fontSize: 13,
        color: "var(--ink)", fontWeight: 600, marginBottom: 4,
      }}>{match.sym}</div>
      <div style={{ color: "var(--ink-soft)", fontSize: 13.5, marginBottom: 6 }}>{match.cause}</div>
      <div style={{
        fontFamily: "Geist Mono, monospace", fontSize: 12.5,
        background: "#1c1814", color: "#e8dfce",
        padding: "8px 10px", borderRadius: 8, overflowX: "auto",
        whiteSpace: "pre",
      }}>{match.fix}</div>
    </div>
  );
}

// =============================================================================
// Export
// =============================================================================
Object.assign(window, {
  VarsContext, VarsProvider, useTemplate, interpolate,
  VarsBuilder, TwoEntryPoints, SituationPicker, ErrorSearch,
});
