import { useOfficeStore } from "@/store/office-store";
import { DESK_SLOTS, HOT_DESK_SLOTS } from "@/lib/positions";
import { ENTRANCE } from "@/lib/constants";

const WATCH_ORIGIN = "http://localhost:5179";
const SSE_URL = `${WATCH_ORIGIN}/events`;

/** Status decay: how long a live status persists without fresh events. */
const SPEAK_TTL = 8_000;
const TOOL_TTL = 12_000;
const THINK_TTL = 45_000;
const OFFLINE_AFTER = 5 * 60_000;
const SUB_MAX_AGE = 20 * 60_000;

interface SubMeta {
  agentId: string;
  parentId: string;
  startedAt: number;
}

let es: EventSource | null = null;
let decayTimer: ReturnType<typeof setInterval> | null = null;
const lastEventAt = new Map<string, number>();
const subsByToolId = new Map<string, SubMeta>();

const S = () => useOfficeStore.getState();

const mainId = (slug: string) => `proj-${slug}`;

function freeSlot(zone: "desk" | "hotDesk"): number {
  const slots = zone === "desk" ? DESK_SLOTS : HOT_DESK_SLOTS;
  const occupied = new Set(
    [...S().agents.values()].filter((a) => a.homeZone === zone).map((a) => a.homeSlot),
  );
  return slots.findIndex((_, i) => !occupied.has(i));
}

function seatProject(slug: string, name: string, lastActivity: number) {
  if (S().agents.has(mainId(slug))) return;
  const slot = freeSlot("desk");
  if (slot === -1) {
    S().addEvent("🈵", `工位已滿,略過專案 ${name}`);
    return;
  }
  const id = mainId(slug);
  S().spawnAgent({ id, name, role: "agent", model: "claude code session", homeSlot: slot });
  S().startWalk(id, DESK_SLOTS[slot], "desk", { arriveStatus: "idle" });
  S().addEvent("🪑", `專案 ${name} 入座`);
  lastEventAt.set(id, lastActivity || Date.now());
}

function spawnSub(slug: string, subId: string, desc: string, kind: string) {
  const parentId = mainId(slug);
  if (!S().agents.has(parentId)) return;
  if (subsByToolId.has(subId)) return;
  const slot = freeSlot("hotDesk");
  if (slot === -1) return;
  const agentId = `live-sub-${subId}`;
  const name = desc || kind;
  S().spawnAgent({ id: agentId, name, role: "subagent", model: kind, homeSlot: slot, parentId });
  S().startWalk(agentId, HOT_DESK_SLOTS[slot], "hotDesk", { arriveStatus: "tool_calling" });
  S().addLink({ sourceId: parentId, targetId: agentId, strength: 0.6, kind: "spawn" });
  S().addEvent("✨", `${S().agents.get(parentId)?.name} 派出 subagent「${name}」`);
  subsByToolId.set(subId, { agentId, parentId, startedAt: Date.now() });
  lastEventAt.set(agentId, Date.now());
}

function endSub(subId: string) {
  const meta = subsByToolId.get(subId);
  if (!meta) return;
  subsByToolId.delete(subId);
  const agent = S().agents.get(meta.agentId);
  if (!agent) return;
  S().removeLink(meta.parentId, meta.agentId);
  S().setSpeech(meta.agentId, null);
  S().addEvent("↩️", `subagent「${agent.name}」完成,離開辦公室`);
  S().startWalk(meta.agentId, { ...ENTRANCE }, "corridor", { despawnOnArrive: true });
}

function bump(id: string) {
  lastEventAt.set(id, Date.now());
}

function onEvent(e: MessageEvent) {
  let ev: Record<string, unknown>;
  try {
    ev = JSON.parse(e.data as string);
  } catch {
    return;
  }
  const slug = ev.slug as string;
  const id = slug ? mainId(slug) : "";

  switch (ev.type) {
    case "snapshot": {
      const projects = ev.projects as Array<{ slug: string; name: string; lastActivity: number }>;
      for (const p of projects.slice(0, DESK_SLOTS.length)) {
        seatProject(p.slug, p.name, p.lastActivity);
      }
      const subs = ev.subs as Array<{ subId: string; slug: string; desc: string; kind: string }>;
      for (const sub of subs) spawnSub(sub.slug, sub.subId, sub.desc, sub.kind);
      break;
    }
    case "project":
      seatProject(slug, ev.name as string, ev.lastActivity as number);
      break;
    case "prompt": {
      if (!S().agents.has(id)) break;
      S().setStatus(id, "thinking");
      S().addEvent("📋", `${S().agents.get(id)?.name} 收到新任務`);
      bump(id);
      break;
    }
    case "tool": {
      if (!S().agents.has(id)) break;
      S().setStatus(id, "tool_calling");
      S().setTool(id, { name: ev.tool as string, startedAt: Date.now() });
      bump(id);
      break;
    }
    case "speech": {
      if (!S().agents.has(id)) break;
      S().setStatus(id, "speaking");
      S().setSpeech(id, ev.text as string);
      bump(id);
      break;
    }
    case "subagent_spawn":
      spawnSub(slug, ev.subId as string, ev.desc as string, ev.kind as string);
      bump(id);
      break;
    case "subagent_end":
      endSub(ev.subId as string);
      bump(id);
      break;
    case "task_accepted": {
      const agent = S().agents.get(id);
      if (!agent) break;
      S().setStatus(id, "thinking");
      S().addEvent("📨", `已派發任務給 ${agent.name}${ev.resume ? "(續上次 session)" : ""}`);
      bump(id);
      break;
    }
    case "task_done": {
      const agent = S().agents.get(id);
      if (!agent) break;
      if (ev.code === 0) {
        S().addEvent("🏁", `${agent.name} 的任務行程結束`);
      } else {
        S().setStatus(id, "error");
        S().addEvent("⚠️", `${agent.name} 任務失敗:${(ev.error as string) ?? `exit ${ev.code}`}`);
      }
      bump(id);
      break;
    }
    case "chat_start": {
      if (!S().agents.has(id)) break;
      S().setStatus(id, "thinking");
      bump(id);
      break;
    }
    case "chat_text": {
      if (!S().agents.has(id)) break;
      S().chatAppend(id, { role: "assistant", text: ev.text as string });
      S().setStatus(id, "speaking");
      S().setSpeech(id, ev.text as string);
      bump(id);
      break;
    }
    case "chat_done": {
      if (!S().agents.has(id)) break;
      S().chatSetBusy(id, false);
      const thread = S().chats.get(id);
      const lastAssistant = [...(thread?.messages ?? [])].reverse().find((m) => m.role === "assistant");
      const finalText = (ev.text as string) ?? "";
      if (ev.ok) {
        // stream-json 的 result 可能與最後一則 assistant 相同,避免重複
        if (finalText && finalText !== lastAssistant?.text) {
          S().chatAppend(id, { role: "assistant", text: finalText });
          S().setSpeech(id, finalText);
          S().setStatus(id, "speaking");
        }
      } else {
        S().chatAppend(id, { role: "system", text: `執行失敗:${(ev.error as string) ?? "未知錯誤"}` });
        S().setStatus(id, "error");
      }
      bump(id);
      break;
    }
  }
}

/** Dispatch a real headless task to a project via the watcher. */
export async function dispatchTask(
  agentId: string,
  prompt: string,
  resume: boolean,
): Promise<{ ok: boolean; error?: string; queued?: number }> {
  const slug = agentId.replace(/^proj-/, "");
  try {
    const res = await fetch(`${WATCH_ORIGIN}/task`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ slug, prompt, resume }),
    });
    return (await res.json()) as { ok: boolean; error?: string; queued?: number };
  } catch {
    return { ok: false, error: "無法連線監看伺服器 (npm run watch)" };
  }
}

/** Send a chat message to the project's office thread (auto-resumed server-side). */
export async function sendChat(
  agentId: string,
  message: string,
  fromProject: boolean,
): Promise<boolean> {
  const slug = agentId.replace(/^proj-/, "");
  S().chatAppend(agentId, { role: "user", text: message });
  S().chatSetBusy(agentId, true);
  try {
    const res = await fetch(`${WATCH_ORIGIN}/chat`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ slug, message, fromProject }),
    });
    const data = (await res.json()) as { ok: boolean; error?: string };
    if (!data.ok) {
      S().chatSetBusy(agentId, false);
      S().chatAppend(agentId, {
        role: "system",
        text: data.error === "busy" ? "上一則還在執行中,請稍候" : `送出失敗:${data.error ?? "未知錯誤"}`,
      });
      return false;
    }
    return true;
  } catch {
    S().chatSetBusy(agentId, false);
    S().chatAppend(agentId, { role: "system", text: "無法連線監看伺服器 (npm run watch)" });
    return false;
  }
}

/** Reset the office chat thread (kills any in-flight run server-side). */
export async function resetChat(agentId: string): Promise<void> {
  const slug = agentId.replace(/^proj-/, "");
  S().chatClear(agentId);
  try {
    await fetch(`${WATCH_ORIGIN}/chat`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ slug, reset: true }),
    });
  } catch {
    /* offline — local clear is enough */
  }
}

/** Fade statuses back to idle / offline when a project goes quiet. */
function decayPass() {
  const now = Date.now();
  for (const agent of S().agents.values()) {
    if (!agent.id.startsWith("proj-")) continue;
    const last = lastEventAt.get(agent.id) ?? now;
    const age = now - last;
    if (agent.status === "speaking" && age > SPEAK_TTL) S().setStatus(agent.id, "idle");
    else if (agent.status === "tool_calling" && age > TOOL_TTL) S().setStatus(agent.id, "idle");
    else if (agent.status === "thinking" && age > THINK_TTL) S().setStatus(agent.id, "idle");
    else if (agent.status === "idle" && age > OFFLINE_AFTER) S().setStatus(agent.id, "offline");
    else if (agent.status === "offline" && age < OFFLINE_AFTER) S().setStatus(agent.id, "idle");
  }
  for (const [subId, meta] of subsByToolId) {
    if (now - meta.startedAt > SUB_MAX_AGE) endSub(subId);
  }
}

export function liveConnect() {
  if (es) return;
  es = new EventSource(SSE_URL);
  es.onmessage = onEvent;
  es.onopen = () => S().addEvent("🔌", "已連上實況伺服器");
  es.onerror = () => {
    if (es?.readyState === EventSource.CLOSED) {
      S().addEvent("⚠️", "實況伺服器連線中斷 (npm run watch)");
    }
  };
  decayTimer = setInterval(decayPass, 1000);
}

export function liveDisconnect() {
  es?.close();
  es = null;
  if (decayTimer) clearInterval(decayTimer);
  decayTimer = null;
  lastEventAt.clear();
  subsByToolId.clear();
}
