/**
 * Model catalog orchestration: live sync (with curated normalization), curated
 * fallback, and resolution of a configured-but-missing model to an `unavailable`
 * entry so the UI can warn without silently dropping the admin's choice.
 */
import { AI_CONFIG_COLLECTION, getAIConfig, putAIConfig, resolveProviderKey, } from './config-store.js';
import { curatedFallbackCatalog, normalizeModel } from './catalog.js';
import { listModels as adapterListModels } from './providers/index.js';
export { AI_CONFIG_COLLECTION };
function adapterContextFor(conn, apiKey) {
    return {
        apiKey,
        baseUrl: conn.baseUrl,
        organizationId: conn.organizationId,
        projectId: conn.projectId,
    };
}
/**
 * Sync the model catalog for a provider connection. Attempts a live listing;
 * on any failure falls back to the curated catalog so the selector is never
 * empty. Persists the result on the `__ai_config__` document under
 * `catalog[connectionId]` and stamps `lastModelSyncAt`.
 */
export async function syncModels(db, connectionId) {
    const config = await getAIConfig(db);
    const conn = config.providers.find((p) => p.id === connectionId);
    if (!conn)
        throw new Error('Provider connection not found');
    const now = new Date().toISOString();
    const apiKey = await resolveProviderKey(conn);
    let models;
    let fromFallback = false;
    let warning;
    if (!apiKey) {
        models = curatedFallbackCatalog(conn.provider, now);
        fromFallback = true;
        warning = 'No API key configured; showing the curated model list.';
    }
    else {
        try {
            const ids = await adapterListModels(conn.provider, adapterContextFor(conn, apiKey));
            if (ids.length === 0) {
                models = curatedFallbackCatalog(conn.provider, now);
                fromFallback = true;
                warning = 'The provider returned no models; showing the curated list.';
            }
            else {
                models = ids.map((providerModelId) => normalizeModel({
                    provider: conn.provider,
                    providerModelId,
                    source: 'provider',
                    status: 'available',
                    lastSyncedAt: now,
                }));
            }
        }
        catch (err) {
            models = curatedFallbackCatalog(conn.provider, now);
            fromFallback = true;
            warning =
                err instanceof Error
                    ? `Live model listing failed (${err.message}); showing the curated list.`
                    : 'Live model listing failed; showing the curated list.';
        }
    }
    const entry = { models, lastSyncedAt: now, fromFallback };
    const nextProviders = config.providers.map((p) => p.id === connectionId ? { ...p, lastModelSyncAt: now } : p);
    await putAIConfig(db, {
        ...config,
        providers: nextProviders,
        catalog: { ...(config.catalog ?? {}), [connectionId]: entry },
    }, conn.updatedBy || 'system');
    return { models, fromFallback, warning };
}
/**
 * Read the cached catalog for a connection (or all connections). Falls back to
 * the curated catalog for connections that have never been synced so the UI
 * always has something to show.
 */
export async function getModels(db, params) {
    const config = await getAIConfig(db);
    const now = new Date().toISOString();
    const out = [];
    const conns = config.providers.filter((p) => {
        if (params?.connectionId)
            return p.id === params.connectionId;
        if (params?.provider)
            return p.provider === params.provider;
        return true;
    });
    for (const conn of conns) {
        const cached = config.catalog?.[conn.id];
        if (cached?.models?.length)
            out.push(...cached.models);
        else
            out.push(...curatedFallbackCatalog(conn.provider, now));
    }
    // De-dupe by normalized id, preferring the first (cached/live) occurrence.
    const seen = new Set();
    return out.filter((m) => (seen.has(m.id) ? false : (seen.add(m.id), true)));
}
/**
 * Resolve a configured model id to a catalog entry. When the id is no longer in
 * the catalog (provider removed/renamed it) returns a synthesized `unavailable`
 * model so the caller can keep the setting and warn the user.
 */
export function resolveModelOrUnavailable(modelId, catalog) {
    if (!modelId)
        return null;
    const found = catalog.find((m) => m.id === modelId || m.providerModelId === modelId);
    if (found)
        return found;
    const parts = modelId.split(':');
    const provider = parts[0] ?? '';
    const providerModelId = parts.slice(1).join(':') || modelId;
    return normalizeModel({
        provider: (['openai', 'anthropic', 'xai', 'custom'].includes(provider)
            ? provider
            : 'custom'),
        providerModelId,
        source: 'config',
        status: 'unavailable',
        lastSyncedAt: new Date().toISOString(),
    });
}
//# sourceMappingURL=models.js.map