import { useEffect, useState, useCallback, useMemo, useRef } from "react";
import { LayoutEngine } from "./LayoutEngine";
import { Sidebar } from "./Sidebar";
import { TopBar } from "./TopBar";
import { CenterToolbar } from "./CenterToolbar";
import { Wizard } from "./Wizard";
import { EventCenter } from "./EventCenter";
import { maybeNotify } from "./notify";
import { COLORS } from "./theme";
import { getStatusFull, applyPreset, onDaemonEvent, onDaemonRawEvent, setWorkspaceMeta, focusSurface, getEventLog, markEventsRead, getHealth } from "./socketBridge";
import type { EventRecord, DaemonHealth } from "./socketBridge";
import { leafIds } from "./layoutTypes";
import type { Group, WorkspaceView, SurfaceState } from "./layoutTypes";

export function App() {
  const [groups, setGroups] = useState<Group[]>([]);
  const [workspaces, setWorkspaces] = useState<WorkspaceView[]>([]);
  const [activeId, setActiveId] = useState<string | null>(null);
  const [running, setRunning] = useState<Record<string, boolean>>({});
  const [states, setStates] = useState<Record<string, SurfaceState>>({});
  const [events, setEvents] = useState<EventRecord[]>([]);
  const [wizard, setWizard] = useState(false);
  const [eventsOpen, setEventsOpen] = useState(true);
  const [health, setHealth] = useState<DaemonHealth | null>(null);
  const [connected, setConnected] = useState(false);
  const [focusedId, setFocusedId] = useState<string | null>(null);
  const activeRef = useRef<string | null>(null);
  const wsRef = useRef<WorkspaceView[]>([]);
  activeRef.current = activeId;
  wsRef.current = workspaces;

  const seedEvents = useCallback(async () => {
    const log = await getEventLog();
    setEvents((existing) => {
      const byId = new Map<number, EventRecord>();
      for (const e of log.events) byId.set(e.id, e);          // daemon is authoritative for overlapping ids
      for (const e of existing) if (!byId.has(e.id)) byId.set(e.id, e); // keep live events not in the snapshot
      return [...byId.values()].sort((a, b) => b.id - a.id).slice(0, 1000);
    });
  }, []);

  const refresh = useCallback(async () => {
    const st = await getStatusFull();
    setGroups(st.groups);
    setWorkspaces(st.workspaces);
    const run: Record<string, boolean> = {};
    const stt: Record<string, SurfaceState> = {};
    st.workspaces.forEach((w) => Object.entries(w.surfaces).forEach(([id, sv]) => { run[id] = sv.running; stt[id] = sv.state; }));
    setRunning(run);
    setStates(stt);
    if (!activeRef.current && st.workspaces.length) setActiveId(st.workspaces[0].id);
  }, []);

  const loadHealth = useCallback(async () => {
    try { setHealth(await getHealth()); setConnected(true); }
    catch { setConnected(false); }
  }, []);

  const wsOf = (surfaceId: string): WorkspaceView | undefined =>
    wsRef.current.find((w) => surfaceId in w.surfaces);

  useEffect(() => {
    void refresh();
    void seedEvents();
    void loadHealth();
    const unlisten = onDaemonEvent((evt) => {
      if (evt.evt === "event") {
        const rec = evt.data.record;
        setEvents((es) => [rec, ...es].slice(0, 1000));
        const w = wsOf(rec.surface_id);
        if (w && w.id !== activeRef.current) void setWorkspaceMeta(w.id, { unread: true });
        void maybeNotify(rec.surface_id, rec.agent_label ?? "shell", rec.workspace_name, rec.kind);
      } else if (evt.evt === "events_read") {
        const ids = new Set(evt.data.ids);
        setEvents((es) => es.map((e) => (ids.has(e.id) ? { ...e, read: true } : e)));
      } else if (evt.evt === "state") {
        setStates((m) => ({ ...m, [evt.data.surface_id]: evt.data.state }));
        void refresh();
      } else if (evt.evt === "exit") {
        void refresh();
      } else {
        void refresh();
      }
    });
    const reconnect = onDaemonRawEvent("spacesh:disconnected", () => {
      setConnected(false);
      void refresh();
      void seedEvents();
      void loadHealth();
    });
    return () => { void unlisten.then((f) => f()); void reconnect.then((f) => f()); };
  }, [refresh, seedEvents, loadHealth]);

  const unread = useMemo(() => events.filter((e) => !e.read).length, [events]);
  const active = workspaces.find((w) => w.id === activeId) ?? null;
  const leaves = active ? leafIds(active.layout) : [];
  const effectiveFocus = focusedId && leaves.includes(focusedId) ? focusedId : leaves[0] ?? null;

  function selectWorkspace(id: string) {
    setActiveId(id);
    setFocusedId(null);
    void setWorkspaceMeta(id, { unread: false });
  }

  return (
    <div style={{ display: "flex", flexDirection: "column", height: "100vh", background: COLORS.bgApp }}>
      <TopBar active={active} eventsOpen={eventsOpen} onToggleEvents={() => setEventsOpen((v) => !v)} unread={unread} />
      <div style={{ flex: 1, display: "flex", minHeight: 0 }}>
        <Sidebar groups={groups} workspaces={workspaces} activeId={activeId} onSelect={selectWorkspace} onNew={() => setWizard(true)} health={health} connected={connected} />
        <div style={{ flex: 1, display: "flex", flexDirection: "column", minWidth: 0 }}>
          {active && (
            <CenterToolbar selected="" onSelect={(p) => { if (active) void applyPreset(active.id, p, []); }} />
          )}
          <div style={{ flex: 1, minHeight: 0 }}>
            {active
              ? <LayoutEngine workspaceId={active.id} layout={active.layout} running={running} states={states} surfaces={active.surfaces} focusedId={effectiveFocus} onFocus={setFocusedId} />
              : <div style={{ color: COLORS.textMuted, padding: 24 }}>No workspace — create one to begin.</div>}
          </div>
        </div>
        {eventsOpen && (
          <EventCenter
            events={events}
            onMarkAllRead={() => { void markEventsRead({ target: "all" }); }}
            onSelect={(sid, id) => { void focusSurface(sid); void markEventsRead({ target: "ids", value: [id] }); }}
          />
        )}
      </div>
      {wizard && <Wizard onDone={(id) => { setWizard(false); setActiveId(id); void refresh(); }} onCancel={() => setWizard(false)} />}
    </div>
  );
}
