import { readFile } from "node:fs/promises";
import { createWriteStream } from "node:fs";
import { join } from "node:path";

const AGENTS_JSON =
  process.env.AGENTS_DIR
    ? join(process.env.AGENTS_DIR, "agents.json")
    : "/Users/teletranone/teletraan/agents/agents.json";

// ─── Types ───────────────────────────────────────────────────────────────────

interface AgentEntry {
  displayName: string;
  description: string;
  port: number;
  fifo: string;
  channelDir: string;
  enabled: boolean;
}

interface AgentsRegistry {
  agents: Record<string, AgentEntry>;
}

// ─── Registry Cache ──────────────────────────────────────────────────────────

let cachedRegistry: AgentsRegistry | null = null;

async function loadRegistry(): Promise<AgentsRegistry> {
  if (cachedRegistry) return cachedRegistry;
  const raw = await readFile(AGENTS_JSON, "utf-8");
  cachedRegistry = JSON.parse(raw) as AgentsRegistry;
  return cachedRegistry;
}

export function invalidateAgentCache(): void {
  cachedRegistry = null;
}

// ─── Lookup helpers ───────────────────────────────────────────────────────────

export async function getAgentFifo(agentName: string): Promise<string | null> {
  const registry = await loadRegistry();
  return registry.agents[agentName]?.fifo ?? null;
}

export async function getAgentPort(agentName: string): Promise<number | null> {
  const registry = await loadRegistry();
  return registry.agents[agentName]?.port ?? null;
}

export async function listAgents(): Promise<string[]> {
  const registry = await loadRegistry();
  return Object.keys(registry.agents);
}

// ─── FIFO Writer ─────────────────────────────────────────────────────────────

const WRITE_TIMEOUT_MS = 5000;

/**
 * Write a stream-json user message into an agent's FIFO.
 *
 * The text is wrapped in the Claude Code stream-json envelope so the receiving
 * Claude session picks it up as an incoming user message.
 *
 * Returns { ok: true } on success or { ok: false, error } on failure.
 */
export async function writeToAgentFifo(
  agentName: string,
  text: string,
  sessionId: string = "default"
): Promise<{ ok: boolean; error?: string }> {
  let fifoPath: string | null;
  try {
    fifoPath = await getAgentFifo(agentName);
  } catch (err) {
    return { ok: false, error: `Cannot load agent registry: ${err}` };
  }

  if (!fifoPath) {
    return { ok: false, error: `Agent "${agentName}" not found in registry` };
  }

  const envelope =
    JSON.stringify({
      type: "user",
      message: { role: "user", content: text },
      session_id: sessionId,
      parent_tool_use_id: null,
    }) + "\n";

  return new Promise((resolve) => {
    const timeoutId = setTimeout(() => {
      resolve({
        ok: false,
        error: `Write to ${agentName} FIFO timed out after ${WRITE_TIMEOUT_MS}ms — agent may not be running`,
      });
    }, WRITE_TIMEOUT_MS);

    let settled = false;
    function settle(result: { ok: boolean; error?: string }) {
      if (settled) return;
      settled = true;
      clearTimeout(timeoutId);
      resolve(result);
    }

    try {
      const stream = createWriteStream(fifoPath!, { flags: "a" });
      stream.on("error", (err) => {
        settle({ ok: false, error: `FIFO write error for ${agentName}: ${err.message}` });
      });
      stream.write(envelope, (err) => {
        stream.end();
        if (err) {
          settle({ ok: false, error: `FIFO write error for ${agentName}: ${err.message}` });
        } else {
          settle({ ok: true });
        }
      });
    } catch (err) {
      settle({ ok: false, error: `Cannot open FIFO for ${agentName}: ${err}` });
    }
  });
}

// ─── Message Formatters ───────────────────────────────────────────────────────

/**
 * Format a task_incoming message for the receiving agent's FIFO.
 */
export function formatTaskIncoming(opts: {
  taskId: string;
  fromAgent: string;
  task: string;
  context?: string;
  conversationId?: string;
}): string {
  const lines = [
    `<task_incoming task_id="${opts.taskId}" from="${opts.fromAgent}">`,
    opts.task,
  ];
  if (opts.context) {
    lines.push("", "Context:", opts.context);
  }
  if (opts.conversationId) {
    lines.push("", `conversation_id: ${opts.conversationId}`);
  }
  lines.push("</task_incoming>");
  lines.push("", `When done, call complete_task("${opts.taskId}", result, "complete")`);
  return lines.join("\n");
}

/**
 * Format a task_completed message for the dispatcher's FIFO.
 */
export function formatTaskCompleted(opts: {
  taskId: string;
  fromAgent: string;
  toAgent: string;
  status: string;
  result: string;
  conversationId?: string;
}): string {
  const lines = [
    `<task_completed task_id="${opts.taskId}" agent="${opts.toAgent}" status="${opts.status}">`,
    `Task assigned to ${opts.toAgent} is now ${opts.status}.`,
    "",
    "Result:",
    opts.result,
  ];
  if (opts.conversationId) {
    lines.push("", `conversation_id: ${opts.conversationId}`);
  }
  lines.push("</task_completed>");
  return lines.join("\n");
}
