import { spawnSync } from "node:child_process";
import { models as cursorFallbackModels } from "@rudderhq/agent-runtime-cursor-local";
const CURSOR_MODELS_TIMEOUT_MS = 5_000;
const CURSOR_MODELS_CACHE_TTL_MS = 60_000;
const MAX_BUFFER_BYTES = 512 * 1024;
let cached = null;
function dedupeModels(models) {
    const seen = new Set();
    const deduped = [];
    for (const model of models) {
        const id = model.id.trim();
        if (!id || seen.has(id))
            continue;
        seen.add(id);
        deduped.push({ id, label: model.label.trim() || id });
    }
    return deduped;
}
function sanitizeModelId(raw) {
    return raw
        .trim()
        .replace(/^["'`]+|["'`]+$/g, "")
        .replace(/\(.*\)\s*$/g, "")
        .trim();
}
function isLikelyModelId(raw) {
    const value = sanitizeModelId(raw);
    if (!value)
        return false;
    return /^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(value);
}
function pushModelId(target, raw) {
    const id = sanitizeModelId(raw);
    if (!isLikelyModelId(id))
        return;
    target.push({ id, label: id });
}
function collectFromJsonValue(value, target) {
    if (typeof value === "string") {
        pushModelId(target, value);
        return;
    }
    if (!Array.isArray(value))
        return;
    for (const item of value) {
        if (typeof item === "string") {
            pushModelId(target, item);
            continue;
        }
        if (typeof item !== "object" || item === null)
            continue;
        const id = item.id;
        if (typeof id === "string") {
            pushModelId(target, id);
        }
    }
}
export function parseCursorModelsOutput(stdout, stderr) {
    const models = [];
    const combined = `${stdout}\n${stderr}`;
    const trimmedStdout = stdout.trim();
    if (trimmedStdout.startsWith("{") || trimmedStdout.startsWith("[")) {
        try {
            const parsed = JSON.parse(trimmedStdout);
            if (Array.isArray(parsed)) {
                collectFromJsonValue(parsed, models);
            }
            else if (typeof parsed === "object" && parsed !== null) {
                const rec = parsed;
                collectFromJsonValue(rec.models, models);
                collectFromJsonValue(rec.data, models);
            }
        }
        catch {
            // Ignore malformed JSON and continue parsing plain text formats.
        }
    }
    for (const match of combined.matchAll(/available models?:\s*([^\n]+)/gi)) {
        const list = match[1] ?? "";
        for (const token of list.split(",")) {
            pushModelId(models, token);
        }
    }
    for (const lineRaw of combined.split(/\r?\n/)) {
        const line = lineRaw.trim();
        if (!line)
            continue;
        const bullet = line.replace(/^[-*]\s+/, "").trim();
        if (!bullet || bullet.includes(" "))
            continue;
        pushModelId(models, bullet);
    }
    return dedupeModels(models);
}
function mergedWithFallback(models) {
    return dedupeModels([...models, ...cursorFallbackModels]);
}
function defaultCursorModelsRunner() {
    const result = spawnSync("agent", ["models"], {
        encoding: "utf8",
        timeout: CURSOR_MODELS_TIMEOUT_MS,
        maxBuffer: MAX_BUFFER_BYTES,
    });
    return {
        status: result.status,
        stdout: typeof result.stdout === "string" ? result.stdout : "",
        stderr: typeof result.stderr === "string" ? result.stderr : "",
        hasError: Boolean(result.error),
    };
}
let cursorModelsRunner = defaultCursorModelsRunner;
function fetchCursorModelsFromCli() {
    const result = cursorModelsRunner();
    const { stdout, stderr } = result;
    if (result.hasError && stdout.trim().length === 0 && stderr.trim().length === 0) {
        return [];
    }
    if ((result.status ?? 1) !== 0 && !/available models?:/i.test(`${stdout}\n${stderr}`)) {
        return [];
    }
    return parseCursorModelsOutput(stdout, stderr);
}
export async function listCursorModels() {
    const now = Date.now();
    if (cached && cached.expiresAt > now) {
        return cached.models;
    }
    const discovered = fetchCursorModelsFromCli();
    if (discovered.length > 0) {
        const merged = mergedWithFallback(discovered);
        cached = {
            expiresAt: now + CURSOR_MODELS_CACHE_TTL_MS,
            models: merged,
        };
        return merged;
    }
    if (cached && cached.models.length > 0) {
        return cached.models;
    }
    return dedupeModels(cursorFallbackModels);
}
export function resetCursorModelsCacheForTests() {
    cached = null;
}
export function setCursorModelsRunnerForTests(runner) {
    cursorModelsRunner = runner ?? defaultCursorModelsRunner;
}
//# sourceMappingURL=cursor-models.js.map