// apps/cli/src/templates/sdk-server.ts

/**
 * Light SDK server template — generates a Bun.serve HTTP wrapper for any reactive agent.
 *
 * Endpoints:
 *   POST /chat           — send message, get JSON response
 *   POST /chat/stream    — send message, get SSE stream
 *   GET  /health         — health check (container probes)
 *   GET  /ready          — readiness check (returns 503 if not ready)
 *   GET  /.well-known/agent.json — A2A agent card discovery
 *
 * This is designed to turn ANY agent into a deployable HTTP API with one command.
 */

export const sdkServerTemplate = (agentName: string) => `// raxd — Light SDK Server
// Generated by: rax deploy up --mode sdk
// Agent: ${agentName}
//
// Endpoints:
//   POST /chat             — { message: string } → { response, agentId, duration }
//   POST /chat/stream      — { message: string } → SSE text/event-stream
//   GET  /health           — health check for container probes
//   GET  /ready            — readiness (503 if agent not initialized)
//   GET  /.well-known/agent.json — A2A agent card

const PORT = parseInt(process.env.PORT ?? process.env.HEALTH_PORT ?? "3000");
const startTime = Date.now();
let agentReady = false;
let agent: Awaited<ReturnType<typeof buildAgent>> | null = null;
let requestCount = 0;

// ─── Agent Setup ────────────────────────────────────────────────────────────
// Import YOUR agent builder here. The server wraps whatever agent you build.

import { ReactiveAgents, registerShutdownHandlers } from "reactive-agents";

async function buildAgent() {
  // TODO: Customize your agent builder here
  const a = await ReactiveAgents.create()
    .withName("${agentName}")
    .withProvider(process.env.LLM_PROVIDER as any ?? "anthropic")
    .withModel(process.env.LLM_MODEL ?? "claude-sonnet-4-20250514")
    .withReasoning()
    .withTools()
    .withMemory("1")
    .build();
  return a;
}

// ─── Agent Card ─────────────────────────────────────────────────────────────

const agentCard = {
  name: "${agentName}",
  description: "Reactive agent deployed via rax deploy --mode sdk",
  url: \`http://localhost:\${PORT}\`,
  protocolVersion: "0.3.0",
  capabilities: {
    streaming: true,
    pushNotifications: false,
  },
  defaultInputModes: ["text/plain", "application/json"],
  defaultOutputModes: ["text/plain", "application/json"],
  skills: [],
};

// ─── Request Handlers ───────────────────────────────────────────────────────

async function handleChat(req: Request): Promise<Response> {
  if (!agent) {
    return Response.json({ error: "Agent not ready" }, { status: 503 });
  }

  let body: { message?: string };
  try {
    body = await req.json();
  } catch {
    return Response.json({ error: "Invalid JSON body" }, { status: 400 });
  }

  const message = body?.message;
  if (!message || typeof message !== "string") {
    return Response.json(
      { error: "Missing 'message' field (string)" },
      { status: 400 },
    );
  }

  requestCount++;
  const start = Date.now();

  try {
    const result = await agent.run(message);
    return Response.json({
      response: result.response,
      agentId: result.agentId,
      iterations: result.iterations,
      durationMs: Date.now() - start,
    });
  } catch (err: unknown) {
    const errorMessage = err instanceof Error ? err.message : "Agent execution failed";
    return Response.json({ error: errorMessage }, { status: 500 });
  }
}

async function handleChatStream(req: Request): Promise<Response> {
  if (!agent) {
    return Response.json({ error: "Agent not ready" }, { status: 503 });
  }

  if (typeof agent.runStream !== "function") {
    return Response.json(
      { error: "Streaming not enabled. Add .withStreaming() to your agent builder." },
      { status: 501 },
    );
  }

  let body: { message?: string };
  try {
    body = await req.json();
  } catch {
    return Response.json({ error: "Invalid JSON body" }, { status: 400 });
  }

  const message = body?.message;
  if (!message || typeof message !== "string") {
    return Response.json(
      { error: "Missing 'message' field (string)" },
      { status: 400 },
    );
  }

  requestCount++;

  const stream = new ReadableStream({
    async start(controller) {
      const encoder = new TextEncoder();
      try {
        for await (const event of agent!.runStream(message)) {
          const data = JSON.stringify(event);
          controller.enqueue(encoder.encode(\`data: \${data}\\n\\n\`));
        }
        controller.enqueue(encoder.encode("data: [DONE]\\n\\n"));
      } catch (err: unknown) {
        const errorMessage = err instanceof Error ? err.message : "Stream error";
        controller.enqueue(
          encoder.encode(\`data: \${JSON.stringify({ _tag: "StreamError", error: errorMessage })}\\n\\n\`),
        );
      } finally {
        controller.close();
      }
    },
  });

  return new Response(stream, {
    headers: {
      "content-type": "text/event-stream",
      "cache-control": "no-cache",
      connection: "keep-alive",
    },
  });
}

function handleHealth(): Response {
  return Response.json({
    status: agentReady ? "healthy" : "starting",
    uptime: Math.floor((Date.now() - startTime) / 1000),
    agent: "${agentName}",
    requests: requestCount,
    timestamp: new Date().toISOString(),
  });
}

function handleReady(): Response {
  if (!agentReady) {
    return Response.json(
      { status: "not_ready", agent: "${agentName}" },
      { status: 503 },
    );
  }
  return Response.json({ status: "ready", agent: "${agentName}" });
}

// ─── HTTP Server ────────────────────────────────────────────────────────────

const server = Bun.serve({
  port: PORT,
  async fetch(req) {
    const url = new URL(req.url);
    const method = req.method;

    // CORS preflight
    if (method === "OPTIONS") {
      return new Response(null, {
        headers: {
          "access-control-allow-origin": "*",
          "access-control-allow-methods": "GET, POST, OPTIONS",
          "access-control-allow-headers": "content-type, authorization",
        },
      });
    }

    // Route
    if (method === "GET" && url.pathname === "/health") return handleHealth();
    if (method === "GET" && url.pathname === "/ready") return handleReady();
    if (method === "GET" && url.pathname === "/.well-known/agent.json") {
      return Response.json(agentCard);
    }
    if (method === "POST" && url.pathname === "/chat") return handleChat(req);
    if (method === "POST" && url.pathname === "/chat/stream") return handleChatStream(req);

    return Response.json({ error: "Not found" }, { status: 404 });
  },
});

// ─── Startup ────────────────────────────────────────────────────────────────

console.log(\`raxd SDK server starting on port \${PORT}...\`);

try {
  agent = await buildAgent();
  agentReady = true;
  console.log(\`✅ Agent ready — \${agentCard.name}\`);
  console.log(\`   POST http://localhost:\${PORT}/chat\`);
  console.log(\`   POST http://localhost:\${PORT}/chat/stream\`);
  console.log(\`   GET  http://localhost:\${PORT}/health\`);
  console.log(\`   GET  http://localhost:\${PORT}/.well-known/agent.json\`);
} catch (err) {
  console.error("❌ Failed to build agent:", err);
  process.exit(1);
}

// ─── Graceful Shutdown ──────────────────────────────────────────────────────

process.on("SIGTERM", async () => {
  console.log("\\nSIGTERM received — shutting down...");
  server.stop();
  if (agent) await agent.dispose();
  process.exit(0);
});

process.on("SIGINT", async () => {
  console.log("\\nSIGINT received — shutting down...");
  server.stop();
  if (agent) await agent.dispose();
  process.exit(0);
});
`;

/** SDK-mode Dockerfile — optimized for the light server */
export const sdkDockerfileTemplate = (
  agentName: string,
) => `# raxd — Light SDK Server
# Generated by: rax deploy up --mode sdk
# Agent: ${agentName}

# ── Stage 1: Dependencies ────────────────────────────────────────────────────
FROM oven/bun:1-alpine AS deps

WORKDIR /app
COPY package.json bun.lock* ./
RUN bun install --frozen-lockfile --production 2>/dev/null || bun install --production

# ── Stage 2: Runtime ─────────────────────────────────────────────────────────
FROM oven/bun:1-alpine

RUN addgroup -g 1001 -S raxd && \\
    adduser -S raxd -u 1001 -G raxd && \\
    mkdir -p /app/data && chown -R raxd:raxd /app

WORKDIR /app

COPY --from=deps --chown=raxd:raxd /app/node_modules ./node_modules
COPY --chown=raxd:raxd . .

USER raxd

ENV PORT=3000

HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \\
  CMD wget -qO- http://localhost:3000/health || exit 1

STOPSIGNAL SIGTERM
EXPOSE 3000

ENTRYPOINT ["bun", "run", "server.ts"]
`;

/** SDK-mode monorepo Dockerfile */
export const sdkDockerfileMonorepoTemplate = (
  agentName: string,
  appPath: string,
) => `# raxd — Light SDK Server (monorepo)
# Generated by: rax deploy up --mode sdk
# Agent: ${agentName}
# Build context: workspace root

# ── Stage 1: Install + build workspace packages ─────────────────────────────
FROM oven/bun:1-alpine AS build

WORKDIR /app
COPY package.json bun.lock tsup.config.base.ts tsconfig.json ./
COPY packages/ packages/
COPY ${appPath}/ ${appPath}/
RUN bun install --frozen-lockfile 2>/dev/null || bun install
RUN bun run build:packages

# ── Stage 2: Runtime ─────────────────────────────────────────────────────────
FROM oven/bun:1-alpine

RUN addgroup -g 1001 -S raxd && \\
    adduser -S raxd -u 1001 -G raxd && \\
    mkdir -p /app/data /app/${appPath}/data && chown -R raxd:raxd /app

WORKDIR /app

COPY --from=build --chown=raxd:raxd /app/node_modules ./node_modules
COPY --from=build --chown=raxd:raxd /app/packages/ ./packages/
COPY --from=build --chown=raxd:raxd /app/${appPath}/ ./${appPath}/
COPY --from=build --chown=raxd:raxd /app/package.json ./package.json

USER raxd

ENV PORT=3000

HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \\
  CMD wget -qO- http://localhost:3000/health || exit 1

STOPSIGNAL SIGTERM
EXPOSE 3000

ENTRYPOINT ["bun", "run", "${appPath}/server.ts"]
`;
