import { getNamespacedSecret, removeNamespacedSecret, setNamespacedSecret } from '../connectors/secret-store.js';
const MCP_OAUTH_SECURE_STORE_NAMESPACE = 'shortcut.mcp-oauth';
const MCP_OAUTH_STORAGE_MANIFEST_FIELD = '__oauthStorage';
const MCP_OAUTH_DISCOVERY_FIELD = '__oauthDiscovery';
function hasStoredMcpOAuthPayload(payload) {
    return (payload.credential !== undefined ||
        payload.clientInfo !== undefined ||
        payload.discoveryInfo !== undefined);
}
export class McpOAuthPersistence {
    isManagedProvider;
    settings = {
        credentialsStoreMode: 'file',
        callbackPort: 0,
        callbackUrl: ''
    };
    constructor(isManagedProvider = (provider) => provider.startsWith('mcp:')) {
        this.isManagedProvider = isManagedProvider;
    }
    configure(settings) {
        this.settings = {
            credentialsStoreMode: settings?.credentialsStoreMode ?? 'file',
            callbackPort: settings?.callbackPort ?? 0,
            callbackUrl: settings?.callbackUrl ?? ''
        };
    }
    getSettings() {
        return { ...this.settings };
    }
    shouldUseKeychain(provider) {
        return this.isManagedProvider(provider) && this.resolveStoreMode() === 'keychain';
    }
    load(content) {
        return this.hydrate(this.parse(content));
    }
    loadOrEmpty(content) {
        try {
            return this.load(content);
        }
        catch {
            return this.empty();
        }
    }
    loadProviderPayload(provider) {
        return getNamespacedSecret(MCP_OAUTH_SECURE_STORE_NAMESPACE, provider);
    }
    getProviderPayload(state, provider) {
        return {
            ...(state.credentials[provider] ? { credential: state.credentials[provider] } : {}),
            ...(state.clientInfo[provider] ? { clientInfo: state.clientInfo[provider] } : {}),
            ...(state.discoveryInfo[provider] ? { discoveryInfo: state.discoveryInfo[provider] } : {})
        };
    }
    writeProviderPayload(baseState, provider, nextPayload) {
        const nextCredentials = { ...baseState.credentials };
        const nextClientInfo = { ...baseState.clientInfo };
        const nextDiscoveryInfo = { ...baseState.discoveryInfo };
        const nextManifest = { ...baseState.storageManifest };
        let persistInFile = !this.shouldUseKeychain(provider);
        if (!persistInFile) {
            try {
                delete nextCredentials[provider];
                delete nextClientInfo[provider];
                delete nextDiscoveryInfo[provider];
                if (hasStoredMcpOAuthPayload(nextPayload)) {
                    nextManifest[provider] = 'keychain';
                    setNamespacedSecret(MCP_OAUTH_SECURE_STORE_NAMESPACE, provider, nextPayload);
                }
                else {
                    delete nextManifest[provider];
                    removeNamespacedSecret(MCP_OAUTH_SECURE_STORE_NAMESPACE, provider);
                }
            }
            catch {
                persistInFile = true;
            }
        }
        if (persistInFile) {
            if (baseState.storageManifest[provider] === 'keychain') {
                try {
                    removeNamespacedSecret(MCP_OAUTH_SECURE_STORE_NAMESPACE, provider);
                }
                catch {
                    // Best-effort cleanup only. Falling back to file storage must not fail login.
                }
            }
            delete nextManifest[provider];
            if (nextPayload.credential) {
                nextCredentials[provider] = nextPayload.credential;
            }
            else {
                delete nextCredentials[provider];
            }
            if (nextPayload.clientInfo) {
                nextClientInfo[provider] = nextPayload.clientInfo;
            }
            else {
                delete nextClientInfo[provider];
            }
            if (nextPayload.discoveryInfo) {
                nextDiscoveryInfo[provider] = nextPayload.discoveryInfo;
            }
            else {
                delete nextDiscoveryInfo[provider];
            }
        }
        return JSON.stringify(this.serialize(nextCredentials, nextClientInfo, nextDiscoveryInfo, nextManifest), null, 2);
    }
    resolveStoreMode() {
        const mode = this.settings.credentialsStoreMode;
        if (mode === 'file') {
            return 'file';
        }
        if (mode === 'keychain') {
            return process.platform === 'darwin' || process.platform === 'win32' ? 'keychain' : 'file';
        }
        return process.platform === 'darwin' || process.platform === 'win32' ? 'keychain' : 'file';
    }
    empty() {
        return {
            credentials: {},
            clientInfo: {},
            discoveryInfo: {},
            storageManifest: {}
        };
    }
    hydrate(state) {
        const hydrated = {
            credentials: { ...state.credentials },
            clientInfo: { ...state.clientInfo },
            discoveryInfo: { ...state.discoveryInfo },
            storageManifest: { ...state.storageManifest }
        };
        for (const provider of Object.keys(state.storageManifest)) {
            const payload = this.loadProviderPayload(provider);
            if (payload?.credential) {
                hydrated.credentials[provider] = payload.credential;
            }
            if (payload?.clientInfo) {
                hydrated.clientInfo[provider] = payload.clientInfo;
            }
            if (payload?.discoveryInfo) {
                hydrated.discoveryInfo[provider] = payload.discoveryInfo;
            }
        }
        return hydrated;
    }
    parse(content) {
        if (!content) {
            return this.empty();
        }
        const parsed = JSON.parse(content);
        const credentials = {};
        const clientInfo = {};
        const discoveryInfo = {};
        const storageManifest = {};
        const rawClientInfo = parsed.__oauthClients;
        if (rawClientInfo && typeof rawClientInfo === 'object') {
            for (const [provider, value] of Object.entries(rawClientInfo)) {
                if (value &&
                    typeof value === 'object' &&
                    typeof value.clientId === 'string') {
                    clientInfo[provider] = {
                        clientId: value.clientId,
                        ...(value.clientSecret &&
                            typeof value.clientSecret === 'string'
                            ? { clientSecret: value.clientSecret }
                            : {})
                    };
                }
            }
        }
        const rawStorageManifest = parsed[MCP_OAUTH_STORAGE_MANIFEST_FIELD];
        if (rawStorageManifest && typeof rawStorageManifest === 'object') {
            for (const [provider, value] of Object.entries(rawStorageManifest)) {
                if (value === 'keychain') {
                    storageManifest[provider] = 'keychain';
                }
            }
        }
        const rawDiscoveryInfo = parsed[MCP_OAUTH_DISCOVERY_FIELD];
        if (rawDiscoveryInfo && typeof rawDiscoveryInfo === 'object') {
            for (const [provider, value] of Object.entries(rawDiscoveryInfo)) {
                if (value &&
                    typeof value === 'object' &&
                    typeof value.authorizationServerUrl === 'string') {
                    discoveryInfo[provider] = value;
                }
            }
        }
        for (const [provider, value] of Object.entries(parsed)) {
            if (provider === '__oauthClients' ||
                provider === MCP_OAUTH_STORAGE_MANIFEST_FIELD ||
                provider === MCP_OAUTH_DISCOVERY_FIELD) {
                continue;
            }
            if (value &&
                typeof value === 'object' &&
                value.type === 'oauth' &&
                typeof value.access === 'string' &&
                typeof value.refresh === 'string' &&
                typeof value.expires === 'number') {
                credentials[provider] = value;
            }
        }
        return {
            credentials,
            clientInfo,
            discoveryInfo,
            storageManifest
        };
    }
    serialize(credentials, clientInfo, discoveryInfo, storageManifest) {
        const payload = { ...credentials };
        if (Object.keys(clientInfo).length > 0) {
            payload.__oauthClients = clientInfo;
        }
        if (Object.keys(discoveryInfo).length > 0) {
            payload[MCP_OAUTH_DISCOVERY_FIELD] = discoveryInfo;
        }
        if (Object.keys(storageManifest).length > 0) {
            payload[MCP_OAUTH_STORAGE_MANIFEST_FIELD] = storageManifest;
        }
        return payload;
    }
}
//# sourceMappingURL=mcp-oauth-state.js.map