/**
 * core/services/config-helpers.ts — Pure config generation helpers.
 *
 * No I/O — operates on typed inputs and returns typed outputs.
 *
 * Extracted from packages/eval/src/lib/generate-configs.ts during
 * the Ports & Adapters migration (Phase 4e).
 */
/**
 * Known Promptfoo provider prefixes, ordered longest-first so
 * "openai:chat:" is tried before "openai:".
 */
const PROVIDER_PREFIXES = [
    "anthropic:messages:",
    "openai:chat:",
    "openai:responses:",
    "openai:",
    "anthropic:",
    "google:",
];
/**
 * Extract the raw API model name from a Promptfoo provider ID.
 *
 * Promptfoo IDs encode the provider + sub-protocol + model, e.g.:
 * - "openai:chat:gpt-5.2" → "gpt-5.2"
 * - "anthropic:messages:claude-opus-4-6" → "claude-opus-4-6"
 * - "openrouter:deepseek/deepseek-r1" → "deepseek/deepseek-r1"
 * - "gpt-4o" → "gpt-4o"
 */
export function extractModelName(id) {
    for (const prefix of PROVIDER_PREFIXES) {
        if (id.startsWith(prefix)) {
            return id.slice(prefix.length);
        }
    }
    // Fallback: strip first colon-separated segment
    const parts = id.split(":");
    return parts.length > 1 ? parts.slice(1).join(":") : id;
}
/**
 * Extract the LLM provider family from a Promptfoo provider ID.
 *
 * - "openai:chat:gpt-5.2" → "openai"
 * - "anthropic:messages:claude-opus-4-6" → "anthropic"
 * - "google:gemini-2.5-pro" → "google"
 * - "gpt-4o" → "openai" (default)
 */
export function extractProvider(id) {
    const colon = id.indexOf(":");
    return colon > 0 ? id.slice(0, colon) : "openai";
}
/**
 * Merge default, model-specific, and mode-specific config into a single object.
 *
 * Only picks scalar defaults (temperature, max_tokens). Model and mode
 * overrides are applied on top.
 */
export function mergeConfig(defaults, modelConfig, overrides) {
    const result = {};
    // Only pick scalar defaults (temperature, max_tokens)
    for (const [key, value] of Object.entries(defaults)) {
        if (typeof value !== "object" || value === null) {
            result[key] = value;
        }
    }
    // Model-specific overrides
    if (modelConfig) {
        Object.assign(result, modelConfig);
    }
    // Mode-specific overrides
    if (overrides) {
        Object.assign(result, overrides);
    }
    return result;
}
/**
 * Check whether a model entry matches a given evaluation mode.
 *
 * Models without a `modes` field match all modes.
 */
export function modelMatchesMode(model, mode) {
    if (!model.modes || model.modes.length === 0) {
        return true;
    }
    return model.modes.includes(mode);
}
/**
 * Resolve which variants a model participates in for a given mode.
 *
 * Resolution rules:
 * - If the mode has no variants defined → returns `undefined` (no variant filtering)
 * - If the model specifies variants for this mode → returns that whitelist
 * - If the model omits variants for this mode → returns ALL mode variants (default)
 *
 * @param model  - The model entry from models config
 * @param modeBase - The mode base (contains variant definitions)
 * @returns Array of variant IDs, or `undefined` if the mode has no variants
 */
export function resolveModelVariants(model, modeBase) {
    const modeVariants = modeBase.mode.variants;
    if (!modeVariants || modeVariants.length === 0)
        return undefined;
    const allVariantIds = modeVariants.map((v) => v.id);
    const modeId = modeBase.mode.id;
    const explicit = model.variants?.[modeId];
    return explicit ?? allVariantIds;
}
