/**
 * grader-api.ts
 *
 * Shared utility for calling LLM grading APIs from grader scripts.
 *
 * Dispatches to the correct provider API (OpenAI, Anthropic) based on the
 * grader model prefix. Reads the appropriate API key from environment.
 *
 * Also exports `loadGraderModel()` to resolve the grader from
 * `config/models`.
 *
 * Migrated from lib/grader-api.ts — no module-level side effects, no
 * process.exit(), accepts rootDir as parameter for file-based operations.
 */
import { ConsoleLogger } from "../adapters/loggers/index.js";
import { loadConfigFile } from "./compiler/config-loader.js";
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
 * Call the grader model once to score a response against a rubric.
 *
 * Dispatches to the correct provider API based on the model prefix.
 * Returns a numeric score (0–100) or null if the call or parse fails.
 */
export async function gradeOnce(graderModel, responseText, rubricText, logger) {
    const log = logger ?? new ConsoleLogger();
    const config = resolveProvider(graderModel);
    const prompt = `You are evaluating an AI assistant's response. Grade the response according to the following rubric.

## Response to evaluate:
${responseText.slice(0, 8000)}

## Rubric:
${rubricText}
`;
    try {
        const provider = graderModel.split(":")[0];
        let content;
        if (provider === "anthropic") {
            content = await callAnthropic(config, prompt, log);
        }
        else if (provider === "openai") {
            content = await callOpenAI(config, prompt, log);
        }
        else {
            // resolveProvider already throws for unknown providers, but just in case
            return null;
        }
        if (content === null)
            return null;
        const score = extractScore(content);
        if (score === null) {
            log.error(`  ⚠ Could not parse grader response: ${content.slice(0, 100)}`);
        }
        return score;
    }
    catch (err) {
        log.error(`  ⚠ Grader call failed: ${err instanceof Error ? err.message : String(err)}`);
        return null;
    }
}
/**
 * Load the grader model from `config/models`.
 * Returns both the model ID and human-readable label.
 * Falls back to `openai:gpt-5` if not configured.
 *
 * @throws Error if config/models is not found
 */
export function loadGraderModel(rootDir) {
    const data = loadConfigFile("models", rootDir).data;
    return {
        id: data?.grader?.id ?? "openai:gpt-5",
        label: data?.grader?.label ?? "GPT-5 (grader)",
    };
}
// ---------------------------------------------------------------------------
// Score extraction (exported for testing)
// ---------------------------------------------------------------------------
export function extractScore(content) {
    // Try JSON parse first: {"score": 85, "reason": "..."}
    try {
        const parsed = JSON.parse(content);
        if (typeof parsed === "object" &&
            parsed !== null &&
            "score" in parsed &&
            typeof parsed.score === "number") {
            return parsed.score;
        }
    }
    catch {
        // Not JSON — fall through
    }
    // Fallback: extract first bare number
    const match = content.match(/(\d+)/);
    if (match)
        return parseInt(match[1], 10);
    return null;
}
// ---------------------------------------------------------------------------
// Provider resolution (exported for testing)
// ---------------------------------------------------------------------------
/**
 * Parse a Promptfoo-style model ID and resolve the provider config.
 *
 * Supported formats:
 *   - `openai:chat:gpt-5.2`       → OpenAI, model = `gpt-5.2`
 *   - `openai:gpt-5`              → OpenAI, model = `gpt-5`
 *   - `anthropic:messages:claude-opus-4-5-20251101` → Anthropic, model = `claude-opus-4-5-20251101`
 *   - `anthropic:claude-sonnet-4`  → Anthropic, model = `claude-sonnet-4`
 */
export function resolveProvider(graderModel) {
    const parts = graderModel.split(":");
    const provider = parts[0];
    if (provider === "anthropic") {
        // "anthropic:messages:claude-opus-4-5" → "claude-opus-4-5"
        // "anthropic:claude-sonnet-4" → "claude-sonnet-4"
        const modelName = parts.length >= 3 && parts[1] === "messages"
            ? parts.slice(2).join(":")
            : parts.slice(1).join(":");
        const apiKey = process.env.ANTHROPIC_API_KEY;
        if (!apiKey) {
            throw new Error("ANTHROPIC_API_KEY not set. Required for grader model: " + graderModel);
        }
        return {
            apiKey,
            baseUrl: "https://api.anthropic.com/v1/messages",
            modelName,
        };
    }
    if (provider === "openai") {
        // "openai:chat:gpt-5.2" → "gpt-5.2", "openai:gpt-5" → "gpt-5"
        const modelName = parts.length >= 3 ? parts.slice(2).join(":") : parts[1];
        const apiKey = process.env.OPENAI_API_KEY;
        if (!apiKey) {
            throw new Error("OPENAI_API_KEY not set. Required for grader model: " + graderModel);
        }
        return {
            apiKey,
            baseUrl: "https://api.openai.com/v1/chat/completions",
            modelName,
        };
    }
    throw new Error(`Unsupported grader provider "${provider}" in model "${graderModel}". ` +
        "Supported: openai, anthropic.");
}
// ---------------------------------------------------------------------------
// Provider-specific API calls
// ---------------------------------------------------------------------------
async function callAnthropic(config, prompt, log) {
    const response = await fetch(config.baseUrl, {
        body: JSON.stringify({
            max_tokens: 256,
            messages: [{ content: prompt, role: "user" }],
            model: config.modelName,
            temperature: 0.2,
        }),
        headers: {
            "anthropic-version": "2023-06-01",
            "Content-Type": "application/json",
            "x-api-key": config.apiKey,
        },
        method: "POST",
    });
    if (!response.ok) {
        const text = await response.text();
        log.error(`  ⚠ Grader API error (Anthropic): ${response.status} ${text.slice(0, 200)}`);
        return null;
    }
    const data = (await response.json());
    const textBlock = data.content?.find((c) => c.type === "text");
    return textBlock?.text ?? "";
}
async function callOpenAI(config, prompt, log) {
    const response = await fetch(config.baseUrl, {
        body: JSON.stringify({
            max_tokens: 256,
            messages: [{ content: prompt, role: "user" }],
            model: config.modelName,
            temperature: 0.2,
        }),
        headers: {
            Authorization: `Bearer ${config.apiKey}`,
            "Content-Type": "application/json",
        },
        method: "POST",
    });
    if (!response.ok) {
        const text = await response.text();
        log.error(`  ⚠ Grader API error (OpenAI): ${response.status} ${text.slice(0, 200)}`);
        return null;
    }
    const data = (await response.json());
    return data.choices?.[0]?.message?.content ?? "";
}
