/* global React */
// Five INTERACTIVE prototypes for the deep-tree / long-label problem.
// One app, a mode switcher, shared 10-level data. Each starts drilled to depth
// so the long-label challenge is visible immediately. Click to expand / drill.

const { useState, useRef, useEffect, useLayoutEffect } = React;

const T = {
  paper: "#f3eee2", paper2: "#e6dfc9", surface: "#fbf8ef",
  ink: "#1c1814", ink2: "#3a342c", muted: "#6c655a",
  hairline: "#d8cfba", hairline2: "#c5b99c",
  accent: "#6a1f23", accentInk: "#fbf8ef", accentSoft: "#f0d8d4",
  ramp: ["#1f3a2e", "#2c5544", "#406b51", "#5a7d3a", "#8a7a1f", "#8a4a1f", "#8a2f1f", "#6a1f23", "#5a1f3a", "#3a2b56"],
  serif: '"Newsreader", Georgia, serif',
  sans: '"Geist", ui-sans-serif, system-ui, sans-serif',
  mono: '"Geist Mono", ui-monospace, Menlo, monospace',
};

const LV = ["Fonds", "Series", "Subseries", "Group", "Subgroup", "File", "Subfile", "Item group", "Item", "Component"];

// spine: chosen node name + the children at that level (next level's siblings)
const SPINE = [
  { node: "Mira K. Adesanya Coastal Adaptation Photographic Archive", chosen: 0,
    children: ["Field Studies and On-Site Documentation", "Press & Media Coverage", "Maps, Plans & Surveys", "Oral Histories", "Site Inspection Reports"] },
  { node: "Field Studies and On-Site Documentation", chosen: 0,
    children: ["Europe — Low Countries", "North America — Gulf & Eastern Seaboard", "West Africa", "Asia-Pacific Deltas"] },
  { node: "Europe — Low Countries", chosen: 0, children: ["Netherlands", "Belgium", "Luxembourg"] },
  { node: "Netherlands", chosen: 0, children: ["Rotterdam Metropolitan Region", "Amsterdam", "Nijmegen", "Dordrecht"] },
  { node: "Rotterdam Metropolitan Region", chosen: 0, children: ["Maeslantkering Storm Surge Barrier", "Boompjes Embankment", "Katendrecht Walkway", "Noordereiland Flood Line"] },
  { node: "Maeslantkering Storm Surge Barrier", chosen: 0, children: ["1998 Commissioning and Acceptance Testing", "2014 Decadal Stress Test", "2021 Structural Inspection"] },
  { node: "1998 Commissioning and Acceptance Testing", chosen: 0, children: ["Roll 14 — Second Full-Closure Stress Test", "Roll 15 — Public Observation Crowd", "Contact Sheet A — proof strips"] },
  { node: "Roll 14 — Second Full-Closure Stress Test", chosen: 1, children: ["Frame 21 — Northern Gate Approach", "Frame 22 — Northern Gate Sealing Sequence", "Frame 23 — Barrier Fully Sealed"] },
  { node: "Frame 22 — Northern Gate Sealing Sequence", chosen: 1, children: ["Recto — Silver gelatin print, full frame", "Verso — Engineer's annotation in pencil", "Conservation note — surface abrasion", "Rights & reproduction statement"] },
  { node: "Verso — Engineer's annotation in pencil", chosen: -1, children: [] },
];

// Build a nested tree. Main spine follows SPINE; siblings get 2 shallow children.
function genLeaf(d, name) { return { lv: LV[d], name, children: [] }; }
function sibling(d, name) {
  return { lv: LV[d], name, children: d < 9 ? [genLeaf(Math.min(d + 1, 9), name + " — part I"), genLeaf(Math.min(d + 1, 9), name + " — part II")] : [] };
}
function buildSpine(d) {
  const s = SPINE[d];
  const node = { lv: LV[d], name: s.node, children: [] };
  if (d < 9) s.children.forEach((c, i) => node.children.push(i === s.chosen ? buildSpine(d + 1) : sibling(d + 1, c)));
  return node;
}
const ROOT = buildSpine(0);
const CHOSEN_PATH = (() => { const p = []; for (let d = 0; d < 9; d++) p.push(SPINE[d].chosen); return p; })();

function nodeAt(path) { let n = ROOT; for (const i of path) n = n.children[i]; return n; }
function eqPath(a, b) { return a.length === b.length && a.every((v, i) => v === b[i]); }
function midTrunc(s, max) { if (s.length <= max) return s; const h = Math.floor((max - 1) / 2); return s.slice(0, h) + "…" + s.slice(s.length - h); }

/* icons */
const Tri = ({ open }) => (<svg viewBox="0 0 12 12" width="9" height="9" style={{ transform: open ? "rotate(90deg)" : "none", transition: "transform .15s" }}><path d="M4 2l5 4-5 4z" fill="currentColor" /></svg>);
const Folder = ({ s = 14 }) => (<svg viewBox="0 0 24 24" width={s} height={s} fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M3 7h6l2 2h10v10H3z" /></svg>);
const FolderOpen = ({ s = 14 }) => (<svg viewBox="0 0 24 24" width={s} height={s} fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M3 7h6l2 2h10v3H3z" /><path d="M3 12h17l-2 7H3z" /></svg>);
const Doc = ({ s = 14 }) => (<svg viewBox="0 0 24 24" width={s} height={s} fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M6 3h9l4 4v14H6z" /><path d="M14 3v5h5" /></svg>);
const SC = ({ children, style }) => (<span style={{ fontFamily: T.sans, fontSize: 9, letterSpacing: "0.13em", textTransform: "uppercase", fontWeight: 600, color: T.muted, ...style }}>{children}</span>);

/* ───────── shared classic tree (modes 3 & 4) ───────── */
function TreeView({ path, setPath, expanded, setExpanded, capped, wrapSelected }) {
  function row(node, idxPath, depth) {
    const key = idxPath.join(".");
    const hasKids = node.children && node.children.length > 0;
    const isOpen = expanded[key];
    const sel = eqPath(idxPath, path);
    const indentLvl = capped ? Math.min(depth, 3) : depth;
    const label = capped ? midTrunc(node.name, 32) : node.name;
    return (
      <div key={key}>
        <div onClick={() => { setPath(idxPath); if (hasKids) setExpanded(e => ({ ...e, [key]: !e[key] })); }}
          style={{ display: "flex", gap: 6, alignItems: wrapSelected && sel ? "flex-start" : "center",
            padding: wrapSelected && sel ? "7px 10px" : "5px 10px", paddingLeft: 8 + indentLvl * 16,
            background: sel ? T.accentSoft : "transparent", borderLeft: sel ? `2px solid ${T.accent}` : "2px solid transparent",
            color: sel ? T.accent : T.ink, cursor: "default" }}>
          <span style={{ width: 10, color: T.muted, marginTop: wrapSelected && sel ? 3 : 0, flexShrink: 0 }}>{hasKids ? <Tri open={isOpen} /> : null}</span>
          {capped && (<span style={{ fontFamily: T.mono, fontSize: 8, color: depth > 3 ? T.accent : T.muted, border: `1px solid ${depth > 3 ? T.accent : T.hairline2}`, padding: "0 3px", flexShrink: 0, marginTop: wrapSelected && sel ? 2 : 0 }}>L{depth + 1}</span>)}
          <span style={{ color: sel ? T.accent : (hasKids ? "#9a6b35" : T.muted), flexShrink: 0, marginTop: wrapSelected && sel ? 1 : 0 }}>{hasKids ? (isOpen ? <FolderOpen s={13} /> : <Folder s={13} />) : <Doc s={13} />}</span>
          <span style={{ fontSize: 12.5, fontWeight: sel ? 600 : 400, lineHeight: 1.3,
            whiteSpace: (wrapSelected && sel) ? "normal" : "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{label}</span>
        </div>
        {hasKids && isOpen && node.children.map((c, i) => row(c, [...idxPath, i], depth + 1))}
      </div>
    );
  }
  return <div style={{ padding: "8px 0", overflow: "auto", height: "100%" }}>{row(ROOT, [], 0)}</div>;
}

/* ───────── 1 · ratcheting re-root ───────── */
function ReRoot({ path, setPath }) {
  const cur = nodeAt(path);
  const crumbs = path.map((_, i) => nodeAt(path.slice(0, i + 1)));
  return (
    <div style={{ height: "100%", display: "flex", flexDirection: "column" }}>
      <div style={{ background: T.paper2, borderBottom: `1px solid ${T.hairline}`, padding: "8px 12px" }}>
        <SC>Path · tap to jump up</SC>
        <div style={{ marginTop: 4 }}>
          <div onClick={() => setPath([])} style={{ display: "flex", gap: 6, alignItems: "center", padding: "1px 0", color: path.length === 0 ? T.accent : T.muted, fontSize: 11, fontWeight: path.length === 0 ? 600 : 400 }}>
            <span style={{ width: 4, height: 4, borderRadius: 9, background: T.hairline2 }} />{ROOT.name}</div>
          {crumbs.map((n, i) => (
            <div key={i} onClick={() => setPath(path.slice(0, i + 1))} style={{ display: "flex", gap: 6, alignItems: "center", padding: "1px 0", paddingLeft: (i + 1) * 8, color: i === crumbs.length - 1 ? T.accent : T.muted, fontSize: 11, fontWeight: i === crumbs.length - 1 ? 600 : 400 }}>
              <span style={{ width: 4, height: 4, borderRadius: 9, background: T.hairline2 }} />
              <span style={{ whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{n.name}</span></div>
          ))}
        </div>
      </div>
      <div style={{ padding: "12px 14px 8px" }}>
        <SC style={{ color: T.accent }}>{cur.lv} · L{path.length + 1}</SC>
        <div style={{ fontFamily: T.serif, fontSize: 17, fontWeight: 500, marginTop: 2, lineHeight: 1.25 }}>{cur.name}</div>
      </div>
      <div style={{ overflow: "auto", flex: 1 }}>
        {cur.children.length > 0 ? (<>
          <div style={{ padding: "2px 14px 6px" }}><SC>Contains</SC></div>
          {cur.children.map((c, i) => (
            <div key={i} onClick={() => setPath([...path, i])} style={{ display: "flex", gap: 9, alignItems: "center", padding: "9px 14px", borderBottom: `1px solid ${T.hairline}`, cursor: "default" }}>
              <span style={{ color: c.children.length ? "#9a6b35" : T.muted }}>{c.children.length ? <Folder /> : <Doc />}</span>
              <span style={{ fontSize: 13, lineHeight: 1.3 }}>{c.name}</span>
              {c.children.length > 0 && <span style={{ marginLeft: "auto", color: T.hairline2 }}><Tri /></span>}
            </div>
          ))}
        </>) : <div style={{ padding: "12px 14px", color: T.muted, fontStyle: "italic", fontFamily: T.serif }}>Leaf — end of branch.</div>}
      </div>
    </div>
  );
}

/* ───────── 2 · miller columns ───────── */
function Miller({ path, setPath }) {
  const scroller = useRef(null);
  useEffect(() => { if (scroller.current) scroller.current.scrollLeft = scroller.current.scrollWidth; }, [path]);
  const cols = [];
  for (let k = 0; k <= path.length; k++) {
    const parent = nodeAt(path.slice(0, k));
    if (!parent.children || parent.children.length === 0) break;
    cols.push({ k, items: parent.children, selIdx: path[k] });
  }
  return (
    <div ref={scroller} style={{ height: "100%", display: "flex", overflowX: "auto" }}>
      {cols.map(({ k, items, selIdx }) => (
        <div key={k} style={{ width: 168, flexShrink: 0, borderRight: `1px solid ${T.hairline}`, background: T.surface, overflowY: "auto" }}>
          <div style={{ padding: "7px 10px", borderBottom: `1px solid ${T.hairline}`, position: "sticky", top: 0, background: T.paper2 }}><SC style={{ fontSize: 8 }}>{LV[k]}</SC></div>
          {items.map((it, i) => {
            const on = i === selIdx;
            return (
              <div key={i} onClick={() => setPath([...path.slice(0, k), i])} style={{ display: "flex", gap: 6, alignItems: "flex-start", padding: "7px 10px", background: on ? T.accent : "transparent", color: on ? T.accentInk : T.ink, cursor: "default" }}>
                <span style={{ marginTop: 1, color: on ? T.accentInk : (it.children.length ? "#9a6b35" : T.muted) }}>{it.children.length ? <Folder s={12} /> : <Doc s={12} />}</span>
                <span style={{ fontSize: 12, lineHeight: 1.3 }}>{it.name}</span>
                {it.children.length > 0 && <span style={{ marginLeft: "auto", color: on ? T.accentInk : T.hairline2, marginTop: 2 }}><Tri /></span>}
              </div>
            );
          })}
        </div>
      ))}
    </div>
  );
}

/* ───────── 5 · indent-free outline ───────── */
function Outline({ path, setPath }) {
  const cur = nodeAt(path);
  const chain = path.map((_, i) => ({ node: nodeAt(path.slice(0, i + 1)), depth: i + 1 }));
  const rows = [{ node: ROOT, depth: 0 }, ...chain];
  return (
    <div style={{ height: "100%", overflow: "auto", padding: "8px 0" }}>
      {rows.map((r, i) => {
        const sel = i === rows.length - 1;
        return (
          <div key={i} onClick={() => setPath(path.slice(0, i))} style={{ display: "grid", gridTemplateColumns: "6px 26px 1fr", gap: 8, alignItems: "center", padding: "7px 12px", background: sel ? T.accentSoft : "transparent", borderBottom: `1px solid ${T.hairline}`, cursor: "default" }}>
            <span style={{ width: 6, height: 20, background: T.ramp[r.depth], borderRadius: 2 }} />
            <span style={{ fontFamily: T.mono, fontSize: 9, color: T.muted, textAlign: "center" }}>L{r.depth + 1}</span>
            <div style={{ minWidth: 0 }}><SC style={{ fontSize: 8, color: sel ? T.accent : T.muted, display: "block" }}>{r.node.lv}</SC>
              <span style={{ fontSize: 12.5, fontWeight: sel ? 600 : 400, color: sel ? T.accent : T.ink, lineHeight: 1.25, display: "block" }}>{r.node.name}</span></div>
          </div>
        );
      })}
      {cur.children.length > 0 && (<>
        <div style={{ padding: "10px 12px 4px" }}><SC>Open from here</SC></div>
        {cur.children.map((c, i) => (
          <div key={i} onClick={() => setPath([...path, i])} style={{ display: "grid", gridTemplateColumns: "6px 26px 1fr auto", gap: 8, alignItems: "center", padding: "7px 12px", cursor: "default" }}>
            <span style={{ width: 6, height: 18, background: T.ramp[Math.min(path.length + 1, 9)], borderRadius: 2, opacity: 0.4 }} />
            <span style={{ fontFamily: T.mono, fontSize: 9, color: T.hairline2, textAlign: "center" }}>L{path.length + 2}</span>
            <span style={{ fontSize: 12.5, lineHeight: 1.25 }}>{c.name}</span>
            {c.children.length > 0 && <span style={{ color: T.hairline2 }}><Tri /></span>}
          </div>
        ))}
      </>)}
    </div>
  );
}

/* ───────── app shell ───────── */
const MODES = [
  { id: "reroot", label: "1 · Re-root" },
  { id: "miller", label: "2 · Columns" },
  { id: "capped", label: "3 · Capped" },
  { id: "wrap", label: "4 · Wrap" },
  { id: "outline", label: "5 · Outline" },
];

function App() {
  const [mode, setMode] = useState("reroot");
  const [path, setPath] = useState(CHOSEN_PATH.slice());
  const [expanded, setExpanded] = useState(() => { const e = {}; for (let k = 0; k <= CHOSEN_PATH.length; k++) e[CHOSEN_PATH.slice(0, k).join(".")] = true; return e; });
  const cur = nodeAt(path);

  return (
    <div style={{ height: "100vh", display: "flex", flexDirection: "column", background: T.paper2, fontFamily: T.sans, color: T.ink }}>
      <div style={{ height: 52, background: T.paper, borderBottom: `1px solid ${T.hairline}`, display: "flex", alignItems: "center", padding: "0 18px", gap: 12, flexShrink: 0 }}>
        <span style={{ width: 26, height: 26, background: T.ink, color: T.paper, display: "grid", placeItems: "center", fontFamily: T.serif, fontWeight: 600, fontSize: 13 }}>E</span>
        <span style={{ fontFamily: T.serif, fontWeight: 600, fontSize: 15 }}>Eastbrook Libraries</span>
        <span style={{ color: T.muted, fontSize: 12.5 }}>· Finding aid — deep tree prototypes</span>
        <div style={{ marginLeft: "auto", display: "inline-flex", border: `1px solid ${T.hairline2}`, overflow: "hidden" }}>
          {MODES.map(m => (
            <button key={m.id} onClick={() => setMode(m.id)} style={{ background: mode === m.id ? T.ink : "transparent", color: mode === m.id ? T.paper : T.ink2, border: 0, padding: "7px 12px", fontFamily: T.sans, fontSize: 12, fontWeight: 500, cursor: "default", borderRight: `1px solid ${T.hairline2}` }}>{m.label}</button>
          ))}
        </div>
      </div>

      <div style={{ flex: 1, display: "grid", gridTemplateColumns: "380px 1fr", minHeight: 0 }}>
        <aside style={{ borderRight: `1px solid ${T.hairline}`, minHeight: 0, background: T.surface, overflow: "hidden" }}>
          {mode === "reroot" && <ReRoot path={path} setPath={setPath} />}
          {mode === "miller" && <Miller path={path} setPath={setPath} />}
          {mode === "capped" && <TreeView path={path} setPath={setPath} expanded={expanded} setExpanded={setExpanded} capped />}
          {mode === "wrap" && <TreeView path={path} setPath={setPath} expanded={expanded} setExpanded={setExpanded} wrapSelected />}
          {mode === "outline" && <Outline path={path} setPath={setPath} />}
        </aside>
        <main style={{ background: T.paper2, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 14, padding: 40, textAlign: "center" }}>
          <div style={{ width: 240, height: 176, background: "linear-gradient(135deg,#1f3a2e,#406b51 60%,#d6e4d8)" }} />
          <div style={{ maxWidth: 460 }}>
            <SC style={{ color: T.accent }}>{cur.lv} · level {path.length + 1} of 10</SC>
            <div style={{ fontFamily: T.serif, fontSize: 23, fontWeight: 500, lineHeight: 1.2, marginTop: 6, textWrap: "balance" }}>{cur.name}</div>
            <div style={{ fontFamily: T.mono, fontSize: 11, color: T.muted, marginTop: 8 }}>MS-2078 · the reading pane always shows the full title</div>
          </div>
        </main>
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
