// The macOS desktop scene: menu bar, dock, draggable app windows
// and their simulated contents (what Puck patrols).

import * as React from "react";
import type { EffectId } from "../engine";
import { type Pos, useDrag } from "../lib/useDrag";

export interface WinState {
  claude: "running" | "done";
  build: "running" | "done" | "fail";
  mail: string[];
  discordNoise: boolean;
  discordMention: boolean;
  calSoon: boolean;
}

export const initialWinState = (): WinState => ({
  claude: "running",
  build: "running",
  mail: ["hf"],
  discordNoise: true,
  discordMention: false,
  calSoon: false,
});

/** How an engine effect mutates the simulated windows. Pure; colocated with WinState. */
export function applyEffect(w: WinState, eff: EffectId): WinState {
  switch (eff) {
    case "claude_done":
      return { ...w, claude: "done" };
    case "build_done":
      return { ...w, build: "done" };
    case "build_fail":
      return { ...w, build: "fail" };
    case "mail_unread":
      return { ...w, mail: Array.from(new Set([...w.mail, w.mail.includes("hf") ? "fin" : "hf"])) };
    case "discord_mention":
      return { ...w, discordMention: true };
    case "discord_noise":
      return { ...w, discordNoise: true };
    case "calendar_soon":
      return { ...w, calSoon: true };
    case "none":
      return w;
  }
}

// ---- menu bar ---------------------------------------------------------------

export function MenuBar({
  moodLabel,
  puckActive,
  badge,
  onPuck,
  clock,
}: {
  moodLabel: string;
  puckActive: boolean;
  badge: number;
  onPuck: () => void;
  clock: string;
}) {
  return (
    <div className="menubar">
      <span className="mb-apple"></span>
      <span className="mb-app">Finder</span>
      <span className="mb-menu">File</span>
      <span className="mb-menu">Edit</span>
      <span className="mb-menu">View</span>
      <span className="mb-menu">Go</span>
      <span className="mb-menu">Window</span>
      <div className="mb-right">
        <button type="button" className={`mb-puck${puckActive ? " active" : ""}`} onClick={onPuck}>
          <span className="mb-puck-dot" />
          <span className="mb-puck-mood">Puck · {moodLabel}</span>
          {badge > 0 && <span className="badge">{badge}</span>}
        </button>
        <span className="mb-ico">􀙇</span>
        <span className="mb-ico">􀛨</span>
        <span className="mb-ico">􀊫</span>
        <span className="mb-clock">{clock}</span>
      </div>
    </div>
  );
}

// ---- dock ---------------------------------------------------------------------

export interface DockApp {
  id: string;
  name: string;
  glyph: string;
  running?: boolean;
  badge?: number;
  tint?: string;
}

export function Dock({ apps, onOpen }: { apps: DockApp[]; onOpen: (id: string) => void }) {
  return (
    <div className="dock">
      {apps.map((a) => (
        <div
          key={a.id}
          className={`dock-ico${a.running ? " run" : ""}`}
          title={a.name}
          onClick={() => onOpen(a.id)}
          style={a.tint ? { color: a.tint } : undefined}
        >
          <span>{a.glyph}</span>
          {(a.badge ?? 0) > 0 && <span className="dk-badge">{a.badge}</span>}
        </div>
      ))}
    </div>
  );
}

// ---- generic draggable window -------------------------------------------------

function AppWindow({
  id,
  pos,
  onPos,
  w,
  h,
  title,
  icon,
  sub,
  attn,
  dimmed,
  onFocus,
  z,
  children,
}: {
  id: string;
  pos: Pos;
  onPos: (id: string, p: Pos) => void;
  w: number;
  h: number;
  title: string;
  icon: string;
  sub?: string;
  attn: boolean;
  dimmed: boolean;
  onFocus: (id: string) => void;
  z: number;
  children: React.ReactNode;
}) {
  const drag = useDrag(pos, (p) => onPos(id, p));
  return (
    <div
      id={`win-${id}`}
      className={`win${attn ? " attn" : ""}${dimmed ? " dimmed" : ""}`}
      style={{ left: pos.x, top: pos.y, width: w, height: h, zIndex: z }}
      onMouseDown={() => onFocus(id)}
    >
      <div
        className="win-head"
        onMouseDown={(e) => {
          onFocus(id);
          drag(e);
        }}
      >
        <div className="traffic">
          <i className="r" />
          <i className="y" />
          <i className="g" />
        </div>
        <div className="win-title">
          <span className="wt-ico">{icon}</span>
          {title}
        </div>
        {sub && <div className="win-sub">{sub}</div>}
      </div>
      <div className="win-body">{children}</div>
    </div>
  );
}

// ---- window bodies --------------------------------------------------------

function ClaudeBody({ st }: { st: WinState }) {
  const done = st.claude === "done";
  return (
    <div>
      <div className="cc-line tok-dim">⏵ implement the memory garden panel</div>
      <div className="cc-line">
        <span className="tok-fn">edit</span> src/garden/Plot.tsx <span className="tok-dim">+48 −6</span>
      </div>
      <div className="cc-line">
        <span className="tok-fn">edit</span> src/garden/seed.ts <span className="tok-dim">+12 −0</span>
      </div>
      <div className="cc-line">
        <span className="tok-fn">write</span> src/garden/decay.ts <span className="tok-dim">new</span>
      </div>
      <div className={`cc-task${done ? " done" : ""}`}>
        {done ? <span className="spin">✓</span> : <span className="spin" />}
        <span>{done ? "Memory Garden — completed · 7 files" : "Memory Garden — running…"}</span>
        <span className="pct">{done ? "done" : "····"}</span>
      </div>
    </div>
  );
}

function TermBody({ st }: { st: WinState }) {
  const s = st.build;
  return (
    <div className="term">
      <div>
        <span className="prompt">vu@workshop</span> ~/puck $ npm run build
      </div>
      <div className="tok-dim">vite v5.3.1 building for production…</div>
      <div>
        transforming modules <span className="tok-dim">·····</span>
      </div>
      {s === "running" && (
        <>
          <div>bundling…</div>
          <div className="bar">
            <i style={{ width: "64%" }} />
          </div>
        </>
      )}
      {s === "done" && (
        <>
          <div className="ok">✓ built in 2m 14s — 0 errors</div>
          <div className="bar">
            <i style={{ width: "100%" }} />
          </div>
        </>
      )}
      {s === "fail" && (
        <>
          <div className="err">✗ 3 tests failing — auth.test.ts</div>
          <div className="warn"> expected 200, received 401</div>
        </>
      )}
    </div>
  );
}

interface ListRow {
  from: string;
  sub: string;
  time: string;
  unread?: boolean;
  mention?: boolean;
  noise?: boolean;
  muted?: boolean;
  av: string;
}

function MsgList({ rows }: { rows: ListRow[] }) {
  return (
    <div className="list">
      {rows.map((r) => (
        <div key={r.from + r.sub} className={`list-row${r.unread || r.mention ? " unread" : ""}`}>
          <div className={`avatar${r.muted || (r.noise && !r.mention) ? " muted" : ""}`}>{r.av}</div>
          <div className="lr-main">
            <div className="lr-top">
              <span className="lr-from">{r.from}</span>
              <span className="lr-time">{r.time}</span>
            </div>
            <div className="lr-sub">{r.sub}</div>
          </div>
          <div className={`lr-dot${r.unread || r.mention ? "" : " hidden"}`} />
        </div>
      ))}
    </div>
  );
}

function MailBody({ st }: { st: WinState }) {
  return (
    <MsgList
      rows={[
        {
          from: "Hugging Face",
          sub: "Build Small — submissions close Sunday",
          time: "9:41",
          unread: st.mail.includes("hf"),
          av: "H",
        },
        {
          from: "Fidelity",
          sub: "Your June statement is ready",
          time: "8:02",
          unread: st.mail.includes("fin"),
          av: "F",
          muted: true,
        },
        { from: "Mira Okafor", sub: "re: puck demo video", time: "Yest", av: "M" },
        { from: "GitHub", sub: "[puck] CI passed on main", time: "Yest", av: "G", muted: true },
      ]}
    />
  );
}

function DiscordBody({ st }: { st: WinState }) {
  return (
    <MsgList
      rows={[
        {
          from: "#general",
          sub: st.discordNoise ? "12 new messages · gif wars again" : "quiet for now",
          time: "now",
          noise: true,
          av: "#",
        },
        {
          from: "Mira",
          sub: "@you can you push the demo branch?",
          time: "now",
          mention: st.discordMention,
          av: "M",
        },
        { from: "#puck-build", sub: "anyone seen the holo weights?", time: "2m", av: "P" },
        { from: "#random", sub: "🐸🐸🐸", time: "5m", noise: true, av: "R" },
      ]}
    />
  );
}

function CalBody({ st }: { st: WinState }) {
  return (
    <div>
      <div className="cal-day">Today · Thu Jun 5</div>
      <div className="cal-ev" style={st.calSoon ? { borderLeftColor: "#ff5b54" } : undefined}>
        <div className="ce-t">Demo sync</div>
        <div className="ce-time">{st.calSoon ? "in 10 min · 11:00" : "11:00 – 11:30"}</div>
      </div>
      <div className="cal-ev" style={{ opacity: 0.6 }}>
        <div className="ce-t">Deep work — Puck UI</div>
        <div className="ce-time">14:00 – 16:00</div>
      </div>
    </div>
  );
}

// ---- the set of windows -----------------------------------------------------

interface WinDef {
  id: string;
  title: string;
  icon: string;
  sub: string;
  w: number;
  h: number;
  Body: React.ComponentType<{ st: WinState }>;
}

export const WIN_DEFS: WinDef[] = [
  { id: "claude", title: "Claude Code", icon: "✳", sub: "Desktop 2", w: 432, h: 296, Body: ClaudeBody },
  { id: "terminal", title: "workshop — build", icon: "▦", sub: "zsh", w: 404, h: 224, Body: TermBody },
  { id: "mail", title: "Mail", icon: "✉", sub: "Inbox", w: 364, h: 318, Body: MailBody },
  { id: "discord", title: "Discord", icon: "◍", sub: "Social Bog", w: 332, h: 236, Body: DiscordBody },
  { id: "calendar", title: "Calendar", icon: "◷", sub: "", w: 252, h: 184, Body: CalBody },
];

// memo: SimApp re-renders every sprite flight; the window scene only depends on these props
export const DesktopWindows = React.memo(function DesktopWindows({
  positions,
  onPos,
  onFocus,
  focused,
  attnId,
  winState,
}: {
  positions: Record<string, Pos>;
  onPos: (id: string, p: Pos) => void;
  onFocus: (id: string) => void;
  focused: string | null;
  attnId: string | null;
  winState: WinState;
}) {
  return (
    <>
      {WIN_DEFS.map((d, i) => (
        <AppWindow
          key={d.id}
          id={d.id}
          pos={positions[d.id]}
          onPos={onPos}
          w={d.w}
          h={d.h}
          title={d.title}
          icon={d.icon}
          sub={d.sub || undefined}
          attn={attnId === d.id}
          dimmed={!!focused && focused !== d.id && attnId !== d.id}
          onFocus={onFocus}
          z={focused === d.id ? 30 : 10 + i}
        >
          <d.Body st={winState} />
        </AppWindow>
      ))}
    </>
  );
});
