/* App shell — router and state. */
/* global React, ReactDOM, Brand, ScreenNav,
   Landing, Workspace, Pyramid, Heatmap, Workshop,
   emptySession, sessionFromSample, priorityCounts,
   encodeSession, decodeSession, readSessionFromHash, writeSessionToHash,
   clearSessionHash, currentShareUrl, summarizeEncoded,
   useTweaks, TweaksPanel, TweakSection, TweakRadio, TweakColor, TweakToggle */

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "leadColor":   "#685ad9",
  "followColor": "#10b981",
  "showGhostLabels": true,
  "compactWorkspace": false,
  "posterLayout": "stepped"
}/*EDITMODE-END*/;

function App() {
  const [screen, setScreen] = useState("landing");
  const [project, setProject] = useState("");
  const [mode, setMode] = useState("self");          // 'self' | 'workshop'
  const [session, setSession] = useState(() => emptySession());

  // Share / restore — peek synchronously at load so the state-sync effect
  // never sees a "no restore yet, empty session" gap and clobbers the hash.
  const [restorePrompt, setRestorePrompt] = useState(() => {
    const encoded = (typeof window !== "undefined") ? readSessionFromHash() : null;
    if (!encoded) return null;
    return summarizeEncoded(encoded);
  });
  const [shareOpen, setShareOpen] = useState(false);

  // If the hash was present but malformed, summarizeEncoded() returned null
  // above — strip it once on mount so the URL doesn't look broken.
  useEffect(() => {
    if (restorePrompt) return;
    if (readSessionFromHash()) clearSessionHash();
    // run once
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Keep the URL hash in lock-step with state, so a refresh preserves work.
  // Suspended while the restore modal is up — otherwise we'd overwrite the
  // saved hash with the empty default session before the user decides.
  useEffect(() => {
    if (restorePrompt) return;
    const hasMeaningfulState =
      !!project ||
      screen !== "landing" ||
      Object.values(session.priorities || {}).some(p => p && p !== "unset");
    if (!hasMeaningfulState) {
      clearSessionHash();
      return;
    }
    const encoded = encodeSession({ project, mode, screen, session });
    writeSessionToHash(encoded);
  }, [screen, project, mode, session, restorePrompt]);

  const applyRestore = (decoded) => {
    setProject(decoded.project);
    setMode(decoded.mode);
    setSession(decoded.session);
    setScreen(decoded.screen);
    setRestorePrompt(null);
  };
  const dismissRestore = () => {
    clearSessionHash();
    setRestorePrompt(null);
  };

  // Tweaks
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);

  // Apply tweak colours as CSS variables on root
  useEffect(() => {
    const r = document.documentElement;
    r.style.setProperty("--c-violet", tweaks.leadColor);
    r.style.setProperty("--c-emerald", tweaks.followColor);
    if (tweaks.showGhostLabels) {
      r.style.setProperty("--ghost-display", "inline-flex");
    } else {
      r.style.setProperty("--ghost-display", "none");
    }
  }, [tweaks.leadColor, tweaks.followColor, tweaks.showGhostLabels]);

  const screens = {
    landing:   <Landing   project={project} setProject={setProject} setSession={setSession} setScreen={setScreen} mode={mode} setMode={setMode} />,
    workspace: <Workspace project={project} session={session} setSession={setSession} setScreen={setScreen} mode={mode} />,
    pyramid:   <Pyramid   project={project} session={session} setScreen={setScreen} mode={mode} />,
    heatmap:   <Heatmap   project={project} session={session} setScreen={setScreen} mode={mode} />,
    workshop:  <Workshop  project={project} session={session} setScreen={setScreen} mode={mode} />,
  };

  return (
    <div className="app">
      {/* Mode banner (only when workshop) */}
      {mode === "workshop" && screen !== "landing" && (
        <div className="mode-banner">
          <span className="dot" />
          Workshop mode · Projected to room
        </div>
      )}

      <header className="app-bar">
        <div className="app-bar-inner">
          <Brand />
          {project && screen !== "landing" && (
            <span className="project-pill">
              <span className="dot" />
              {project}
            </span>
          )}
          <ScreenNav screen={screen} setScreen={setScreen} />
          <ShareButton
            open={shareOpen}
            setOpen={setShareOpen}
            project={project}
            mode={mode}
            screen={screen}
            session={session}
          />
        </div>
      </header>

      <main className="app-main" data-screen-label={screen}>
        {screens[screen]}
      </main>

      {restorePrompt && (
        <RestoreModal
          summary={restorePrompt}
          onRestore={() => applyRestore(restorePrompt.decoded)}
          onDismiss={dismissRestore}
        />
      )}

      <TweaksPanel title="Tweaks">
        <TweakSection title="Priority colors" subtitle="Recolor the lead and follow accents across all five screens.">
          <TweakColor
            label="Lead"
            value={tweaks.leadColor}
            options={["#685ad9", "#4338ca", "#d946ef", "#0ea5e9", "#dc2626"]}
            onChange={(v) => setTweak("leadColor", v)}
          />
          <TweakColor
            label="Follow"
            value={tweaks.followColor}
            options={["#10b981", "#059669", "#0d9488", "#84cc16", "#f59e0b"]}
            onChange={(v) => setTweak("followColor", v)}
          />
        </TweakSection>

        <TweakSection title="Poster" subtitle="What lands on the wall.">
          <TweakToggle
            label="Show ghost out-of-scope labels"
            checked={tweaks.showGhostLabels}
            onChange={(v) => setTweak("showGhostLabels", v)}
            help="The framework's durability is itself the message — out-of-scope leaves stay visible but de-emphasized."
          />
        </TweakSection>

        <TweakSection title="Jump to a screen" subtitle="Skip the flow.">
          <TweakRadio
            label="Screen"
            value={screen}
            options={[
              { value: "landing", label: "Landing" },
              { value: "workspace", label: "Workspace" },
              { value: "pyramid", label: "Pyramid" },
              { value: "heatmap", label: "Heatmap" },
              { value: "workshop", label: "Workshop" },
            ]}
            onChange={(v) => setScreen(v)}
          />
          <TweakRadio
            label="Mode"
            value={mode}
            options={[
              { value: "self", label: "Self-serve" },
              { value: "workshop", label: "Workshop" },
            ]}
            onChange={(v) => setMode(v)}
          />
        </TweakSection>

        <TweakSection title="Quick seed" subtitle="Populate with the canonical example.">
          <button className="btn btn-ghost" style={{ width: "100%", justifyContent: "center" }} onClick={() => {
            setProject(window.FRAMEWORK.sample.project);
            setSession(sessionFromSample());
            setScreen("workspace");
          }}>Load nanobody example</button>
        </TweakSection>
      </TweaksPanel>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
window.FRAMEWORK_READY.then(() => {
  root.render(<App />);
});

// ============================================================================
// Share button — top app bar. Opens a popover with the current sharable URL.
// ============================================================================
function ShareButton({ open, setOpen, project, mode, screen, session }) {
  const wrapRef = useRef(null);
  const [copied, setCopied] = useState(false);

  useEffect(() => {
    if (!open) return;
    const onDown = (e) => {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
    };
    document.addEventListener("mousedown", onDown);
    return () => document.removeEventListener("mousedown", onDown);
  }, [open]);

  // Recompute the URL every time the popover opens — so it reflects the
  // latest state even if the parent's state-sync effect hasn't fired yet.
  const url = useMemo(() => {
    const encoded = encodeSession({ project, mode, screen, session });
    return currentShareUrl(encoded);
  }, [project, mode, screen, session, open]);

  const ready = !!project || screen !== "landing" ||
    Object.values(session.priorities || {}).some(p => p && p !== "unset");

  const copy = async () => {
    try {
      await navigator.clipboard.writeText(url);
      setCopied(true);
      setTimeout(() => setCopied(false), 1800);
    } catch (_) {
      // Fallback for non-clipboard environments — select the input.
      const el = document.getElementById("share-url-input");
      if (el) { el.focus(); el.select(); }
    }
  };

  return (
    <div ref={wrapRef} style={{ position: "relative", marginLeft: "auto" }}>
      <button
        onClick={() => ready && setOpen(o => !o)}
        disabled={!ready}
        title={ready ? "Share a link to this session" : "Pick at least one priority to share"}
        aria-expanded={open}
        style={{
          appearance: "none",
          display: "inline-flex", alignItems: "center", gap: 8,
          padding: "8px 14px",
          height: 36,
          borderRadius: 999,
          border: "1px solid var(--border-default)",
          background: open ? "var(--surface-navy)" : "rgba(255,255,255,0.6)",
          color: open ? "var(--fg-on-dark)" : "var(--fg-2)",
          fontFamily: "var(--font-jakarta)", fontWeight: 600, fontSize: 12,
          letterSpacing: "-0.005em",
          cursor: ready ? "pointer" : "not-allowed",
          opacity: ready ? 1 : 0.45,
          transition: "all 160ms",
        }}
      >
        <ShareIcon />
        <span>Share</span>
      </button>
      {open && (
        <div role="dialog" aria-label="Share session"
          style={{
            position: "absolute",
            top: "calc(100% + 10px)", right: 0,
            width: 420,
            background: "var(--surface-paper)",
            border: "1px solid var(--border-strong)",
            borderRadius: 16,
            boxShadow: "0 24px 60px rgba(34,49,83,0.18), 0 4px 14px rgba(34,49,83,0.06)",
            padding: 18, zIndex: 50,
          }}
        >
          <span style={{
            position: "absolute", top: -7, right: 22,
            width: 12, height: 12, transform: "rotate(45deg)",
            background: "var(--surface-paper)",
            borderTop: "1px solid var(--border-strong)",
            borderLeft: "1px solid var(--border-strong)",
          }} />
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 8 }}>
            <span style={{ fontFamily: "var(--font-mono)", fontSize: 10, letterSpacing: "0.18em", color: "var(--c-violet-deep)" }}>
              SHARE · RESTORE
            </span>
            <button onClick={() => setOpen(false)} aria-label="Close"
              style={{ appearance: "none", border: 0, background: "transparent", color: "var(--fg-4)", cursor: "pointer", fontSize: 16, padding: 4 }}>×</button>
          </div>
          <p style={{
            fontFamily: "var(--font-serif)", fontSize: 13, color: "var(--fg-3)",
            lineHeight: 1.5, margin: "0 0 12px",
          }}>
            The whole session is encoded into the link. Send it to a collaborator
            or bookmark it for later — they’ll be asked before anything loads.
          </p>
          <div style={{
            display: "flex", gap: 8, alignItems: "stretch",
            background: "rgba(255,255,255,0.7)",
            border: "1px solid var(--border-default)",
            borderRadius: 10, padding: 6,
          }}>
            <input
              id="share-url-input"
              readOnly
              value={url}
              onFocus={(e) => e.target.select()}
              style={{
                flex: 1, border: 0, background: "transparent",
                fontFamily: "var(--font-mono)", fontSize: 11,
                color: "var(--fg-2)", outline: "none",
                padding: "6px 8px",
              }}
            />
            <button
              className="btn btn-primary"
              onClick={copy}
              style={{ padding: "6px 14px", fontSize: 12, whiteSpace: "nowrap" }}
            >{copied ? "Copied ✓" : "Copy"}</button>
          </div>
          <div style={{
            marginTop: 12, fontFamily: "var(--font-mono)", fontSize: 9.5,
            letterSpacing: "0.12em", color: "var(--fg-5)",
          }}>
            v1 · STATELESS · NO BACKEND
          </div>
        </div>
      )}
    </div>
  );
}

function ShareIcon() {
  return (
    <svg width="14" height="14" viewBox="0 0 16 16" fill="none" style={{ display: "block" }}>
      <circle cx="12" cy="3.5" r="2" fill="currentColor" />
      <circle cx="4" cy="8" r="2" fill="currentColor" />
      <circle cx="12" cy="12.5" r="2" fill="currentColor" />
      <path d="M5.7 7 L10.3 4.5 M5.7 9 L10.3 11.5" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
    </svg>
  );
}

// ============================================================================
// Restore modal — shown on initial load when the URL carries a session.
// Names the project, shows lead/follow counts, asks before loading.
// ============================================================================
function RestoreModal({ summary, onRestore, onDismiss }) {
  // Esc dismisses (treats as "start fresh") — friendly default.
  useEffect(() => {
    const onKey = (e) => {
      if (e.key === "Escape") onDismiss();
      if (e.key === "Enter") onRestore();
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onRestore, onDismiss]);

  const { project, counts, screen, mode } = summary;
  return (
    <div
      role="dialog"
      aria-modal="true"
      aria-label="Restore saved session?"
      style={{
        position: "fixed", inset: 0, zIndex: 100,
        background: "rgba(34,49,83,0.42)",
        backdropFilter: "blur(4px)",
        WebkitBackdropFilter: "blur(4px)",
        display: "grid", placeItems: "center",
        padding: 24,
        animation: "fadeIn 180ms ease-out",
      }}
    >
      <div style={{
        width: "min(520px, 100%)",
        background: "var(--surface-paper)",
        border: "1px solid var(--border-strong)",
        borderRadius: 20,
        boxShadow: "0 40px 80px rgba(34,49,83,0.28), 0 8px 20px rgba(34,49,83,0.08)",
        padding: 28,
        animation: "popIn 200ms cubic-bezier(0.16, 1, 0.3, 1)",
      }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 16 }}>
          <span style={{
            width: 28, height: 28, borderRadius: 8,
            background: "var(--c-violet)", color: "#fff",
            display: "grid", placeItems: "center",
          }}>
            <ShareIcon />
          </span>
          <span className="eyebrow violet" style={{ margin: 0 }}>Saved session</span>
        </div>

        <h2 style={{
          fontFamily: "var(--font-jakarta)",
          fontWeight: 800,
          fontSize: 26,
          letterSpacing: "-0.02em",
          margin: "0 0 6px",
          color: "var(--fg-1)",
          textWrap: "balance",
        }}>
          Pick up where {project ? "you" : "this"} left off?
        </h2>
        <p style={{
          fontFamily: "var(--font-serif)", fontSize: 15,
          color: "var(--fg-3)", lineHeight: 1.5, margin: "0 0 18px",
        }}>
          This link carries a complete session for{" "}
          <strong style={{ color: "var(--fg-1)", fontFamily: "var(--font-jakarta)", fontWeight: 700 }}>
            {project || "an untitled project"}
          </strong>
          . Restore it, or start fresh.
        </p>

        <div style={{
          display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10,
          marginBottom: 22,
        }}>
          <RestoreStat label="Lead" value={counts.lead} color="var(--c-violet)" />
          <RestoreStat label="Follow" value={counts.follow} color="var(--c-emerald)" />
          <RestoreStat label="Out-of-scope" value={counts.out} color="rgba(34,49,83,0.30)" />
        </div>

        {(mode === "workshop" || (screen && screen !== "landing")) && (
          <div style={{
            display: "flex", gap: 12, flexWrap: "wrap",
            marginBottom: 18, paddingBottom: 18,
            borderBottom: "1px dashed var(--border-default)",
          }}>
            {mode === "workshop" && (
              <span style={{ fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--c-violet-deep)", letterSpacing: "0.16em" }}>
                · WORKSHOP MODE
              </span>
            )}
            {screen && screen !== "landing" && (
              <span style={{ fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--fg-4)", letterSpacing: "0.16em" }}>
                · LAST ON: {String(screen).toUpperCase()}
              </span>
            )}
          </div>
        )}

        <div style={{ display: "flex", gap: 10, justifyContent: "flex-end" }}>
          <button className="btn btn-ghost" onClick={onDismiss}>Start fresh</button>
          <button className="btn btn-primary" onClick={onRestore} autoFocus>
            Restore session
            <span aria-hidden style={{ fontFamily: "var(--font-mono)", marginLeft: 4 }}>↵</span>
          </button>
        </div>
      </div>
    </div>
  );
}

function RestoreStat({ label, value, color }) {
  return (
    <div style={{
      padding: "12px 14px",
      borderRadius: 12,
      background: "rgba(255,255,255,0.6)",
      border: "1px solid var(--border-default)",
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 4 }}>
        <span style={{ width: 6, height: 6, borderRadius: 999, background: color }} />
        <span style={{
          fontFamily: "var(--font-mono)", fontSize: 9.5,
          letterSpacing: "0.16em", textTransform: "uppercase",
          color: "var(--fg-4)",
        }}>{label}</span>
      </div>
      <div style={{
        fontFamily: "var(--font-jakarta)", fontWeight: 700, fontSize: 24,
        letterSpacing: "-0.02em", color: "var(--fg-1)", lineHeight: 1,
      }}>{value}</div>
    </div>
  );
}
