import { Router } from "express";
import { existsSync, rmSync } from "fs";
import { createAgent, listAgents, getAgent, deleteAgent, agentExists, isValidAlias, getAgentWorkspacePath, ensureAgentWorkspaceDir, } from "../services/agent-file-service.js";
import { compileSystemPrompt, scaffoldWorkspace } from "../services/claude-compiler.js";
import { agentWorkspaceRouter } from "./agent-workspace.js";
import { agentMemoryRouter } from "./agent-memory.js";
import { agentCronJobsRouter } from "./agent-cron-jobs.js";
import { agentActivityRouter } from "./agent-activity.js";
import { agentTriggersRouter } from "./agent-triggers.js";
import { agentExportImportRouter } from "./agent-export-import.js";
import { cancelAllJobsForAgent, scheduleJob } from "../services/cron-scheduler.js";
import { ensureDefaultCronJobs, listCronJobs } from "../services/agent-cron-jobs.js";
import { appendActivity } from "../services/agent-activity.js";
import { ensureCallerConfigForAlias } from "../services/connection-manager.js";
import { resolveAgentKeyAlias, routeKeyAliasForPersist } from "../services/agent-settings.js";
export const agentsRouter = Router();
// Mount sub-routers for agent operational data
agentsRouter.use("/:alias/workspace", agentWorkspaceRouter);
agentsRouter.use("/:alias/memory", agentMemoryRouter);
agentsRouter.use("/:alias/cron-jobs", agentCronJobsRouter);
agentsRouter.use("/:alias/activity", agentActivityRouter);
agentsRouter.use("/:alias/triggers", agentTriggersRouter);
agentsRouter.use("/", agentExportImportRouter);
const SERVER_TIMEZONE = Intl.DateTimeFormat().resolvedOptions().timeZone;
function withWorkspacePath(agent) {
    const workspacePath = ensureAgentWorkspaceDir(agent.alias);
    const resolved = resolveAgentKeyAlias(agent);
    return { ...resolved, workspacePath, serverTimezone: SERVER_TIMEZONE };
}
agentsRouter.get("/", (_req, res) => {
    const agents = listAgents().map(withWorkspacePath);
    res.json({ agents });
});
agentsRouter.post("/", (req, res) => {
    const { name, alias, description, systemPrompt, emoji, personality, role, tone } = req.body;
    if (!name || !alias || !description) {
        res.status(400).json({ error: "Name, alias, and description are required" });
        return;
    }
    if (typeof name !== "string" || name.trim().length === 0 || name.length > 128) {
        res.status(400).json({ error: "Name must be 1-128 characters" });
        return;
    }
    if (!isValidAlias(alias)) {
        res.status(400).json({
            error: "Alias must be 2-64 characters: lowercase letters, numbers, hyphens, underscores. Must start with a letter or number.",
        });
        return;
    }
    if (typeof description !== "string" || description.trim().length === 0 || description.length > 512) {
        res.status(400).json({ error: "Description must be 1-512 characters" });
        return;
    }
    if (systemPrompt !== undefined && systemPrompt !== null && typeof systemPrompt !== "string") {
        res.status(400).json({ error: "System prompt must be a string" });
        return;
    }
    if (agentExists(alias)) {
        res.status(409).json({ error: `An agent with alias "${alias}" already exists` });
        return;
    }
    const config = {
        name: name.trim(),
        alias: alias.trim(),
        description: description.trim(),
        systemPrompt: systemPrompt?.trim() || undefined,
        createdAt: Date.now(),
        ...(emoji && { emoji }),
        ...(personality && { personality: personality.trim() }),
        ...(role && { role: role.trim() }),
        ...(tone && { tone: tone.trim() }),
    };
    createAgent(config);
    // Ensure workspace directory exists and scaffold initial files
    const workspacePath = ensureAgentWorkspaceDir(config.alias);
    scaffoldWorkspace(workspacePath);
    // Create default cron jobs (e.g., heartbeat) and schedule them
    const defaultJobs = ensureDefaultCronJobs(config.alias);
    for (const job of defaultJobs) {
        if (job.status === "active") {
            scheduleJob(config.alias, job);
        }
    }
    res.status(201).json({ agent: { ...config, workspacePath, serverTimezone: SERVER_TIMEZONE } });
});
// Identity prompt — returns compiled identity + pre-loaded workspace context for SDK systemPrompt.append
agentsRouter.get("/:alias/identity-prompt", (req, res) => {
    const alias = req.params.alias;
    const agent = getAgent(alias);
    if (!agent) {
        res.status(404).json({ error: "Agent not found" });
        return;
    }
    const workspacePath = getAgentWorkspacePath(alias);
    const prompt = compileSystemPrompt(agent, workspacePath).prompt;
    res.json({ prompt });
});
// System message preview — full append string plus a per-section breakdown with
// size estimates, for the dashboard preview UI
agentsRouter.get("/:alias/system-message-preview", (req, res) => {
    const alias = req.params.alias;
    const agent = getAgent(alias);
    if (!agent) {
        res.status(404).json({ error: "Agent not found" });
        return;
    }
    const workspacePath = getAgentWorkspacePath(alias);
    const compiled = compileSystemPrompt(agent, workspacePath);
    res.json({
        sections: compiled.sections,
        fullPrompt: compiled.prompt,
        totalChars: compiled.totalChars,
        totalEstTokens: compiled.totalEstTokens,
        notes: {
            basePrompt: "This content is appended to the provider's base system prompt (e.g. the Claude Code preset), whose size is not included here.",
            runtimeAdditions: "When proxy mode is enabled, a listing of available API connections is also appended at session start.",
        },
    });
});
agentsRouter.get("/:alias", (req, res) => {
    const alias = req.params.alias;
    const agent = getAgent(alias);
    if (!agent) {
        res.status(404).json({ error: "Agent not found" });
        return;
    }
    res.json({ agent: withWorkspacePath(agent) });
});
agentsRouter.put("/:alias", (req, res) => {
    const alias = req.params.alias;
    const existing = getAgent(alias);
    if (!existing) {
        res.status(404).json({ error: "Agent not found" });
        return;
    }
    const { name, description, systemPrompt, emoji, personality, role, tone, pronouns, languages, guidelines, userName, userTimezone, userLocation, userContext, eventSubscriptions, mcpKeyAlias, enabled, } = req.body;
    // Build updated config — only override fields present in request body
    let updated = {
        ...existing,
        ...(name !== undefined && { name: name.trim() }),
        ...(description !== undefined && { description: description.trim() }),
        ...(systemPrompt !== undefined && { systemPrompt: systemPrompt?.trim() || undefined }),
        ...(emoji !== undefined && { emoji: emoji || undefined }),
        ...(personality !== undefined && { personality: personality?.trim() || undefined }),
        ...(role !== undefined && { role: role?.trim() || undefined }),
        ...(tone !== undefined && { tone: tone?.trim() || undefined }),
        ...(pronouns !== undefined && { pronouns: pronouns?.trim() || undefined }),
        ...(languages !== undefined && { languages }),
        ...(guidelines !== undefined && { guidelines }),
        ...(userName !== undefined && { userName: userName?.trim() || undefined }),
        ...(userTimezone !== undefined && { userTimezone: userTimezone?.trim() || undefined }),
        ...(userLocation !== undefined && { userLocation: userLocation?.trim() || undefined }),
        ...(userContext !== undefined && { userContext: userContext?.trim() || undefined }),
        ...(eventSubscriptions !== undefined && { eventSubscriptions }),
        ...(enabled !== undefined && { enabled }),
    };
    // Route mcpKeyAlias to the correct per-mode field and strip before persist
    if (mcpKeyAlias !== undefined) {
        updated = routeKeyAliasForPersist(updated, mcpKeyAlias);
    }
    else {
        // Even if mcpKeyAlias wasn't sent, strip the transient field from persisted config
        delete updated.mcpKeyAlias;
    }
    // Validate required fields
    if (!updated.name || updated.name.length === 0 || updated.name.length > 128) {
        res.status(400).json({ error: "Name must be 1-128 characters" });
        return;
    }
    if (!updated.description || updated.description.length === 0 || updated.description.length > 512) {
        res.status(400).json({ error: "Description must be 1-512 characters" });
        return;
    }
    // Persist (createAgent acts as upsert — mkdirSync with recursive is a no-op)
    createAgent(updated);
    // Auto-create caller config if the agent was assigned a proxy key alias
    // that doesn't yet have a corresponding entry in remote.config.json.
    const resolved = resolveAgentKeyAlias(updated);
    if (resolved.mcpKeyAlias) {
        ensureCallerConfigForAlias(resolved.mcpKeyAlias);
    }
    const workspacePath = ensureAgentWorkspaceDir(alias);
    res.json({ agent: { ...resolved, workspacePath, serverTimezone: SERVER_TIMEZONE } });
});
agentsRouter.patch("/:alias/toggle", (req, res) => {
    const alias = req.params.alias;
    const existing = getAgent(alias);
    if (!existing) {
        res.status(404).json({ error: "Agent not found" });
        return;
    }
    const { enabled } = req.body;
    if (typeof enabled !== "boolean") {
        res.status(400).json({ error: "enabled must be a boolean" });
        return;
    }
    // Persist the updated enabled state
    const updated = { ...existing, enabled };
    createAgent(updated);
    if (enabled) {
        // Re-schedule all active cron jobs
        ensureDefaultCronJobs(alias);
        const jobs = listCronJobs(alias);
        for (const job of jobs) {
            if (job.status === "active") {
                scheduleJob(alias, job);
            }
        }
    }
    else {
        // Cancel all scheduled cron jobs
        cancelAllJobsForAgent(alias);
    }
    appendActivity(alias, {
        type: "system",
        message: enabled ? "Agent enabled" : "Agent disabled",
    });
    const resolvedToggle = resolveAgentKeyAlias(updated);
    const workspacePath = ensureAgentWorkspaceDir(alias);
    res.json({ agent: { ...resolvedToggle, workspacePath, serverTimezone: SERVER_TIMEZONE } });
});
agentsRouter.delete("/:alias", (req, res) => {
    const alias = req.params.alias;
    // Cancel all cron jobs before deleting
    cancelAllJobsForAgent(alias);
    const deleted = deleteAgent(alias);
    if (!deleted) {
        res.status(404).json({ error: "Agent not found" });
        return;
    }
    // Clean up workspace directory
    const workspacePath = getAgentWorkspacePath(alias);
    if (existsSync(workspacePath)) {
        rmSync(workspacePath, { recursive: true, force: true });
    }
    res.json({ ok: true });
});
