"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.deleteAiProviderEntry = exports.updateAiProviderEntry = exports.createAiProviderEntry = exports.listAiProviders = void 0;
const types_1 = require("./types");
const config_store_1 = require("./config-store");
function maskSecret(value) {
    if (!value)
        return undefined;
    return value.length > 8 ? `...${value.slice(-4)}` : '***';
}
function sanitizeUrlForView(value) {
    const trimmed = value?.trim();
    if (!trimmed)
        return undefined;
    try {
        const parsed = new URL(trimmed);
        if (parsed.username)
            parsed.username = '***';
        if (parsed.password)
            parsed.password = '***';
        return parsed.toString();
    }
    catch {
        return trimmed;
    }
}
function restoreMaskedViewValue(value, existing, sanitizeForView = maskSecret) {
    const next = value?.trim() || undefined;
    if (!next || !existing)
        return next;
    return next === sanitizeForView(existing) ? existing : next;
}
function normalizeHeaders(headers, existing) {
    if (!headers)
        return undefined;
    const normalized = headers.reduce((acc, header) => {
        const key = header.key.trim();
        if (!key)
            return acc;
        acc[key] = restoreMaskedViewValue(header.value, existing?.[key]) || '';
        return acc;
    }, {});
    return Object.keys(normalized).length > 0 ? normalized : undefined;
}
function toHeaderPairs(headers) {
    return Object.entries(headers || {}).map(([key, value]) => ({
        key,
        value: maskSecret(value) || '***',
    }));
}
function readModelRulePart(model, key) {
    if (!model || typeof model !== 'object') {
        return '';
    }
    const value = model[key];
    return typeof value === 'string' ? value.trim() : '';
}
function normalizeModelAliases(models) {
    return (Array.isArray(models) ? models : [])
        .map((model) => ({
        name: readModelRulePart(model, 'name'),
        alias: readModelRulePart(model, 'alias'),
    }))
        .filter((model) => model.name.length > 0 || model.alias.length > 0);
}
function buildApiKeyEntryView(family, entry, index) {
    return {
        id: entry.id || `${family}:${index}`,
        index,
        label: entry.prefix?.trim() || sanitizeUrlForView(entry['base-url']) || `Entry ${index + 1}`,
        baseUrl: sanitizeUrlForView(entry['base-url']),
        proxyUrl: sanitizeUrlForView(entry['proxy-url']),
        prefix: entry.prefix?.trim() || undefined,
        headers: toHeaderPairs(entry.headers),
        excludedModels: [...(entry['excluded-models'] || [])],
        models: normalizeModelAliases(entry.models),
        apiKeyMasked: maskSecret(entry['api-key']),
        secretConfigured: Boolean(entry['api-key']),
    };
}
function buildOpenAiCompatEntryView(entry, index) {
    return {
        id: entry.id || `openai-compatibility:${index}`,
        index,
        name: entry.name,
        label: entry.name,
        baseUrl: sanitizeUrlForView(entry['base-url']),
        headers: toHeaderPairs(entry.headers),
        excludedModels: [],
        models: normalizeModelAliases(entry.models),
        apiKeysMasked: (entry['api-key-entries'] || []).map((apiKeyEntry) => maskSecret(apiKeyEntry['api-key']) || '***'),
        secretConfigured: (entry['api-key-entries'] || []).length > 0,
    };
}
function resolveFamilyStatus(entries) {
    if (entries.length === 0)
        return 'empty';
    return entries.every((entry) => entry.secretConfigured) ? 'ready' : 'partial';
}
async function listAiProviders() {
    const families = await Promise.all(types_1.AI_PROVIDER_FAMILY_IDS.map(async (familyId) => {
        const definition = types_1.AI_PROVIDER_FAMILY_DEFINITIONS[familyId];
        if (familyId === 'openai-compatibility') {
            const entries = await (0, config_store_1.readFamilyEntries)(familyId);
            const entryViews = entries.map((entry, index) => buildOpenAiCompatEntryView(entry, index));
            return {
                ...definition,
                status: resolveFamilyStatus(entryViews),
                entries: entryViews,
            };
        }
        const entries = await (0, config_store_1.readFamilyEntries)(familyId);
        const entryViews = entries.map((entry, index) => buildApiKeyEntryView(familyId, entry, index));
        return {
            ...definition,
            status: resolveFamilyStatus(entryViews),
            entries: entryViews,
        };
    }));
    return {
        source: (0, config_store_1.getAiProvidersSourceSummary)(),
        families,
    };
}
exports.listAiProviders = listAiProviders;
function toApiKeyEntry(input, existing) {
    const nextSecret = input.apiKey !== undefined
        ? input.apiKey.trim()
        : input.preserveSecrets
            ? existing?.['api-key'] || ''
            : existing?.['api-key'] || '';
    return {
        id: existing?.id,
        'api-key': nextSecret,
        'base-url': restoreMaskedViewValue(input.baseUrl, existing?.['base-url'], sanitizeUrlForView),
        'proxy-url': restoreMaskedViewValue(input.proxyUrl, existing?.['proxy-url'], sanitizeUrlForView),
        prefix: input.prefix?.trim() || undefined,
        headers: normalizeHeaders(input.headers, existing?.headers),
        'excluded-models': (input.excludedModels || [])
            .map((value) => value.trim())
            .filter((value) => value.length > 0),
        models: normalizeModelAliases(input.models),
    };
}
function toOpenAiCompatEntry(input, existing) {
    const nextApiKeys = input.apiKeys !== undefined
        ? input.apiKeys.map((value) => value.trim()).filter((value) => value.length > 0)
        : input.preserveSecrets
            ? (existing?.['api-key-entries'] || []).map((entry) => entry['api-key'])
            : (existing?.['api-key-entries'] || []).map((entry) => entry['api-key']);
    return {
        id: existing?.id,
        name: input.name?.trim() || existing?.name || 'connector',
        'base-url': restoreMaskedViewValue(input.baseUrl, existing?.['base-url'], sanitizeUrlForView) ||
            existing?.['base-url'] ||
            '',
        headers: normalizeHeaders(input.headers, existing?.headers),
        'api-key-entries': nextApiKeys.map((apiKey) => ({ 'api-key': apiKey })),
        models: normalizeModelAliases(input.models),
    };
}
function resolveEntryIndex(entries, entryId) {
    const normalizedEntryId = entryId.trim();
    const matchedIndex = entries.findIndex((entry) => entry.id === normalizedEntryId);
    if (matchedIndex !== -1) {
        return matchedIndex;
    }
    const legacyIndex = Number.parseInt(normalizedEntryId, 10);
    if (Number.isInteger(legacyIndex) &&
        legacyIndex >= 0 &&
        legacyIndex < entries.length &&
        String(legacyIndex) === normalizedEntryId) {
        return legacyIndex;
    }
    if (normalizedEntryId.startsWith('openai-compatibility:') || normalizedEntryId.includes(':')) {
        const legacySuffix = normalizedEntryId.split(':').at(-1) || '';
        const legacySuffixIndex = Number.parseInt(legacySuffix, 10);
        if (Number.isInteger(legacySuffixIndex) &&
            legacySuffixIndex >= 0 &&
            legacySuffixIndex < entries.length &&
            String(legacySuffixIndex) === legacySuffix) {
            return legacySuffixIndex;
        }
    }
    throw new Error('Entry not found');
}
function assertEntryId(entryId) {
    if (!entryId.trim()) {
        throw new Error('Entry not found');
    }
}
function validateFamilyInput(family, input) {
    if (family === 'openai-compatibility') {
        if (!(input.name?.trim() || '').length) {
            throw new Error('name is required');
        }
        if (!(input.baseUrl?.trim() || '').length) {
            throw new Error('baseUrl is required');
        }
        if (!input.preserveSecrets && !(input.apiKeys || []).some((value) => value.trim().length > 0)) {
            throw new Error('At least one api key is required');
        }
        return;
    }
    if (!input.preserveSecrets && !(input.apiKey?.trim() || '').length) {
        throw new Error('apiKey is required');
    }
}
async function createAiProviderEntry(family, input) {
    validateFamilyInput(family, input);
    if (family === 'openai-compatibility') {
        const entries = await (0, config_store_1.readFamilyEntries)(family);
        entries.push(toOpenAiCompatEntry(input));
        await (0, config_store_1.writeFamilyEntries)(family, entries);
        return;
    }
    const entries = await (0, config_store_1.readFamilyEntries)(family);
    entries.push(toApiKeyEntry(input));
    await (0, config_store_1.writeFamilyEntries)(family, entries);
}
exports.createAiProviderEntry = createAiProviderEntry;
async function updateAiProviderEntry(family, entryId, input) {
    assertEntryId(entryId);
    if (family === 'openai-compatibility') {
        const entries = await (0, config_store_1.readFamilyEntries)(family);
        const index = resolveEntryIndex(entries, entryId);
        validateFamilyInput(family, input);
        entries[index] = toOpenAiCompatEntry(input, entries[index]);
        await (0, config_store_1.writeFamilyEntries)(family, entries);
        return;
    }
    const entries = await (0, config_store_1.readFamilyEntries)(family);
    const index = resolveEntryIndex(entries, entryId);
    validateFamilyInput(family, input);
    entries[index] = toApiKeyEntry(input, entries[index]);
    await (0, config_store_1.writeFamilyEntries)(family, entries);
}
exports.updateAiProviderEntry = updateAiProviderEntry;
async function deleteAiProviderEntry(family, entryId) {
    assertEntryId(entryId);
    if (family === 'openai-compatibility') {
        const entries = await (0, config_store_1.readFamilyEntries)(family);
        const index = resolveEntryIndex(entries, entryId);
        entries.splice(index, 1);
        await (0, config_store_1.writeFamilyEntries)(family, entries);
        return;
    }
    const entries = await (0, config_store_1.readFamilyEntries)(family);
    const index = resolveEntryIndex(entries, entryId);
    entries.splice(index, 1);
    await (0, config_store_1.writeFamilyEntries)(family, entries);
}
exports.deleteAiProviderEntry = deleteAiProviderEntry;
//# sourceMappingURL=service.js.map