import os from "node:os";
import path from "node:path";
import { tryLoadPersistedConfig } from "../../persisted-config.js";
const CACHE_TTL_MS = 5 * 60 * 1000;
const CODEX_THINKING_OPTIONS = [
    { id: "minimal", label: "Minimal" },
    { id: "low", label: "Low" },
    { id: "medium", label: "Medium" },
    { id: "high", label: "High", isDefault: true },
    { id: "xhigh", label: "Extra High" },
];
const CLAUDE_THINKING_OPTIONS = [
    { id: "low", label: "Low" },
    { id: "medium", label: "Medium", isDefault: true },
    { id: "high", label: "High" },
    { id: "max", label: "Max" },
];
function inferThinkingOptions(provider, modelId) {
    if (provider === "codex") {
        if (/^(gpt-5|o[13])/i.test(modelId)) {
            return { thinkingOptions: CODEX_THINKING_OPTIONS, defaultThinkingOptionId: "high" };
        }
        return undefined;
    }
    if (provider === "claude") {
        if (/^claude-(opus|sonnet)-4/i.test(modelId)) {
            return { thinkingOptions: CLAUDE_THINKING_OPTIONS, defaultThinkingOptionId: "medium" };
        }
        return undefined;
    }
    return undefined;
}
function decorateWithThinkingOptions(model) {
    if (model.thinkingOptions)
        return model;
    const inferred = inferThinkingOptions(model.provider, model.id);
    if (!inferred)
        return model;
    return {
        ...model,
        thinkingOptions: inferred.thinkingOptions,
        defaultThinkingOptionId: inferred.defaultThinkingOptionId,
    };
}
function getSeaworkHome() {
    return process.env.SEAWORK_HOME ?? path.join(os.homedir(), ".seawork");
}
/**
 * Read provider model overrides from `~/.seawork/config.json` →
 * `agents.models.<provider>` via the shared `loadPersistedConfig` parser.
 * Returns null when no valid override is configured. Going through the
 * persisted-config layer is the single source of truth for what counts as
 * a valid override — a malformed entry there gets dropped with a warning,
 * so this function and the daemon agree on the same view of the file.
 */
function loadLocalModelOverrides(provider) {
    const config = tryLoadPersistedConfig(getSeaworkHome());
    const list = config?.agents?.models?.[provider];
    if (!list || list.length === 0)
        return null;
    const models = list.map((entry) => decorateWithThinkingOptions({
        provider,
        id: entry.id,
        label: entry.label,
        description: entry.description,
        isDefault: entry.isDefault ?? false,
    }));
    if (!models.some((m) => m.isDefault)) {
        models[0].isDefault = true;
    }
    return models;
}
let cachedModels = null;
let cachedAt = 0;
// In-flight promise so concurrent callers share one fetch. Without this,
// two providers (claude + codex) hitting fetchAllModels at the same time
// after the cache expires both enter the network branch — and if one
// connection fails, that caller returns an empty array (driving its UI
// to the hardcoded FALLBACK_MODELS) even though the other concurrently
// succeeded. Sharing the promise also makes the cache write race-free.
let pendingFetch = null;
function getApiBaseUrl() {
    return process.env.SEAWORK_API_BASE_URL || null;
}
function getApiKey() {
    return process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY || null;
}
async function fetchAllModelsFromGateway() {
    const baseUrl = getApiBaseUrl();
    if (!baseUrl)
        return cachedModels ?? [];
    const apiKey = getApiKey();
    try {
        const headers = {};
        if (apiKey) {
            headers.Authorization = `Bearer ${apiKey}`;
        }
        const res = await fetch(`${baseUrl}/api/v1/models`, { headers });
        const json = (await res.json());
        if (json.code !== 0 || !json.data) {
            return cachedModels ?? [];
        }
        // API returns { "display-name": "gateway-model-id" }
        // Use the gateway model ID as the actual id (sent to the API),
        // and the display name as the label (shown in the UI).
        const entries = Object.entries(json.data);
        cachedModels = entries.map(([displayName, gatewayId], index) => decorateWithThinkingOptions({
            provider: resolveProvider(gatewayId),
            id: gatewayId,
            label: formatLabel(displayName),
            isDefault: index === 0,
        }));
        cachedAt = Date.now();
        return cachedModels;
    }
    catch {
        return cachedModels ?? [];
    }
}
async function fetchAllModels() {
    const now = Date.now();
    if (cachedModels && now - cachedAt < CACHE_TTL_MS) {
        return cachedModels;
    }
    if (pendingFetch) {
        return pendingFetch;
    }
    pendingFetch = fetchAllModelsFromGateway().finally(() => {
        pendingFetch = null;
    });
    return pendingFetch;
}
function resolveProvider(modelId) {
    if (modelId.startsWith("claude"))
        return "claude";
    if (modelId.startsWith("gemini"))
        return "claude";
    if (modelId.startsWith("gpt") || modelId.startsWith("o1") || modelId.startsWith("o3")) {
        return "codex";
    }
    return "claude";
}
function formatLabel(id) {
    return id
        .split("-")
        .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
        .join(" ");
}
const FALLBACK_MODELS = [
    {
        provider: "claude",
        id: "claude-opus-4-7[1m]",
        label: "Opus 4.7 1M",
        description: "Opus 4.7 with 1M context window",
        isDefault: false,
    },
    {
        provider: "claude",
        id: "claude-opus-4-7",
        label: "Opus 4.7",
        description: "Opus 4.7 · Most capable for complex work",
        isDefault: true,
    },
    {
        provider: "claude",
        id: "claude-sonnet-4-6",
        label: "Sonnet 4.6",
        description: "Sonnet 4.6 · Best for everyday tasks",
        isDefault: false,
    },
    {
        provider: "claude",
        id: "claude-haiku-4-5",
        label: "Haiku 4.5",
        description: "Haiku 4.5 · Fastest for quick answers",
        isDefault: false,
    },
    {
        provider: "claude",
        id: "gemini-3.1-pro-preview-seawork",
        label: "Gemini 3.1 Pro",
        description: "Gemini 3.1 Pro Preview · Served through the Seawork Claude gateway",
        isDefault: false,
    },
    {
        provider: "codex",
        id: "gpt-5.5",
        label: "GPT 5.5",
        description: "GPT 5.5 · Latest OpenAI model",
        isDefault: true,
    },
    {
        provider: "codex",
        id: "gpt-5.3-codex",
        label: "GPT 5.3 Codex",
        description: "GPT 5.3 Codex · Optimized for coding",
        isDefault: false,
    },
];
export async function getSeaworkModels(provider) {
    // Priority 1: local overrides in ~/.seawork/config.json (agents.models.<provider>).
    const local = loadLocalModelOverrides(provider);
    if (local) {
        return local;
    }
    // Priority 2: gateway-fetched list (cached).
    const all = await fetchAllModels();
    let filtered = all.filter((m) => m.provider === provider);
    // Priority 3: hardcoded fallback for offline / first-run.
    if (filtered.length === 0) {
        filtered = FALLBACK_MODELS.filter((m) => m.provider === provider).map((m) => decorateWithThinkingOptions({ ...m }));
    }
    if (filtered.length > 0 && !filtered.some((m) => m.isDefault)) {
        filtered[0].isDefault = true;
    }
    return filtered;
}
export function invalidateModelCache() {
    cachedModels = null;
    cachedAt = 0;
}
//# sourceMappingURL=seawork-models.js.map