import { builtinProviders } from "@earendil-works/pi-ai/providers/all";
const GLOBAL_LEGACY_SOURCE = "atomic:legacy-global";
const legacyProviders = new Map();
const CALLBACK_SERVER_PROVIDERS = new Set(["anthropic", "openai-codex"]);
function abortable(promise, signal, onAbort) {
    if (!signal)
        return promise;
    if (signal.aborted) {
        onAbort?.();
        return Promise.reject(signal.reason ?? new Error("Authentication prompt cancelled"));
    }
    return new Promise((resolve, reject) => {
        const abort = () => {
            onAbort?.();
            reject(signal.reason ?? new Error("Authentication prompt cancelled"));
        };
        signal.addEventListener("abort", abort, { once: true });
        promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
    });
}
function builtinOAuth(providerId) {
    return builtinProviders().find((provider) => provider.id === providerId)?.auth.oauth;
}
function toInteraction(callbacks) {
    return {
        signal: callbacks.signal,
        prompt: async (prompt) => {
            switch (prompt.type) {
                case "select":
                    return ((await callbacks.onSelect({
                        message: prompt.message,
                        options: prompt.options.map(({ id, label }) => ({ id, label })),
                    })) ?? "");
                case "manual_code":
                    return abortable(callbacks.onManualCodeInput
                        ? callbacks.onManualCodeInput()
                        : callbacks.onPrompt({ message: prompt.message, placeholder: prompt.placeholder }), prompt.signal, callbacks.onManualCodeCancel);
                default:
                    return callbacks.onPrompt({ message: prompt.message, placeholder: prompt.placeholder });
            }
        },
        notify: (event) => {
            switch (event.type) {
                case "auth_url":
                    callbacks.onAuth({ url: event.url, instructions: event.instructions });
                    break;
                case "device_code":
                    callbacks.onDeviceCode(event);
                    break;
                case "progress":
                case "info":
                    callbacks.onProgress?.(event.message);
                    break;
            }
        },
    };
}
function latestLegacyProvider(providerId) {
    const providers = legacyProviders.get(providerId);
    return providers ? [...providers.values()].at(-1) : undefined;
}
export function registerLegacyOAuthProvider(providerId, provider, sourceId = GLOBAL_LEGACY_SOURCE) {
    const providers = legacyProviders.get(providerId) ?? new Map();
    providers.delete(sourceId);
    providers.set(sourceId, provider);
    legacyProviders.set(providerId, providers);
}
/** Legacy registry aliases retained for Atomic's extension/test compatibility. */
export function registerOAuthProvider(provider) {
    registerLegacyOAuthProvider(provider.id, provider);
}
export function getOAuthProvider(providerId) {
    const provider = latestLegacyProvider(providerId);
    return provider ? { id: providerId, ...provider } : getOAuthProviderDescriptors().find((entry) => entry.id === providerId);
}
export function resetLegacyOAuthProviders() {
    legacyProviders.clear();
}
export function unregisterLegacyOAuthProviders(sourceId) {
    for (const [providerId, providers] of legacyProviders) {
        providers.delete(sourceId);
        if (providers.size === 0)
            legacyProviders.delete(providerId);
    }
}
export function getLegacyOAuthProvider(providerId) {
    return latestLegacyProvider(providerId);
}
export function getOAuthProviders() {
    return getOAuthProviderDescriptors();
}
export const resetOAuthProviders = resetLegacyOAuthProviders;
export function getOAuthProviderDescriptors() {
    const descriptors = new Map();
    for (const provider of builtinProviders()) {
        const oauth = provider.auth.oauth;
        if (!oauth)
            continue;
        descriptors.set(provider.id, {
            id: provider.id,
            name: oauth.name,
            loginLabel: oauth.loginLabel,
            usesCallbackServer: CALLBACK_SERVER_PROVIDERS.has(provider.id),
            login: async (callbacks) => oauth.login(toInteraction(callbacks)),
            refreshToken: async (credentials) => oauth.refresh({ ...credentials, type: "oauth" }),
            getApiKey: (credentials) => credentials.access,
        });
    }
    for (const [id, providers] of legacyProviders) {
        const provider = [...providers.values()].at(-1);
        if (provider)
            descriptors.set(id, { id, ...provider });
    }
    return [...descriptors.values()];
}
export async function loginOAuthProvider(providerId, callbacks) {
    const legacy = latestLegacyProvider(providerId);
    if (legacy)
        return { type: "oauth", ...(await legacy.login(callbacks)) };
    const oauth = builtinOAuth(providerId);
    if (!oauth)
        throw new Error(`Unknown OAuth provider: ${providerId}`);
    return oauth.login(toInteraction(callbacks));
}
export async function refreshOAuthProvider(providerId, credential) {
    const legacy = latestLegacyProvider(providerId);
    if (legacy) {
        const { type: _type, ...credentials } = credential;
        const refreshed = { type: "oauth", ...(await legacy.refreshToken(credentials)) };
        return { credential: refreshed, auth: { apiKey: legacy.getApiKey(refreshed) } };
    }
    const oauth = builtinOAuth(providerId);
    if (!oauth)
        return undefined;
    const refreshed = await oauth.refresh(credential);
    return { credential: refreshed, auth: await oauth.toAuth(refreshed) };
}
export async function oauthCredentialToAuth(providerId, credential) {
    const legacy = latestLegacyProvider(providerId);
    if (legacy)
        return { apiKey: legacy.getApiKey(credential) };
    return builtinOAuth(providerId)?.toAuth(credential);
}
export async function getOAuthApiKey(providerId, credentials) {
    if (!getOAuthProvider(providerId))
        throw new Error(`Unknown OAuth provider: ${providerId}`);
    const selected = credentials[providerId];
    if (!selected)
        return null;
    const credential = { type: "oauth", ...selected };
    let resolved;
    if (Date.now() >= credential.expires) {
        try {
            resolved = await refreshOAuthProvider(providerId, credential);
        }
        catch {
            throw new Error(`Failed to refresh OAuth token for ${providerId}`);
        }
    }
    else {
        resolved = { credential, auth: await oauthCredentialToAuth(providerId, credential) };
    }
    const apiKey = resolved?.auth?.apiKey;
    if (!resolved || !apiKey)
        return null;
    const { type: _type, ...newCredentials } = resolved.credential;
    return { newCredentials, apiKey };
}
//# sourceMappingURL=oauth-provider-bridge.js.map