/* global React, window */
/**
 * side-rail.jsx, in-page section navigation.
 *
 * Three modes:
 * - "permanent": always-open 220px column (currently unused).
 * - "compact":   64px column, expands to 260px on hover (default on both pages).
 * - "floating":  right-edge dot column with hover-revealed labels.
 *
 * Quirk: Active section is detected by scroll position, not IntersectionObserver.
 * Tall sections (>3.3× viewport) can never reach a 0.25 ratio, so an IO threshold
 * would skip them. Instead the active section = the last whose top has crossed
 * a fixed line at 30% of the viewport height.
 */

(() => {
const T = window.TOKENS;
const RAIL_BLUE = T.color.blue;

window.SideRail = function SideRail({ dark, mode = "permanent", sections }) {
  // useState/useEffect are pulled off React inside the function (not at top level).
  // Top-level `const { useState } = React` would collide between classic-script
  // JSX bundles since they share global scope.
  const { useState, useEffect } = React;
  const [active, setActive] = useState(sections[0]?.id || "");
  const [expanded, setExpanded] = useState(false);
  const v = window.useViewport();
  const isMobile = v === "mobile";

  // Scroll-based active-section detection. See file header for rationale.
  // ALWAYS call useEffect (Rules of Hooks); guard the work inside so it's
  // a no-op on mobile where the rail isn't rendered.
  useEffect(() => {
    if (isMobile) return;
    const TRIGGER_RATIO = 0.30;
    const updateActive = () => {
      const trigger = window.innerHeight * TRIGGER_RATIO;
      let current = sections[0]?.id || "";
      for (const s of sections) {
        const el = document.getElementById(s.id);
        if (!el) continue;
        if (el.getBoundingClientRect().top - trigger <= 0) current = s.id;
      }
      setActive(current);
    };
    updateActive();
    window.addEventListener("scroll", updateActive, { passive: true });
    window.addEventListener("resize", updateActive);
    return () => {
      window.removeEventListener("scroll", updateActive);
      window.removeEventListener("resize", updateActive);
    };
  }, [sections, isMobile]);

  // Mobile: hide the rail entirely. The page already has the top nav and
  // section anchors are reachable via the URL hash; an in-page rail at 64px
  // would steal too much horizontal real estate on small screens.
  if (isMobile) return null;

  const ink = dark ? "#fff" : T.color.ink;
  const sub = dark ? "rgba(255,255,255,0.55)" : "rgba(0,0,0,0.45)";
  const surface = dark ? "rgba(15,17,20,0.85)" : "rgba(255,255,255,0.85)";
  const border = dark ? "rgba(255,255,255,0.10)" : "rgba(0,0,0,0.08)";

  const go = (id) => (e) => {
    e.preventDefault();
    const el = document.getElementById(id);
    if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
  };

  // PERMANENT, 220px sticky column, full labels.
  if (mode === "permanent") {
    return (
      <aside style={{
        position: "fixed", top: 96, left: 24, width: 220, zIndex: (window.Z && window.Z.RAIL) || 30,
        padding: 18, background: surface, border: `1px solid ${border}`,
        borderRadius: T.radius.md + 2, backdropFilter: "blur(12px)", WebkitBackdropFilter: "blur(12px)",
      }}>
        <div style={{ fontFamily: T.font.mono, fontSize: 9, letterSpacing: "0.22em", color: sub, marginBottom: 12, paddingBottom: 12, borderBottom: `1px solid ${border}` }}>
          {(window.useLang && window.useLang() === "en") ? "ON THIS PAGE" : "AUF DIESER SEITE"}
        </div>
        <nav style={{ display: "flex", flexDirection: "column", gap: 2 }}>
          {sections.map((s) => {
            const isActive = active === s.id;
            return (
              <a key={s.id} href={"#" + s.id} onClick={go(s.id)} style={{
                display: "flex", alignItems: "center", gap: 10,
                fontFamily: T.font.sans, fontSize: 13, fontWeight: isActive ? 600 : 500,
                color: isActive ? RAIL_BLUE : ink,
                textDecoration: "none", padding: "8px 10px", borderRadius: 6,
                background: isActive ? (dark ? "rgba(15,79,255,0.12)" : "rgba(15,79,255,0.08)") : "transparent",
                transition: "all 0.15s",
              }}>
                <span style={{ fontFamily: T.font.mono, fontSize: 9, color: isActive ? RAIL_BLUE : sub, letterSpacing: "0.18em", minWidth: 22 }}>{s.num}</span>
                <span>{s.label}</span>
              </a>
            );
          })}
        </nav>
      </aside>
    );
  }

  // COMPACT, full-height left column, 64px → 260px on hover.
  if (mode === "compact") {
    const w = expanded ? 260 : 64;
    return (
      <aside
        onMouseEnter={() => setExpanded(true)}
        onMouseLeave={() => setExpanded(false)}
        style={{
          position: "fixed", top: 0, bottom: 0, left: 0, width: w, zIndex: (window.Z && window.Z.RAIL) || 30,
          paddingTop: 96, paddingBottom: 24, paddingLeft: 12, paddingRight: 12,
          background: surface, borderRight: `1px solid ${border}`,
          backdropFilter: "blur(12px)", WebkitBackdropFilter: "blur(12px)",
          transition: "width 0.25s ease", overflow: "hidden",
          display: "flex", flexDirection: "column", justifyContent: "center",
        }}>
        <nav style={{ display: "flex", flexDirection: "column", gap: 6 }}>
          {sections.map((s) => {
            const isActive = active === s.id;
            return (
              <a key={s.id} href={"#" + s.id} onClick={go(s.id)} style={{
                display: "flex", alignItems: "center", gap: 14,
                fontFamily: T.font.sans, fontSize: 13, fontWeight: isActive ? 600 : 500,
                color: isActive ? RAIL_BLUE : ink,
                textDecoration: "none", padding: "14px 8px", borderRadius: 6,
                background: isActive ? (dark ? "rgba(15,79,255,0.12)" : "rgba(15,79,255,0.08)") : "transparent",
                whiteSpace: "nowrap",
              }}>
                <span style={{ fontFamily: T.font.mono, fontSize: 10, color: isActive ? RAIL_BLUE : sub, letterSpacing: "0.18em", minWidth: 28, textAlign: "center" }}>{s.num}</span>
                <span style={{ opacity: expanded ? 1 : 0, transition: "opacity 0.15s" }}>{s.label}</span>
              </a>
            );
          })}
        </nav>
      </aside>
    );
  }

  // FLOATING, right-edge dots, label on hover.
  return (
    <aside style={{ position: "fixed", top: "50%", right: 24, transform: "translateY(-50%)", zIndex: (window.Z && window.Z.RAIL) || 30, display: "flex", flexDirection: "column", gap: 14 }}>
      {sections.map((s) => {
        const isActive = active === s.id;
        return (
          <a key={s.id} href={"#" + s.id} onClick={go(s.id)} title={s.label} style={{
            display: "flex", alignItems: "center", gap: 12, justifyContent: "flex-end",
            textDecoration: "none", color: ink, position: "relative",
          }}
            onMouseEnter={(e) => { const lbl = e.currentTarget.querySelector(".floating-label"); if (lbl) lbl.style.opacity = "1"; }}
            onMouseLeave={(e) => { const lbl = e.currentTarget.querySelector(".floating-label"); if (lbl) lbl.style.opacity = "0"; }}>
            <span className="floating-label" style={{
              opacity: 0, transition: "opacity 0.15s", fontFamily: T.font.sans, fontSize: 12, fontWeight: 500,
              padding: "4px 10px", borderRadius: T.radius.sm, background: surface, border: `1px solid ${border}`, color: ink,
              backdropFilter: "blur(10px)", WebkitBackdropFilter: "blur(10px)",
              pointerEvents: "none", whiteSpace: "nowrap",
            }}>
              <span style={{ color: sub, marginRight: 6 }}>{s.num}</span>{s.label}
            </span>
            <span style={{
              width: isActive ? 28 : 10, height: isActive ? 4 : 10,
              borderRadius: isActive ? 2 : T.radius.pill,
              background: isActive ? RAIL_BLUE : (dark ? "rgba(255,255,255,0.35)" : "rgba(0,0,0,0.30)"),
              transition: "all 0.2s",
            }} />
          </a>
        );
      })}
    </aside>
  );
};
})();
