/* global React */
// Sliding-indent tree WITH SIBLINGS at every level.
// Shows how breadth (siblings) coexists with the compress-ancestors-left idea.
// Ancestors don't collapse fully to one x — they keep a small "compressed" step
// so sibling groups stay visually nested; the deepest WINDOW levels get full indent.

const { useState } = 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",
  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"];
const N = 10;

// chosen lineage; .sibs = the children of the chosen node at that level
const SPINE = [
  { node: "Mira K. Adesanya Coastal Adaptation Photographic Archive", chosen: 0,
    sibs: ["Field Studies and On-Site Documentation", "Press & Media Coverage", "Maps, Plans & Surveys", "Oral Histories"] },
  { node: "Field Studies and On-Site Documentation", chosen: 0,
    sibs: ["Europe — Low Countries", "North America — Gulf & Eastern Seaboard", "West Africa", "Asia-Pacific Deltas"] },
  { node: "Europe — Low Countries", chosen: 0, sibs: ["Netherlands", "Belgium", "Luxembourg"] },
  { node: "Netherlands", chosen: 0, sibs: ["Rotterdam Metropolitan Region", "Amsterdam", "Nijmegen", "Dordrecht"] },
  { node: "Rotterdam Metropolitan Region", chosen: 0, sibs: ["Maeslantkering Storm Surge Barrier", "Boompjes Embankment", "Katendrecht Walkway"] },
  { node: "Maeslantkering Storm Surge Barrier", chosen: 0, sibs: ["1998 Commissioning and Acceptance Testing", "2014 Decadal Stress Test", "2021 Structural Inspection"] },
  { node: "1998 Commissioning and Acceptance Testing", chosen: 0, sibs: ["Roll 14 — Second Full-Closure Stress Test", "Roll 15 — Public Observation Crowd", "Contact Sheet A"] },
  { node: "Roll 14 — Second Full-Closure Stress Test", chosen: 1, sibs: ["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, sibs: ["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, sibs: [] },
];

const GUTTER = 12;
const INDENT = 22;     // full indent for the deep window
const COMPRESSED = 9;  // indent for compressed ancestors (kept >0 so siblings stay nested)
const WINDOW = 2;      // deepest N levels keep full indent

const ROW_H = 30;

function unitFor(depth, openMax) { return (openMax - depth) <= WINDOW ? INDENT : COMPRESSED; }
function xForDepth(depth, openMax) {
  let x = GUTTER;
  for (let k = 0; k < depth; k++) x += unitFor(k, openMax);
  return x;
}

const Tri = ({ open }) => (<svg viewBox="0 0 12 12" width="9" height="9" style={{ transform: open ? "rotate(90deg)" : "none", transition: "transform .2s" }}><path d="M4 2l5 4-5 4z" fill="currentColor" /></svg>);
const Folder = ({ s = 13 }) => (<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 = 13 }) => (<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 = 13 }) => (<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>);

function buildItems(openMax) {
  const items = [];
  items.push({ id: "root", name: SPINE[0].node, depth: 0, onPath: true, active: openMax === 0, open: openMax > 0, leaf: false, tag: LV[0] });
  function rec(d, depth) {
    const parent = SPINE[d];
    parent.sibs.forEach((nm, i) => {
      const onPath = i === parent.chosen;
      const active = onPath && depth === openMax;
      const leaf = depth === N - 1;
      const open = onPath && depth < openMax;
      items.push({ id: `${d}.${i}`, name: nm, depth, onPath, active, open, leaf, tag: LV[depth], expandable: onPath });
      if (onPath) {
        if (depth < openMax) rec(d + 1, depth + 1);
        else if (depth < N - 1) {
          // frontier: show the active node's children (all siblings) as closed
          SPINE[depth].sibs.forEach((cn, ci) => items.push({
            id: `f${depth}.${ci}`, name: cn, depth: depth + 1, onPath: false, active: false, open: false,
            leaf: depth + 1 === N - 1, tag: LV[depth + 1], frontier: true, isChosen: ci === SPINE[depth].chosen,
          }));
        }
      }
    });
  }
  if (openMax > 0) rec(0, 1);
  return items;
}

function SiblingTree({ openMax, setOpenMax }) {
  const items = buildItems(openMax);
  // depth rails (on-path levels) to keep structure legible when compressed
  const railX = [];
  for (let d = 0; d < openMax; d++) railX.push(xForDepth(d, openMax) + 18);

  return (
    <div style={{ height: "100%", display: "flex", flexDirection: "column" }}>
      <div style={{ padding: "10px 14px", borderBottom: `1px solid ${T.hairline}`, display: "flex", alignItems: "center", gap: 10 }}>
        <SC style={{ color: T.accent }}>Depth L{openMax + 1} / {N}</SC>
        <span style={{ fontSize: 11, color: T.muted }}>· siblings shown at every level</span>
        <div style={{ marginLeft: "auto", display: "flex", gap: 6 }}>
          <button onClick={() => setOpenMax(Math.max(0, openMax - 1))} style={btn}>− up</button>
          <button onClick={() => setOpenMax(Math.min(N - 1, openMax + 1))} style={btnDark}>down ↓</button>
        </div>
      </div>
      <div style={{ flex: 1, overflow: "auto", padding: "6px 0", position: "relative" }}>
        {/* depth rails */}
        {railX.map((x, i) => (
          <div key={i} style={{ position: "absolute", left: x, top: 4, bottom: 4, width: 1,
            background: T.hairline2, opacity: 0.5, transition: "left .32s cubic-bezier(.4,0,.2,1)" }} />
        ))}
        {items.map((it, idx) => {
          const x = xForDepth(it.depth, openMax);
          const col = it.active ? T.accent : (it.leaf ? T.muted : "#9a6b35");
          return (
            <div key={it.id} onClick={() => { if (it.onPath) setOpenMax(it.depth); else if (it.frontier && it.isChosen) setOpenMax(it.depth); }}
              style={{
                position: "relative", height: ROW_H, display: "flex", alignItems: "center", gap: 7,
                paddingLeft: x, paddingRight: 10,
                background: it.active ? T.accentSoft : "transparent",
                borderLeft: it.active ? `2px solid ${T.accent}` : "2px solid transparent",
                color: it.active ? T.accent : (it.frontier ? T.ink2 : T.ink),
                cursor: "default", whiteSpace: "nowrap",
                transition: "padding-left .32s cubic-bezier(.4,0,.2,1)",
                opacity: it.frontier ? 0.92 : 1,
              }}>
              <span style={{ width: 10, color: T.muted, flexShrink: 0, opacity: it.expandable || it.frontier && !it.leaf ? 1 : 0.0 }}>
                {(!it.leaf) ? <Tri open={it.open} /> : null}
              </span>
              <span style={{ color: col, flexShrink: 0 }}>{it.leaf ? <Doc /> : (it.open ? <FolderOpen /> : <Folder />)}</span>
              <span style={{ fontSize: 12.5, fontWeight: it.active ? 600 : (it.onPath ? 500 : 400), overflow: "hidden", textOverflow: "ellipsis" }}>{it.name}</span>
              {it.active && <span style={{ marginLeft: "auto", fontFamily: T.mono, fontSize: 8.5, color: T.accent, flexShrink: 0, paddingLeft: 8 }}>{it.tag}</span>}
              {!it.active && <span style={{ marginLeft: "auto", fontFamily: T.mono, fontSize: 8.5, color: T.hairline2, flexShrink: 0, paddingLeft: 8 }}>{it.tag}</span>}
            </div>
          );
        })}
      </div>
      <div style={{ padding: "9px 14px", borderTop: `1px solid ${T.hairline}`, background: T.paper2 }}>
        <div style={{ fontSize: 11, color: T.muted, lineHeight: 1.45 }}>
          Ancestors keep a small {COMPRESSED}px step (not zero) so sibling groups stay nested and legible; the deepest
          {" "}{WINDOW + 1} levels get the full {INDENT}px indent. Faint rails mark the path depths.
        </div>
      </div>
    </div>
  );
}

const btn = { background: "transparent", border: `1px solid ${T.hairline2}`, color: T.ink2, padding: "4px 10px", fontFamily: T.sans, fontSize: 11.5, fontWeight: 500, cursor: "default" };
const btnDark = { ...btn, background: T.ink, color: T.paper, border: 0 };

function App() {
  const [openMax, setOpenMax] = useState(3);
  const cur = SPINE[openMax];
  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 }}>· Sliding-indent finding aid — with siblings</span>
      </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" }}>
          <SiblingTree openMax={openMax} setOpenMax={setOpenMax} />
        </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 }}>{LV[openMax]} · level {openMax + 1} of {N}</SC>
            <div style={{ fontFamily: T.serif, fontSize: 23, fontWeight: 500, lineHeight: 1.2, marginTop: 6, textWrap: "balance" }}>{cur.node}</div>
          </div>
        </main>
      </div>
    </div>
  );
}

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