/**
 * Claude Code provider — OAuth via CLAUDE_CODE_OAUTH_TOKEN.
 *
 * Spawns the `claude` CLI as subprocess (same pattern as scripts/02-vision-page.py).
 * The CLI reads CLAUDE_CODE_OAUTH_TOKEN from env and uses it instead of the
 * credentials file. NEVER uses ANTHROPIC_API_KEY — this project forbids it.
 *
 * Requires the `claude` binary in PATH. In Docker, install with:
 *   RUN curl -fsSL https://claude.ai/install.sh | bash
 */
import { spawn } from "node:child_process";
import type { ChatProvider, ChatRequest, ChatResponse } from "./types";

const MODEL = process.env.CLAUDE_CODE_MODEL || "haiku";
// W1-TD#30: subprocess timeout is now configurable. Default 90s matches the
// previous hard-coded value. Lower it (e.g. 60s) when the provider should bail
// out of slow generations sooner; raise it (e.g. 180s) when running heavier
// models like opus on long contexts.
const TIMEOUT_MS = Number(process.env.CLAUDE_CODE_TIMEOUT_MS || 90_000);

function buildPrompt(req: ChatRequest): string {
  // Single-shot prompt: collapse history into a structured transcript.
  const parts: string[] = [];
  parts.push(req.system);
  parts.push("\n\n# CONVERSATION HISTORY\n");
  const recent = req.messages.slice(-20);
  for (const m of recent.slice(0, -1)) {
    parts.push(`${m.role.toUpperCase()}: ${m.content}`);
  }
  const last = recent[recent.length - 1];
  if (last) parts.push(`\nUSER ASKS NOW: ${last.content}`);
  return parts.join("\n");
}

export const claudeCodeProvider: ChatProvider = {
  name: "claude-code",
  isAvailable: () => Boolean(process.env.CLAUDE_CODE_OAUTH_TOKEN),
  async send(req: ChatRequest): Promise<ChatResponse> {
    const prompt = buildPrompt(req);
    const t0 = Date.now();

    const text = await new Promise<{ result: string; durationMs: number; tokensIn?: number; tokensOut?: number; costUsd?: number }>((resolve, reject) => {
      const child = spawn(
        "claude",
        [
          "-p",
          "--model", MODEL,
          "--output-format", "json",
          "--max-turns", "3",
          "--",
          prompt,
        ],
        {
          stdio: ["ignore", "pipe", "pipe"],
          env: { ...process.env },  // forwards CLAUDE_CODE_OAUTH_TOKEN
        },
      );
      let stdout = "";
      let stderr = "";
      child.stdout.on("data", (c) => (stdout += c.toString()));
      child.stderr.on("data", (c) => (stderr += c.toString()));
      const t = setTimeout(() => {
        child.kill("SIGKILL");
        reject(new Error(`claude CLI timeout > ${TIMEOUT_MS}ms`));
      }, TIMEOUT_MS);
      child.on("error", (e) => {
        clearTimeout(t);
        reject(new Error(`claude CLI spawn failed: ${e.message}`));
      });
      child.on("close", (code) => {
        clearTimeout(t);
        if (code !== 0) {
          // Detect rate limit shape — surface a typed error
          const msg = stderr.slice(-500) || `rc=${code}`;
          const err = new Error(`claude-code rc=${code}: ${msg}`);
          if (/usage limit|rate.?limit|429/i.test(stderr)) {
            (err as Error & { isRateLimit?: boolean }).isRateLimit = true;
          }
          return reject(err);
        }
        try {
          const cli = JSON.parse(stdout);
          if (cli.is_error) {
            return reject(new Error(`claude-code reported error: ${cli.result?.slice(0, 300)}`));
          }
          resolve({
            result: cli.result || "",
            durationMs: cli.duration_ms || Date.now() - t0,
            tokensIn: cli.usage?.input_tokens,
            tokensOut: cli.usage?.output_tokens,
            costUsd: cli.total_cost_usd,
          });
        } catch (e) {
          reject(new Error(`claude-code stdout parse: ${e instanceof Error ? e.message : String(e)}`));
        }
      });
    });

    return {
      provider: "claude-code",
      model: MODEL,
      content: text.result,
      tokensIn: text.tokensIn,
      tokensOut: text.tokensOut,
      costUsd: text.costUsd,
      durationMs: text.durationMs,
    };
  },
};
