import { scanDeepSeekAccountTokenFiles, scanOAuthTokenFiles } from '../token-file-scanner.js';
import { VirtualRouterError, VirtualRouterErrorCode } from '../types.js';
import { asRecord, normalizeAlias, pushUnique, readOptionalString } from './utils.js';
const MULTI_TOKEN_OAUTH_PROVIDERS = new Set(['iflow', 'qwen', 'gemini-cli', 'antigravity']);
export function extractProviderAuthEntries(providerId, raw) {
    const provider = asRecord(raw);
    const auth = asRecord(provider.auth);
    const entries = [];
    const aliasSet = new Set();
    const baseTypeInfo = interpretAuthType(auth.type);
    const baseType = baseTypeInfo.type;
    const baseTypeSource = typeof auth.type === 'string' ? auth.type : undefined;
    const defaults = collectAuthDefaults(auth);
    const buildAuthCandidate = (typeHint, extras = {}) => {
        const source = typeof typeHint === 'string' && typeHint.trim() ? typeHint.trim() : baseTypeSource;
        const typeInfo = interpretAuthType(source ?? baseType);
        const rawType = typeInfo.raw ?? source ?? baseTypeSource;
        return {
            ...extras,
            type: typeInfo.type,
            rawType,
            oauthProviderId: extras.oauthProviderId ?? typeInfo.oauthProviderId
        };
    };
    const pushEntry = (candidateAlias, authConfig) => {
        const alias = normalizeAlias(candidateAlias, aliasSet);
        const typeSource = authConfig.rawType ?? authConfig.type ?? baseTypeSource ?? baseType;
        const typeInfo = interpretAuthType(typeSource);
        const entryType = typeInfo.type;
        const oauthProviderId = authConfig.oauthProviderId ?? typeInfo.oauthProviderId ?? baseTypeInfo.oauthProviderId;
        if (entryType === 'oauth' && !oauthProviderId) {
            throw new VirtualRouterError(`Provider ${providerId} OAuth auth entries must declare provider-specific type (e.g. "qwen-oauth")`, VirtualRouterErrorCode.CONFIG_ERROR);
        }
        const normalized = {
            type: entryType,
            rawType: typeof typeSource === 'string' ? typeSource : undefined,
            oauthProviderId,
            value: readOptionalString(authConfig.value ?? authConfig.apiKey),
            secretRef: readOptionalString(authConfig.secretRef)
        };
        normalized.tokenFile = readOptionalString(authConfig.tokenFile);
        normalized.tokenUrl = readOptionalString(authConfig.tokenUrl ?? authConfig.token_url);
        normalized.deviceCodeUrl = readOptionalString(authConfig.deviceCodeUrl ?? authConfig.device_code_url);
        normalized.clientId = readOptionalString(authConfig.clientId ?? authConfig.client_id);
        normalized.clientSecret = readOptionalString(authConfig.clientSecret ?? authConfig.client_secret);
        normalized.authorizationUrl = readOptionalString(authConfig.authorizationUrl ??
            authConfig.authorization_url ??
            authConfig.authUrl);
        normalized.userInfoUrl = readOptionalString(authConfig.userInfoUrl ?? authConfig.user_info_url);
        normalized.refreshUrl = readOptionalString(authConfig.refreshUrl ?? authConfig.refresh_url);
        normalized.scopes = normalizeScopeList(authConfig.scopes ?? authConfig.scope);
        normalized.secretRef ??= defaults.secretRef;
        normalized.tokenFile ??= defaults.tokenFile;
        normalized.tokenUrl ??= defaults.tokenUrl;
        normalized.deviceCodeUrl ??= defaults.deviceCodeUrl;
        normalized.clientId ??= defaults.clientId;
        normalized.clientSecret ??= defaults.clientSecret;
        normalized.authorizationUrl ??= defaults.authorizationUrl;
        normalized.userInfoUrl ??= defaults.userInfoUrl;
        normalized.refreshUrl ??= defaults.refreshUrl;
        normalized.scopes = mergeScopes(normalized.scopes, defaults.scopes);
        if (entryType === 'apiKey' && !normalized.secretRef) {
            normalized.secretRef = `${providerId}.${alias}`;
        }
        entries.push({ keyAlias: alias, auth: normalized });
        aliasSet.add(alias);
    };
    const fromRecord = (record) => {
        const data = asRecord(record);
        const alias = readOptionalString(data.alias);
        const typeValue = data.type ?? baseTypeSource ?? baseType;
        pushEntry(alias, buildAuthCandidate(typeValue, {
            value: data.value ?? data.apiKey,
            secretRef: data.secretRef,
            tokenFile: data.tokenFile,
            tokenUrl: data.tokenUrl ?? data.token_url,
            deviceCodeUrl: data.deviceCodeUrl ?? data.device_code_url,
            clientId: data.clientId ?? data.client_id,
            clientSecret: data.clientSecret ?? data.client_secret,
            authorizationUrl: data.authorizationUrl ??
                data.authorization_url ??
                data.authUrl,
            userInfoUrl: data.userInfoUrl ?? data.user_info_url,
            refreshUrl: data.refreshUrl ?? data.refresh_url,
            scopes: data.scopes ?? data.scope
        }));
    };
    if (Array.isArray(auth.entries)) {
        for (const entry of auth.entries) {
            fromRecord(entry);
        }
    }
    if (Array.isArray(auth.keys)) {
        for (const entry of auth.keys) {
            fromRecord(entry);
        }
    }
    else {
        const keysObject = asRecord(auth.keys);
        for (const [alias, entry] of Object.entries(keysObject)) {
            if (entry && typeof entry === 'object') {
                fromRecord({ alias, ...entry });
            }
            else if (typeof entry === 'string') {
                pushEntry(alias, buildAuthCandidate(baseTypeSource, { value: entry }));
            }
        }
    }
    const apiKeyField = provider.apiKey ?? provider.apiKeys ?? auth.apiKey;
    if (Array.isArray(apiKeyField)) {
        for (const item of apiKeyField) {
            if (typeof item === 'string' && item.trim()) {
                pushEntry(undefined, buildAuthCandidate(baseTypeSource, { value: item.trim() }));
            }
            else if (item && typeof item === 'object') {
                fromRecord(item);
            }
        }
    }
    else if (typeof apiKeyField === 'string' && apiKeyField.trim()) {
        pushEntry(undefined, buildAuthCandidate(baseTypeSource, { value: apiKeyField.trim() }));
    }
    const hasExplicitEntries = entries.length > 0;
    if (baseType === 'oauth' && !hasExplicitEntries) {
        const scanCandidates = new Set();
        const pushCandidate = (value) => {
            if (typeof value === 'string' && value.trim()) {
                scanCandidates.add(value.trim().toLowerCase());
            }
        };
        pushCandidate(auth.oauthProviderId);
        pushCandidate(baseTypeInfo.oauthProviderId);
        pushCandidate(providerId);
        for (const candidate of scanCandidates) {
            if (!MULTI_TOKEN_OAUTH_PROVIDERS.has(candidate)) {
                continue;
            }
            const tokenFiles = scanOAuthTokenFiles(candidate);
            if (!tokenFiles.length) {
                continue;
            }
            const baseTypeAlias = baseTypeInfo.oauthProviderId?.toLowerCase();
            for (const match of tokenFiles) {
                const alias = match.alias && match.alias !== 'default'
                    ? `${match.sequence}-${match.alias}`
                    : String(match.sequence);
                const typeHint = baseTypeSource && baseTypeAlias === candidate
                    ? baseTypeSource
                    : `${candidate}-oauth`;
                const authConfig = {
                    ...defaults,
                    type: typeHint,
                    tokenFile: match.filePath,
                    oauthProviderId: candidate
                };
                pushEntry(alias, authConfig);
            }
        }
    }
    const baseRawType = String(baseTypeInfo.raw ?? baseTypeSource ?? '').trim().toLowerCase();
    if (baseType === 'apiKey' && baseRawType === 'deepseek-account' && !hasExplicitEntries) {
        const tokenFiles = scanDeepSeekAccountTokenFiles();
        for (const match of tokenFiles) {
            const authConfig = {
                ...defaults,
                type: baseTypeSource ?? 'deepseek-account',
                tokenFile: match.filePath
            };
            pushEntry(match.alias, authConfig);
        }
    }
    if (!entries.length) {
        const fallbackExtras = {
            value: readOptionalString(auth.value),
            secretRef: readOptionalString(auth.secretRef),
            tokenFile: readOptionalString(auth.tokenFile ?? auth.file),
            tokenUrl: readOptionalString(auth.tokenUrl ?? auth.token_url),
            deviceCodeUrl: readOptionalString(auth.deviceCodeUrl ?? auth.device_code_url),
            clientId: readOptionalString(auth.clientId ?? auth.client_id),
            clientSecret: readOptionalString(auth.clientSecret ?? auth.client_secret),
            authorizationUrl: readOptionalString(auth.authorizationUrl ?? auth.authorization_url ?? auth.authUrl),
            userInfoUrl: readOptionalString(auth.userInfoUrl ?? auth.user_info_url),
            refreshUrl: readOptionalString(auth.refreshUrl ?? auth.refresh_url),
            scopes: normalizeScopeList(auth.scopes ?? auth.scope),
            cookieFile: readOptionalString(auth.cookieFile)
        };
        const fallbackHasData = Boolean(fallbackExtras.value ||
            fallbackExtras.secretRef ||
            fallbackExtras.tokenFile ||
            fallbackExtras.tokenUrl ||
            fallbackExtras.deviceCodeUrl ||
            fallbackExtras.clientId ||
            fallbackExtras.clientSecret ||
            fallbackExtras.cookieFile ||
            (fallbackExtras.scopes &&
                Array.isArray(fallbackExtras.scopes) &&
                fallbackExtras.scopes.length));
        if (fallbackHasData) {
            pushEntry(undefined, buildAuthCandidate(baseTypeSource, fallbackExtras));
        }
    }
    if (!entries.length && baseType === 'apiKey') {
        const authDeclared = Object.prototype.hasOwnProperty.call(provider, 'auth') ||
            Object.prototype.hasOwnProperty.call(provider, 'apiKey') ||
            Object.prototype.hasOwnProperty.call(provider, 'apiKeys') ||
            Object.prototype.hasOwnProperty.call(provider, 'authType');
        if (authDeclared) {
            pushEntry(undefined, buildAuthCandidate(baseTypeSource, { value: '' }));
        }
    }
    if (!entries.length) {
        throw new VirtualRouterError(`Provider ${providerId} is missing auth configuration`, VirtualRouterErrorCode.CONFIG_ERROR);
    }
    return entries;
}
function collectAuthDefaults(auth) {
    return {
        secretRef: readOptionalString(auth.secretRef) ?? readOptionalString(auth.file),
        tokenFile: readOptionalString(auth.tokenFile) ?? readOptionalString(auth.file),
        tokenUrl: readOptionalString(auth.tokenUrl ?? auth.token_url),
        deviceCodeUrl: readOptionalString(auth.deviceCodeUrl ?? auth.device_code_url),
        clientId: readOptionalString(auth.clientId ?? auth.client_id),
        clientSecret: readOptionalString(auth.clientSecret ?? auth.client_secret),
        authorizationUrl: readOptionalString(auth.authorizationUrl ?? auth.authorization_url ?? auth.authUrl),
        userInfoUrl: readOptionalString(auth.userInfoUrl ?? auth.user_info_url),
        refreshUrl: readOptionalString(auth.refreshUrl ?? auth.refresh_url),
        scopes: normalizeScopeList(auth.scopes ?? auth.scope)
    };
}
function normalizeScopeList(value) {
    if (Array.isArray(value)) {
        const normalized = [];
        for (const item of value) {
            const str = readOptionalString(item);
            if (str) {
                pushUnique(normalized, str);
            }
        }
        return normalized.length ? normalized : undefined;
    }
    if (typeof value === 'string' && value.trim()) {
        const normalized = value
            .split(/[,\s]+/)
            .map((item) => item.trim())
            .filter(Boolean);
        return normalized.length ? normalized : undefined;
    }
    return undefined;
}
function mergeScopes(primary, fallback) {
    if ((!primary || !primary.length) && (!fallback || !fallback.length)) {
        return undefined;
    }
    const merged = new Set();
    for (const scope of primary ?? []) {
        if (scope.trim())
            merged.add(scope.trim());
    }
    for (const scope of fallback ?? []) {
        if (scope.trim())
            merged.add(scope.trim());
    }
    return merged.size ? Array.from(merged) : undefined;
}
function interpretAuthType(value) {
    if (typeof value !== 'string') {
        return { type: 'apiKey' };
    }
    const trimmed = value.trim();
    if (!trimmed) {
        return { type: 'apiKey' };
    }
    const lower = trimmed.toLowerCase();
    if (lower === 'apikey' || lower === 'api-key') {
        return { type: 'apiKey', raw: trimmed };
    }
    if (lower === 'oauth') {
        return { type: 'oauth', raw: trimmed };
    }
    const match = lower.match(/^([a-z0-9._-]+)-oauth$/);
    if (match) {
        return { type: 'oauth', oauthProviderId: match[1], raw: trimmed };
    }
    if (lower.includes('oauth')) {
        return { type: 'oauth', raw: trimmed };
    }
    return { type: 'apiKey', raw: trimmed };
}
