// lattice.jsx — App shell. Composes data + map + panels + views + incident.
// Wires every topbar button to a real popover, every rail item to a real view,
// search to live filtering, layout to localStorage persistence.

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

// ─── Persistence helpers ────────────────────────────────────────────
const LS = (k, fallback) => {
  try { const v = localStorage.getItem("lattice:" + k); return v == null ? fallback : JSON.parse(v); } catch { return fallback; }
};
const setLS = (k, v) => { try { localStorage.setItem("lattice:" + k, JSON.stringify(v)); } catch {} };

const DEFAULTS = /*EDITMODE-BEGIN*/{
  "theme": "cream",
  "dark": false,
  "showMesh": true,
  "eventSpeed": 1.8,
  "region": "Global"
}/*EDITMODE-END*/;

// ─── Generic popover (click-outside to close) ───────────────────────
function Popover({ open, onClose, children, className, anchorSel }) {
  const ref = useRef(null);
  useEffect(() => {
    if (!open) return;
    const fn = (e) => {
      if (ref.current && !ref.current.contains(e.target) && !(anchorSel && e.target.closest(anchorSel))) onClose();
    };
    document.addEventListener("mousedown", fn);
    const esc = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", esc);
    return () => { document.removeEventListener("mousedown", fn); document.removeEventListener("keydown", esc); };
  }, [open, onClose, anchorSel]);
  if (!open) return null;
  return <div ref={ref} className={"popover " + (className || "")}>{children}</div>;
}

// ─── Topbar with all working buttons ────────────────────────────────
function Topbar({
  region, setRegion,
  isDark, setIsDark,
  theme, setTheme,
  events, nodes,
  onOpenIncident, onSelectNode, onSwitchView,
  unread, setUnread, onToggleEvents,
}) {
  const [openMenu, setOpenMenu] = useState(null);  // "search" | "region" | "bell" | "settings" | "avatar"
  const [query, setQuery] = useState("");

  const close = () => setOpenMenu(null);

  // ── Search results ──
  const searchResults = useMemo(() => {
    if (!query) return null;
    const q = query.toLowerCase();
    const matchNodes = nodes.filter(n => (n.id + " " + n.city + " " + n.country).toLowerCase().includes(q)).slice(0, 5);
    const matchEvents = events.filter(e => (e.title + " " + e.msg + " " + e.node).toLowerCase().includes(q)).slice(0, 5);
    return { nodes: matchNodes, events: matchEvents };
  }, [query, events, nodes]);

  return (
    <header className="lat__top">
      <div className="lat__brand">
        <ChromaticMark size={20}/>
        <span className="lat__brand-name">Lattice</span>
        <span className="lat__brand-tag">CYBER-PHYSICAL · FUSION</span>
      </div>

      {/* Region selector */}
      <div className="pop-anchor" data-anchor="region">
        <button className="lat__crumb" onClick={() => setOpenMenu(openMenu === "region" ? null : "region")} aria-label="region">
          <span className="muted">REGION</span>
          <span>{region}</span>
          <Icon name="chevron-down" size={12}/>
        </button>
        <Popover open={openMenu === "region"} onClose={close} anchorSel='[data-anchor="region"]' className="popover--left popover--wide">
          <div className="popover__sec">Region focus</div>
          {Object.keys(REGIONS).map(r => (
            <div key={r}
              className={"popover__item" + (r === region ? " popover__item--active" : "")}
              onClick={() => { setRegion(r); close(); }}>
              <Icon name="layers" size={12} style={{ color: "var(--muted-foreground)" }}/>
              <span>{r}</span>
              {r === region && <Icon name="check" size={12} style={{ marginLeft: "auto", color: "var(--success)" }}/>}
            </div>
          ))}
        </Popover>
      </div>

      {/* Search */}
      <div className="pop-anchor" style={{ flex: 1, maxWidth: 480 }} data-anchor="search">
        <div className="lat__search">
          <Icon name="search" size={12}/>
          <input
            placeholder="search nodes, geofences, modules, events…"
            value={query}
            onChange={(e) => { setQuery(e.target.value); setOpenMenu(e.target.value ? "search" : null); }}
            onFocus={() => { if (query) setOpenMenu("search"); }}
          />
          <kbd>⌘K</kbd>
        </div>
        <Popover open={openMenu === "search" && !!query} onClose={() => setOpenMenu(null)} anchorSel='[data-anchor="search"]' className="search-pop popover--xwide popover--left">
          {searchResults && searchResults.nodes.length === 0 && searchResults.events.length === 0 ? (
            <div className="muted" style={{ padding: "12px 14px", fontSize: 12 }}>No matches.</div>
          ) : (
            <>
              {searchResults && searchResults.nodes.length > 0 && (
                <>
                  <div className="popover__sec">Nodes</div>
                  {searchResults.nodes.map(n => (
                    <div key={n.id} className="popover__item" onClick={() => { onSelectNode(n); setQuery(""); close(); }}>
                      <span className="dot" style={{ color: statusColor(n.status) }}/>
                      <span><span className="mono" style={{ fontSize: 11 }}>{n.id}</span> · {n.city}</span>
                      <span className="muted" style={{ marginLeft: "auto", fontSize: 11 }}>{n.region}</span>
                    </div>
                  ))}
                </>
              )}
              {searchResults && searchResults.events.length > 0 && (
                <>
                  <div className="popover__sec">Events</div>
                  {searchResults.events.map(e => (
                    <div key={e.id} className="popover__item" onClick={() => { onOpenIncident(e); setQuery(""); close(); }}>
                      <span className={"pill " + statusPill(e.sev)} style={{ fontSize: 8 }}>{e.sev}</span>
                      <span style={{ flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{e.title}</span>
                      <span className="muted mono" style={{ fontSize: 10 }}>{e.time}</span>
                    </div>
                  ))}
                </>
              )}
            </>
          )}
        </Popover>
      </div>

      <div className="lat__topright">
        <button className="icon-btn ev-toggle" onClick={onToggleEvents} aria-label="event stream"><Icon name="inbox" size={15}/></button>
        {/* Notifications */}
        <div className="pop-anchor" data-anchor="bell">
          <button className="icon-btn" onClick={() => { setOpenMenu(openMenu === "bell" ? null : "bell"); setUnread(0); }} aria-label="alerts">
            <Icon name="bell" size={14}/>
            {unread > 0 && <span className="badge"/>}
          </button>
          <Popover open={openMenu === "bell"} onClose={close} anchorSel='[data-anchor="bell"]' className="popover--wide">
            <div className="popover__head">
              <div style={{ font: "500 13px/1.2 var(--font-sans)" }}>Notifications</div>
              <span className="muted mono" style={{ fontSize: 10 }}>{events.length} in window</span>
            </div>
            {events.filter(e => e.sev !== "info").slice(0, 6).map(e => (
              <div key={e.id} className="popover__item" onClick={() => { onOpenIncident(e); close(); }}>
                <span className={"pill " + statusPill(e.sev)} style={{ fontSize: 8 }}>{e.sev}</span>
                <div style={{ flex: 1, overflow: "hidden" }}>
                  <div style={{ font: "500 12px/1.3 var(--font-sans)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{e.title}</div>
                  <div className="muted mono" style={{ fontSize: 10 }}>{e.node} · {e.time}</div>
                </div>
              </div>
            ))}
            <div className="popover__divider"/>
            <div className="popover__item" onClick={() => { onSwitchView("sec"); close(); }}>
              <Icon name="alert-circle" size={12}/>
              <span>Open security center</span>
              <Icon name="arrow-up-right" size={11} style={{ marginLeft: "auto", color: "var(--muted-foreground)" }}/>
            </div>
          </Popover>
        </div>

        {/* Settings */}
        <div className="pop-anchor" data-anchor="settings">
          <button className="icon-btn" onClick={() => setOpenMenu(openMenu === "settings" ? null : "settings")} aria-label="settings">
            <Icon name="settings" size={14}/>
          </button>
          <Popover open={openMenu === "settings"} onClose={close} anchorSel='[data-anchor="settings"]' className="popover--wide">
            <div className="popover__head">
              <div style={{ font: "500 13px/1.2 var(--font-sans)" }}>Workspace settings</div>
            </div>
            <div className="settings-row">
              <label>Dark mode</label>
              <span className={"toggle" + (isDark ? " toggle--on" : "")} onClick={() => setIsDark(!isDark)}>
                <span className="toggle__knob"/>
              </span>
            </div>
            <div className="settings-row">
              <label>Theme</label>
              <select value={theme} onChange={(e) => setTheme(e.target.value)}>
                <option value="cream">cream · oxblood</option>
                <option value="trust">trust · blue-gray</option>
                <option value="urgency">urgency · red-gray</option>
                <option value="spirit">spirit · violet-gray</option>
                <option value="harmony">harmony · teal-gray</option>
                <option value="neutral">neutral · achromatic</option>
              </select>
            </div>
            <div className="settings-row">
              <label>Units</label>
              <select defaultValue="metric"><option>metric</option><option>imperial</option></select>
            </div>
            <div className="settings-row">
              <label>Timestamps</label>
              <select defaultValue="UTC"><option>UTC</option><option>local</option><option>relative</option></select>
            </div>
            <div className="popover__divider"/>
            <div className="popover__item"><Icon name="folder" size={12}/> Audit log</div>
            <div className="popover__item"><Icon name="users" size={12}/> Team & roles</div>
            <div className="popover__item"><Icon name="arrow-up-right" size={12}/> Docs ↗</div>
          </Popover>
        </div>

        {/* Avatar */}
        <div className="pop-anchor" data-anchor="avatar">
          <span className="avatar" style={{ cursor: "pointer" }} title="Operator menu"
                onClick={() => setOpenMenu(openMenu === "avatar" ? null : "avatar")}>OP</span>
          <Popover open={openMenu === "avatar"} onClose={close} anchorSel='[data-anchor="avatar"]'>
            <div className="profile-row">
              <span className="profile-row__avatar">OP</span>
              <div>
                <div className="profile-row__name">Operator · L3</div>
                <div className="profile-row__email">ops-3@lattice.cl</div>
              </div>
            </div>
            <div className="popover__divider"/>
            <div className="popover__item"><Icon name="user" size={12}/> Profile</div>
            <div className="popover__item"><Icon name="circle" size={12}/> Set status</div>
            <div className="popover__item"><Icon name="layers" size={12}/> Switch workspace</div>
            <div className="popover__divider"/>
            <div className="popover__item" style={{ color: "var(--destructive)" }}><Icon name="arrow-up-right" size={12}/> Sign out</div>
          </Popover>
        </div>
      </div>
    </header>
  );
}

// ─── Rail ──────────────────────────────────────────────────────────
function Rail({ active, setActive }) {
  const items = [
    ["map",   "layers",        "Map"],
    ["meridian", "git-branch",  "Meridian"],
    ["edge",  "activity",      "Edge"],
    ["ai",    "sparkles",      "AI"],
    ["telem", "trending-up",   "Telem"],
    ["sec",   "alert-circle",  "Sec"],
    ["logs",  "file",          "Logs"],
  ];
  return (
    <nav className="lat__rail">
      {items.map(([id, ic, label]) => (
        <button key={id}
          className={"rail-btn" + (active === id ? " rail-btn--active" : "")}
          onClick={() => setActive(id)}>
          <Icon name={ic} size={16}/>
          <span>{label}</span>
        </button>
      ))}
      <div className="rail-sep"/>
      <button className="rail-btn" aria-label="more"><Icon name="more-horizontal" size={16}/><span>More</span></button>
    </nav>
  );
}

// ─── Event stream (right rail) ─────────────────────────────────────
function EventStream({ events, filter, setFilter, onOpenIncident }) {
  const filtered = filter === "all" ? events : events.filter(e => e.sev === filter);
  return (
    <div className="lat__events">
      <div className="events__head">
        <div className="col" style={{ gap: 2 }}>
          <div style={{ font: "500 12px/1 var(--font-sans)" }}>Event stream</div>
          <div className="muted mono" style={{ fontSize: 10 }}>UTC · {events.length} in window · click warn/crit</div>
        </div>
        <span className="pill pill--info"><span className="dot dot--pulse" style={{ color: "var(--info)" }}/> LIVE</span>
      </div>
      <div className="events__list">
        {filtered.map(e => (
          <div
            className={"event" + (e.new ? " event--new" : "") + ((e.sev === "crit" || e.sev === "warn") ? " event--clickable" : "")}
            key={e.id}
            onClick={() => { if (e.sev === "crit" || e.sev === "warn") onOpenIncident(e); }}>
            <div className="event__time">
              {e.time}
              <div style={{ marginTop: 3 }}>
                <span className={"pill " + statusPill(e.sev)} style={{ fontSize: 8 }}>{e.sev}</span>
              </div>
            </div>
            <div className="event__body">
              <div className="event__title">
                <span style={{ fontWeight: 500 }}>{e.title}</span>
                <code>{e.type}</code>
              </div>
              <div className="event__msg">{e.msg}</div>
            </div>
          </div>
        ))}
      </div>
      <div className="events__footer">
        <div className="events__filters">
          {["all","info","warn","crit"].map(f => (
            <button key={f} className={"filter-chip" + (filter === f ? " filter-chip--on" : "")} onClick={() => setFilter(f)}>{f}</button>
          ))}
        </div>
        <button className="icon-btn" aria-label="export"><Icon name="arrow-up-right" size={12}/></button>
      </div>
    </div>
  );
}

// ─── Stat bar ─────────────────────────────────────────────────────
function StatBar({ stats }) {
  return (
    <div className="statbar">
      {stats.map((s, i) => (
        <div className="stat" key={i}>
          <div className="stat__label">{s.label}</div>
          <div className="stat__value">{s.value}<span className="stat__unit">{s.unit}</span></div>
          <div className="stat__row">
            <span className="dot" style={{ color: s.tone || "var(--success)" }}/>
            <span>{s.sub}</span>
          </div>
        </div>
      ))}
    </div>
  );
}

// ─── Draggable bottom-panel wrapper ────────────────────────────────
function DraggablePanel({ id, order, setOrder, children }) {
  const [dragging, setDragging] = useState(false);
  const [over, setOver] = useState(false);
  return React.cloneElement(children, {
    draggable: true,
    className: (children.props.className || "") + (dragging ? " dragging" : "") + (over ? " drag-over" : ""),
    onDragStart: (e) => { e.dataTransfer.setData("text/plain", id); e.dataTransfer.effectAllowed = "move"; setDragging(true); },
    onDragEnd:   () => { setDragging(false); setOver(false); },
    onDragOver:  (e) => { e.preventDefault(); e.dataTransfer.dropEffect = "move"; setOver(true); },
    onDragLeave: () => setOver(false),
    onDrop: (e) => {
      e.preventDefault();
      const src = e.dataTransfer.getData("text/plain");
      setOver(false);
      if (!src || src === id) return;
      const next = [...order];
      const si = next.indexOf(src), di = next.indexOf(id);
      next.splice(si, 1); next.splice(di, 0, src);
      setOrder(next);
    },
  });
}

// ─── App ──────────────────────────────────────────────────────────
function App() {
  const [tweaks, setTweak] = useTweaks(DEFAULTS);
  const [active, setActive]   = useState(() => LS("view", "map"));
  const [selected, setSelected] = useState(null);
  const [events, setEvents]   = useState(() => seedEvents());
  const [filter, setFilter]   = useState("all");
  const [tick, setTick]       = useState(0);
  const [incident, setIncident] = useState(null);
  const [flights, setFlights] = useState([]);
  const [dispatched, setDispatched] = useState([]);
  const [panelOrder, setPanelOrder] = useState(() => LS("panels", ["modules", "inference", "fusion", "telemetry"]));
  const [unread, setUnread]   = useState(0);
  const [eventsOpen, setEventsOpen] = useState(false);
  const [bottomH, setBottomH] = useState(() => LS("bottomH", 280));

  const mesh = useMemo(() => computeMesh(NODES), []);

  // Theme + dark on <html>
  useEffect(() => {
    document.documentElement.setAttribute("data-theme", tweaks.theme);
    document.documentElement.classList.toggle("dark", tweaks.dark);
  }, [tweaks.theme, tweaks.dark]);

  // Persistence
  useEffect(() => setLS("view", active), [active]);
  useEffect(() => setLS("panels", panelOrder), [panelOrder]);
  useEffect(() => setLS("bottomH", bottomH), [bottomH]);

  // Event stream
  useEffect(() => {
    const ms = Math.max(400, Math.round(2200 / Math.max(0.2, tweaks.eventSpeed)));
    const t = setInterval(() => {
      setEvents(prev => {
        const next = [{ ...buildEvent(new Date()) }, ...prev.slice(0, 39)];
        // Bump unread if new event is warn/crit and bell not focused
        if (next[0].sev !== "info") setUnread(u => u + 1);
        return next.map((e, i) => i === 0 ? e : { ...e, new: false });
      });
    }, ms);
    return () => clearInterval(t);
  }, [tweaks.eventSpeed]);

  // Telemetry tick
  useEffect(() => {
    const t = setInterval(() => setTick(v => v + 1), 1500);
    return () => clearInterval(t);
  }, []);

  // ⌘K focuses search
  useEffect(() => {
    const fn = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
        e.preventDefault();
        const input = document.querySelector(".lat__search input");
        if (input) input.focus();
      }
    };
    window.addEventListener("keydown", fn);
    return () => window.removeEventListener("keydown", fn);
  }, []);

  // Bottom strip resize (drag handle)
  const resizeRef = useRef(null);
  const startResize = (e) => {
    const startY = e.clientY, startH = bottomH;
    const onMove = (ev) => {
      const dy = startY - ev.clientY;
      setBottomH(Math.max(180, Math.min(440, startH + dy)));
    };
    const onUp = () => { document.removeEventListener("mousemove", onMove); document.removeEventListener("mouseup", onUp); document.body.style.cursor = ""; };
    document.addEventListener("mousemove", onMove); document.addEventListener("mouseup", onUp);
    document.body.style.cursor = "ns-resize";
  };

  const openIncidentFor = (ev) => {
    const node = NODES.find(n => n.id === ev.node) || NODES.find(n => n.status === "crit") || NODES[0];
    setIncident({ event: ev, node });
  };

  const stats = [
    { label: "INGEST",         value: "126.4", unit: "Mpps", sub: "sustained 90s",   tone: "var(--success)" },
    { label: "MESH HEALTH",    value: "98.4",  unit: "%",    sub: "p99 18 ms",       tone: "var(--success)" },
    { label: "ACTIVE NODES",   value: "19",    unit: "/19",  sub: "0 unreachable",   tone: "var(--success)" },
    { label: "ANOMALIES (1h)", value: "3",     unit: "",     sub: "1 critical",      tone: "var(--destructive)" },
    { label: "INFERENCE",      value: "13.6k", unit: "rps",  sub: "p99 12 ms",       tone: "var(--info)" },
  ];

  const mainGrid = active === "map"
    ? { gridTemplateRows: `auto minmax(0, 1fr) ${bottomH}px`, padding: "20px 24px 24px" }
    : { gridTemplateRows: "minmax(0, 1fr)", padding: 0 };

  return (
    <div className={"lat" + (active === "meridian" ? " lat--wide" : "") + (eventsOpen ? " lat--events-open" : "")}>
      <Topbar
        region={tweaks.region} setRegion={(r) => setTweak("region", r)}
        isDark={tweaks.dark}   setIsDark={(d) => setTweak("dark", d)}
        theme={tweaks.theme}   setTheme={(t) => setTweak("theme", t)}
        events={events} nodes={NODES}
        onOpenIncident={openIncidentFor}
        onSelectNode={(n) => { setActive("map"); setSelected(n.id); }}
        onSwitchView={setActive}
        unread={unread} setUnread={setUnread}
        onToggleEvents={() => setEventsOpen(o => !o)}
      />
      <Rail active={active} setActive={setActive}/>

      <div className={"lat__main" + (active !== "map" ? " lat__main--view" : "")} style={mainGrid}>
        {active === "map" && <>
          <StatBar stats={stats}/>
          <MapBoundary><WorldMap
            nodes={NODES} mesh={mesh}
            showMesh={tweaks.showMesh}
            flights={flights}
            isDark={tweaks.dark}
            region={tweaks.region}
            selected={selected} setSelected={setSelected}
            onOpenNode={(n) => setSelected(n.id)}
          /></MapBoundary>
          <div className="bottom" style={{ position: "relative" }}>
            <div className="bottom-resize" onMouseDown={startResize}/>
            {panelOrder.map(id => {
              const panel =
                id === "modules"   ? <ModulesCard/>               :
                id === "inference" ? <InferenceCard/>             :
                id === "telemetry" ? <TelemetryCard tick={tick}/> :
                id === "fusion"    ? <AlarmFusionCard tick={tick}/> : null;
              return <DraggablePanel key={id} id={id} order={panelOrder} setOrder={setPanelOrder}>{panel}</DraggablePanel>;
            })}
          </div>
        </>}

        {active === "meridian" && <MeridianView/>}
        {active === "edge"  && <EdgeView  nodes={NODES} onOpenNode={(n) => { setActive("map"); setSelected(n.id); }} query=""/>}
        {active === "ai"    && <AIView    query=""/>}
        {active === "telem" && <TelemView tick={tick}/>}
        {active === "sec"   && <SecView   onOpenIncident={(i) => {
          const n = NODES.find(x => x.id === i.node) || NODES[0];
          setIncident({ event: { ...i, msg: i.title, time: i.time }, node: n });
        }} query=""/>}
        {active === "logs"  && <LogsView  events={events} query=""/>}
      </div>

      {eventsOpen && <div className="ev-scrim" onClick={() => setEventsOpen(false)}/>}
      <EventStream events={events} filter={filter} setFilter={setFilter} onOpenIncident={openIncidentFor}/>

      {incident && (
        <Incident
          incident={incident}
          tick={tick}
          dispatched={dispatched}
          flights={flights}
          onClose={() => setIncident(null)}
          onDispatchDrone={(drone, from, to) => {
            setDispatched(d => d.includes(drone.id) ? d : [...d, drone.id]);
            setFlights(f => [...f, { id: Math.random().toString(36).slice(2, 7), drone, from, to, startedAt: Date.now() }]);
          }}
        />
      )}

      <TweaksPanel title="Tweaks">
        <TweakSection title="Theme & mode">
          <TweakSelect tweak="theme" label="Intention"
            options={[
              { value: "cream",   label: "cream · oxblood" },
              { value: "trust",   label: "trust · blue-gray" },
              { value: "urgency", label: "urgency · red-gray" },
              { value: "spirit",  label: "spirit · violet-gray" },
              { value: "harmony", label: "harmony · teal-gray" },
              { value: "neutral", label: "neutral · achromatic" },
            ]}/>
          <TweakToggle tweak="dark" label="Dark mode"/>
        </TweakSection>
        <TweakSection title="Map">
          <TweakToggle tweak="showMesh" label="Show zenoh mesh"/>
          <TweakSelect tweak="region" label="Region focus"
            options={Object.keys(REGIONS)}/>
        </TweakSection>
        <TweakSection title="Event stream">
          <TweakSlider tweak="eventSpeed" label="Speed (events/sec)" min={0.2} max={6} step={0.2} format={v => v.toFixed(1) + "×"}/>
        </TweakSection>
        <TweakSection title="Demo">
          <TweakButton onClick={() => {
            const n = NODES.find(x => x.id === "LAT-AF-02") || NODES[0];
            setIncident({ event: { id: "demo", sev: "crit", title: "Geofence breach", node: n.id, time: "now", msg: "" }, node: n });
          }}>Open sample incident →</TweakButton>
          <TweakButton onClick={() => { setFlights([]); setDispatched([]); }}>Recall flight units</TweakButton>
          <TweakButton onClick={() => {
            localStorage.removeItem("lattice:panels");
            localStorage.removeItem("lattice:bottomH");
            localStorage.removeItem("lattice:view");
            setPanelOrder(["modules","inference","fusion","telemetry"]);
            setBottomH(280);
            setActive("map");
          }}>Reset layout</TweakButton>
          <div className="muted" style={{ fontSize: 11, lineHeight: 1.5 }}>
            Drag bottom panels to reorder · drag the top edge of the strip to resize · click any warn/crit event to open incident.
          </div>
        </TweakSection>
      </TweaksPanel>
    </div>
  );
}

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