/* === App shell — sidebar nav + content router + tweaks === */
const { useState, useEffect } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "rollupDensity": "cards",
  "timelineFuzziness": "medium",
  "showODA": false
}/*EDITMODE-END*/;

/* Computed per render so counts stay live after edits (state loads async). */
function buildNav() {
  return [
    { id: "overview",      label: "Overview",       count: null,  serif: true },
    { id: "milestones",    label: "Milestones",     count: window.MILESTONES.length },
    { id: "jurisdictions", label: "Jurisdictions",  count: Object.keys(window.JURISDICTIONS).length },
    { id: "powermap",      label: "Power map",      count: Object.keys(window.POWER_MAP_STATES).length },
    { id: "work",          label: "Work",           count: window.WORK_ITEMS.length },
    { id: "contracts",     label: "Contracts",      count: window.CONTRACTS.length },
    { id: "changes",       label: "What's changed", count: window.CHANGES.risks.length + window.CHANGES.wins.length + window.CHANGES.blocked.length },
    { id: "reports",       label: "Reports",        count: null },
    ...(window.Store.session() && window.Store.session().role === "admin"
      ? [{ id: "admin", label: "Admin", count: null }] : []),
  ];
}

function App() {
  const NAV = buildNav();
  const me = window.Store.session();
  // Deep-linkable: ?view=work opens a tab directly (shareable bookmarks).
  const [view, _setView] = useState(() => {
    const v = new URLSearchParams(location.search).get("view");
    return ["overview", "milestones", "jurisdictions", "powermap", "work", "contracts", "changes", "reports", "admin"].includes(v) ? v : "overview";
  });
  // ?state=CO deep-links a jurisdiction's panel open.
  const [selectedState, setSelectedState] = useState(() => new URLSearchParams(location.search).get("state"));
  const setView = (v) => { setSelectedState(null); _setView(v); };
  const [showQuorum, setShowQuorum] = useState(() => new URLSearchParams(location.search).get("upload") === "quorum");
  const [t, setTweak] = window.useTweaks ? window.useTweaks(TWEAK_DEFAULTS) : [TWEAK_DEFAULTS, () => {}];

  // ESC closes side panel
  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") setSelectedState(null); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  return (
    <div className="app">
      {/* Sidebar */}
      <aside className="sidebar">
        <div className="sidebar__brand">
          <div className="sidebar__brand-eyebrow">Project tracker</div>
          <div className="sidebar__brand-name">Retirement&nbsp;Savings</div>
          <div className="sidebar__brand-sub">{window.FY}</div>
        </div>

        <div className="sidebar__section">Sections</div>
        <nav className="nav">
          {NAV.map(n => (
            <button
              key={n.id}
              className={`nav__item ${view === n.id ? "nav__item--active" : ""}`}
              onClick={() => setView(n.id)}
            >
              <span style={n.serif ? { fontFamily: "var(--serif)", fontSize: 14 } : null}>{n.label}</span>
              {n.count != null ? <span className="nav__count">{n.count}</span> : null}
            </button>
          ))}
        </nav>

        <div className="sidebar__spacer"></div>

        <div className="sidebar__section" style={{ paddingBottom: 6 }}>Cycle</div>
        <div style={{ padding: "0 12px 12px" }}>
          <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
            {[
              { fy: window.FY || "—", on: true, state: "active" },
            ].map(r => (
              <div key={r.fy} style={{
                display: "flex", justifyContent: "space-between", alignItems: "center",
                padding: "5px 10px", borderRadius: 4,
                background: r.on ? "var(--accent-bg)" : "transparent",
                fontFamily: "var(--mono)", fontSize: 11,
                color: r.on ? "var(--accent)" : "var(--ink-mute)"
              }}>
                <span>{r.fy}</span><span>{r.state}</span>
              </div>
            ))}
          </div>
        </div>

        <div className="sidebar__foot">
          <div className="sidebar__foot-row" style={{ paddingBottom: 8, marginBottom: 8, borderBottom: "1px solid var(--rule)" }}>
            <span style={{ color: "var(--ink-2)" }}>
              <span className="dot-live"></span>{me ? me.name : "—"}
              <span style={{ color: "var(--ink-faint)", marginLeft: 5, textTransform: "uppercase", fontSize: 9, letterSpacing: ".1em" }}>{me ? me.role : ""}</span>
            </span>
            <button onClick={() => window.Store.logout()}
                    style={{ color: "var(--ink-mute)", fontFamily: "var(--mono)", fontSize: 10, letterSpacing: ".08em", textTransform: "uppercase" }}>
              sign out
            </button>
          </div>
          <div className="sidebar__foot-row">
            {me && me.role === "admin" ? (
              <button onClick={() => setShowQuorum(true)}
                      style={{ color: "var(--ink-2)", fontFamily: "var(--mono)", fontSize: 11, textDecoration: "underline", textDecorationColor: "var(--border-strong)", textUnderlineOffset: 3 }}>
                Quorum upload
              </button>
            ) : <span>Quorum upload</span>}
            <span>{window.timeAgoShort ? window.timeAgoShort(window.META && window.META.lastQuorumUpload) : "—"}</span>
          </div>
          <div className="sidebar__foot-row">
            <span>CAPS sync</span>
            <span>{window.timeAgoShort ? window.timeAgoShort(window.META && window.META.lastCapsSync) : "—"}</span>
          </div>
        </div>
      </aside>

      {/* Main */}
      <main className="main">
        <Topbar view={view} t={t} setTweak={setTweak} />

        {view === "overview" ? (
          <Overview tweaks={t} selectedState={selectedState} onSelectState={setSelectedState} />
        ) : view === "milestones" ? (
          <MilestonesTab tweaks={t} />
        ) : view === "jurisdictions" ? (
          <JurisdictionsTab selectedState={selectedState} onSelectState={setSelectedState} />
        ) : view === "powermap" ? (
          <div className="content"><PowerMap /></div>
        ) : view === "work" ? (
          <div className="content"><WorkItems /></div>
        ) : view === "contracts" ? (
          <div className="content"><ContractsView /></div>
        ) : view === "changes" ? (
          <div className="content"><ExceptionsSurface /></div>
        ) : view === "reports" ? (
          <div className="content"><GRReport /></div>
        ) : view === "admin" ? (
          <div className="content"><AdminPanel /></div>
        ) : null}
      </main>

      {/* Jurisdiction side panel */}
      {selectedState ? <JurisdictionPanel abbr={selectedState} onClose={() => setSelectedState(null)} /> : null}

      {/* Quorum re-upload */}
      {showQuorum ? <QuorumUpload onClose={() => setShowQuorum(false)} /> : null}

      {/* Tweaks panel */}
      {window.TweaksPanel ? (
        <window.TweaksPanel title="Tweaks">
          <window.TweakSection title="Layout">
            <window.TweakRadio
              label="Rollup density"
              value={t.rollupDensity}
              options={[
                { value: "cards", label: "Cards" },
                { value: "table", label: "Table" },
              ]}
              onChange={(v) => setTweak("rollupDensity", v)}
            />
            <window.TweakToggle
              label="Show ODA backbone"
              hint="The multi-year structural backbone behind milestones."
              value={t.showODA}
              onChange={(v) => setTweak("showODA", v)}
            />
          </window.TweakSection>
          <window.TweakSection title="Timeline">
            <window.TweakRadio
              label="Date fuzziness"
              value={t.timelineFuzziness}
              options={[
                { value: "subtle", label: "Subtle" },
                { value: "medium", label: "Medium" },
                { value: "bold",   label: "Bold" },
              ]}
              onChange={(v) => setTweak("timelineFuzziness", v)}
            />
          </window.TweakSection>
        </window.TweaksPanel>
      ) : null}
    </div>
  );
}

function Topbar({ view, t, setTweak }) {
  const labels = {
    overview:      { title: "Overview",       sub: "Where things stand" },
    milestones:    { title: "Milestones",     sub: "Click a milestone to drill into linked work, jurisdictions, and rationale" },
    jurisdictions: { title: "Jurisdictions",  sub: "States, DC, cities, and federal — click for detail" },
    powermap:      { title: "Power map",      sub: "Sponsors, committees, supporters, opposers — for the states where work is moving" },
    work:          { title: "Work",           sub: "Project work — typed items + stages" },
    contracts:     { title: "Contracts",      sub: "CAPS deadlines · timing and percent remaining only" },
    changes:       { title: "What's changed", sub: "Wins · risks · blocked" },
    reports:       { title: "Reports",        sub: "Generated outputs — GR check-in · Comms (coming)" },
    admin:         { title: "Admin",          sub: "Cycle · bulk import · data reset" },
  }[view];

  return (
    <header className="topbar">
      <div>
        <div className="topbar__sub">{labels.sub}</div>
        <div className="topbar__title">{labels.title}</div>
      </div>
      <div className="topbar__right">
        <div className="fy-pill">
          {window.FY || "—"} · current
          <span className="fy-pill__chev">▾</span>
        </div>
        <button className="btn">
          <span style={{ fontFamily: "var(--mono)", fontSize: 10, letterSpacing: ".1em", color: "var(--ink-mute)" }}>QUICK FIND</span>
          <span className="kbd">⌘K</span>
        </button>
        <button className="btn btn--primary">Export rollup ↗</button>
      </div>
    </header>
  );
}

/* ============ Milestones tab ============ */
function MilestonesTab({ tweaks }) {
  const M = window.MILESTONES;
  // ?milestone=M-02 deep-links straight to a milestone's panel.
  const [selectedM, setSelectedM] = useState(() => new URLSearchParams(location.search).get("milestone"));
  const [showNewM, setShowNewM] = useState(false);
  const isAdmin = window.Store.session() && window.Store.session().role === "admin";
  const numericMilestones = M.filter((m) => m.kind === "numeric");
  const qualitativeMilestones = M.filter((m) => m.kind === "qualitative");
  const onTrack = M.filter((m) => m.rag === "green").length;
  const atRisk  = M.filter((m) => m.rag === "yellow" || m.rag === "red").length;

  // By-objective health: count yellow+red per ODA objective
  const objHealth = window.ODA.map((o) => {
    const deliverableIds = o.deliverables.map((d) => d.id);
    const linked = M.filter((m) => m.oda.some((id) => id === o.id || deliverableIds.includes(id)));
    return {
      id: o.id,
      short: o.short,
      total: linked.length,
      red: linked.filter((m) => m.rag === "red").length,
      yellow: linked.filter((m) => m.rag === "yellow").length,
      green: linked.filter((m) => m.rag === "green").length,
    };
  });

  return (
    <div className="content">
      {isAdmin ? (
        <div className="row row--end" style={{ marginBottom: 10 }}>
          <button className="btn btn--primary" onClick={() => setShowNewM(true)}>+ New milestone</button>
        </div>
      ) : null}
      {showNewM ? <NewMilestoneForm onClose={() => setShowNewM(false)} /> : null}

      {/* Tab summary */}
      <div className="tab-summary">
        <SumStat label="On track" value={onTrack} accent={onTrack === M.length ? "green" : null} />
        <SumStat label="At risk" value={atRisk} accent={atRisk > 0 ? "yellow" : null} />
        <SumStat label="Numeric" value={numericMilestones.length} muted />
        <SumStat label="Qualitative" value={qualitativeMilestones.length} muted />
      </div>

      {/* By objective — horizontal strip */}
      <div className="objective-grid">
        {objHealth.map((o) => (
          <div key={o.id} className="objective-cell">
            <div className="objective-cell__head">
              <span className="mono" style={{ fontSize: 11, color: "var(--navy)", fontWeight: 600 }}>{o.id}</span>
              <span className="mono faint" style={{ fontSize: 10 }}>{o.total} linked</span>
            </div>
            <div style={{ fontSize: 12, fontWeight: 600, marginTop: 2 }}>{o.short}</div>
            <div style={{ display: "flex", gap: 3, marginTop: 8, flexWrap: "wrap" }}>
              {Array.from({ length: o.green }).map((_, i) => <span key={"g"+i} style={{ width: 12, height: 4, borderRadius: 2, background: "var(--green)" }}></span>)}
              {Array.from({ length: o.yellow }).map((_, i) => <span key={"y"+i} style={{ width: 12, height: 4, borderRadius: 2, background: "var(--yellow)" }}></span>)}
              {Array.from({ length: o.red }).map((_, i) => <span key={"r"+i} style={{ width: 12, height: 4, borderRadius: 2, background: "var(--red)" }}></span>)}
              {o.total === 0 ? <span className="mono faint" style={{ fontSize: 10 }}>—</span> : null}
            </div>
          </div>
        ))}
      </div>

      {/* Milestone rollup full-width */}
      <div className="card" style={{ marginBottom: 28 }}>
        <div className="card__head">
          <div>
            <h3>All milestones · {window.FY}</h3>
            <div className="section-sub">{M.length} milestones · ⊕ numeric / ◇ qualitative · click for detail</div>
          </div>
          <div className="row">
            <Chip kind="green" mono={false}>{onTrack}</Chip>
            <Chip kind="yellow" mono={false}>{M.filter(m => m.rag === "yellow").length}</Chip>
            <Chip kind="red" mono={false}>{M.filter(m => m.rag === "red").length}</Chip>
          </div>
        </div>
        <MilestoneRollup density={tweaks.rollupDensity} showODA={true} onSelect={setSelectedM} />
      </div>

      {selectedM ? <MilestonePanel id={selectedM} onClose={() => setSelectedM(null)} /> : null}

      <div>
        <SectionHead title="Objectives, Deliverables, and Activities (ODAs)" sub="The multi-year ODAs that inform milestones." />
        <ODABackbone />
      </div>
    </div>
  );
}

/* ============ Jurisdictions tab ============ */
function JurisdictionsTab({ selectedState, onSelectState }) {
  const totalLive = window.STATES_LIVE.length;
  const totalEnacted = window.STATES_ENACTED.length;
  const totalActive = window.STATES_ACTIVE_LEG.length;
  const totalRedActive = Object.values(window.JURISDICTIONS).filter(j => j.trifecta === "R" && (j.legActivity === "active" || j.legActivity === "enacted-session")).length;
  const totalDActive   = Object.values(window.JURISDICTIONS).filter(j => j.trifecta === "D" && (j.legActivity === "active" || j.legActivity === "enacted-session")).length;
  const totalBills = window.BILLS.length;

  return (
    <div className="content">
      <div className="tab-summary">
        <SumStat label="Live programs" value={`${totalLive}${window.HAS_DC_LIVE ? " + DC" : ""}`} />
        <SumStat label="Implementing" value={`${totalEnacted}${window.HAS_DC_ENACTED ? " + DC" : ""}`} />
        <SumStat label="Legislation moving" value={totalActive} />
        <SumStat label="R-trifecta moving" value={totalRedActive} muted />
        <SumStat label="D-trifecta moving" value={totalDActive} muted />
        <SumStat label="Bills tracked" value={totalBills} muted />
      </div>

      <div className="card map-card" style={{ marginBottom: 22 }}>
        <div className="map-toolbar">
          <div className="map-toolbar__title-row">
            <MapLegend />
            <TrifectaLegend />
          </div>
        </div>
        <div className="us-map">
          <USTileMap onSelectState={onSelectState} selectedState={selectedState} />
        </div>
      </div>

      <JurisdictionTable onSelect={onSelectState} />
    </div>
  );
}

function JurisdictionTable({ onSelect }) {
  const rows = Object.entries(window.JURISDICTIONS).map(([abbr, j]) => ({ abbr, ...j })).sort((a, b) => {
    const order = { live: 0, enacted: 1, none: 2 };
    return order[a.program] - order[b.program] || a.abbr.localeCompare(b.abbr);
  });
  const programLabel = { live: "Live", enacted: "Implementing", none: "—" };
  const legLabel = { "enacted-session": "Enacted (session)", "active": "Active", "recent": "Recent", "none": "—" };
  return (
    <div className="card">
      <div className="card__head">
        <h3>All jurisdictions</h3>
        <div className="row">
          <Chip kind="navy">{window.STATES_LIVE.length} live</Chip>
          <Chip kind="soft">{window.STATES_ENACTED.length} implementing</Chip>
          <Chip kind="accent">{window.STATES_ACTIVE_LEG.length} active leg.</Chip>
        </div>
      </div>
      <div>
        <div className="list-row" style={{ gridTemplateColumns: "48px 1.5fr 1fr 1fr 1.1fr 70px 80px", padding: "8px 16px", fontFamily: "var(--mono)", fontSize: 10, color: "var(--ink-mute)", textTransform: "uppercase", letterSpacing: ".12em", borderBottom: "1px solid var(--rule)" }}>
          <div>Code</div><div>Program / label</div><div>Maturity</div><div>Leg activity</div><div>Engagement</div><div>Bills</div><div style={{ textAlign: "right" }}>Trifecta</div>
        </div>
        {rows.map(r => {
          const bills = window.BILLS.filter(b => b.state === r.abbr).length;
          return (
            <div key={r.abbr}
                 className="list-row"
                 style={{ gridTemplateColumns: "48px 1.5fr 1fr 1fr 1.1fr 70px 80px", cursor: "pointer" }}
                 onClick={() => onSelect(r.abbr)}>
              <div className="mono" style={{ fontWeight: 600 }}>{r.abbr}</div>
              <div>
                <div style={{ fontWeight: 600, fontSize: 13 }}>{window.STATE_NAMES[r.abbr]}</div>
                <div className="muted" style={{ fontSize: 11 }}>{r.label || "—"}</div>
              </div>
              <div>
                <Chip kind={r.program === "live" ? "navy" : r.program === "enacted" ? "soft" : "ghost"}>{programLabel[r.program]}</Chip>
              </div>
              <div>
                <Chip kind={r.legActivity === "enacted-session" ? "accent" : r.legActivity === "active" ? "accent" : r.legActivity === "recent" ? "ghost" : "ghost"}>{legLabel[r.legActivity]}</Chip>
              </div>
              <div><EngagementChips list={r.engagement} small /></div>
              <div className="mono" style={{ fontSize: 12, color: "var(--ink-2)" }}>{bills}</div>
              <div className="row row--end">
                {r.trifecta ? <Chip kind={r.trifecta === "R" ? "red" : "navy"}>{r.trifecta}</Chip> : <span className="mono faint" style={{ fontSize: 11 }}>—</span>}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

Object.assign(window, { App });
/* Rendering is owned by boot.jsx (login gate + state load). */
