/** Persists MCP OAuth state so callback handling can resume the original runtime request. */
import { join } from 'node:path';
import { getAgentDir } from '../../config.js';
import { FileAuthStorageBackend, InMemoryAuthStorageBackend, ReadOnlyFileAuthStorageBackend } from '../auth/auth-storage.js';
import { McpOAuthPersistence } from '../auth/mcp-oauth-state.js';
import { isManagedMcpOAuthProvider } from './managed-oauth-providers.js';
/**
 * MCP-owned OAuth protocol state.
 *
 * Owns hosted-MCP credentials, client registration, and discovery metadata
 * using the shared auth backend plus optional keychain storage.
 */
export class McpOAuthStore {
    storage;
    persistence;
    credentials = {};
    clientInfo = {};
    discoveryInfo = {};
    constructor(storage, options) {
        this.storage = storage;
        this.isManagedProvider = options?.isManagedMcpOAuthProvider ?? isManagedMcpOAuthProvider;
        this.persistence = new McpOAuthPersistence(this.isManagedProvider);
        this.persistence.configure(options?.settings);
        this.reload();
    }
    isManagedProvider;
    static create(options) {
        return new McpOAuthStore(new FileAuthStorageBackend(options?.authPath ?? join(getAgentDir(), 'auth.json')), options);
    }
    static createReadOnly(options) {
        return new McpOAuthStore(new ReadOnlyFileAuthStorageBackend(options?.authPath ?? join(getAgentDir(), 'auth.json')), options);
    }
    static fromStorage(storage, options) {
        return new McpOAuthStore(storage, options);
    }
    static inMemory(options) {
        return new McpOAuthStore(new InMemoryAuthStorageBackend(), options);
    }
    reload() {
        let content;
        try {
            this.storage.withLock((current) => {
                content = current;
                return { result: undefined };
            });
            this.applyState(this.persistence.load(content));
        }
        catch {
            this.applyState(this.persistence.loadOrEmpty(undefined));
        }
    }
    getSettings() {
        return this.persistence.getSettings();
    }
    setSettings(settings) {
        this.persistence.configure(settings);
    }
    owns(provider) {
        return this.isManagedProvider(provider);
    }
    listProviders() {
        return Object.keys(this.credentials);
    }
    has(provider) {
        return this.get(provider) !== undefined;
    }
    get(provider) {
        return this.credentials[provider];
    }
    set(provider, credential) {
        this.credentials[provider] = credential;
        this.persistProviderPayload(provider, (payload) => ({
            ...payload,
            credential
        }));
    }
    remove(provider) {
        delete this.credentials[provider];
        this.persistProviderPayload(provider, (payload) => {
            delete payload.credential;
            return payload;
        });
    }
    async mutateCredentialWithLock(provider, mutate) {
        return this.storage.withLockAsync(async (current) => {
            const currentState = this.persistence.loadOrEmpty(current);
            const { result, credential } = await mutate(currentState.credentials[provider]);
            const nextPayload = this.persistence.getProviderPayload(currentState, provider);
            if (credential) {
                nextPayload.credential = credential;
            }
            else {
                delete nextPayload.credential;
            }
            const next = this.persistence.writeProviderPayload(currentState, provider, {
                ...nextPayload
            });
            this.applyState(this.persistence.loadOrEmpty(next));
            return {
                result,
                next
            };
        });
    }
    getClientInfo(provider) {
        return this.clientInfo[provider];
    }
    setClientInfo(provider, info) {
        this.clientInfo[provider] = info;
        this.persistProviderPayload(provider, (payload) => ({
            ...payload,
            clientInfo: info
        }));
    }
    removeClientInfo(provider) {
        delete this.clientInfo[provider];
        this.persistProviderPayload(provider, (payload) => {
            delete payload.clientInfo;
            return payload;
        });
    }
    getDiscoveryState(provider) {
        return this.discoveryInfo[provider];
    }
    setDiscoveryState(provider, info) {
        this.discoveryInfo[provider] = info;
        this.persistProviderPayload(provider, (payload) => ({
            ...payload,
            discoveryInfo: info
        }));
    }
    removeDiscoveryState(provider) {
        delete this.discoveryInfo[provider];
        this.persistProviderPayload(provider, (payload) => {
            delete payload.discoveryInfo;
            return payload;
        });
    }
    clear(provider) {
        delete this.credentials[provider];
        delete this.clientInfo[provider];
        delete this.discoveryInfo[provider];
        this.persistProviderPayload(provider, (payload) => {
            delete payload.credential;
            delete payload.clientInfo;
            delete payload.discoveryInfo;
            return payload;
        });
    }
    applyState(state) {
        this.credentials = { ...state.credentials };
        this.clientInfo = { ...state.clientInfo };
        this.discoveryInfo = { ...state.discoveryInfo };
    }
    persistProviderPayload(provider, update) {
        this.storage.withLock((current) => {
            const currentState = this.persistence.loadOrEmpty(current);
            const nextPayload = update(this.persistence.getProviderPayload(currentState, provider));
            const next = this.persistence.writeProviderPayload(currentState, provider, nextPayload);
            this.applyState(this.persistence.loadOrEmpty(next));
            return {
                result: undefined,
                next
            };
        });
    }
}
//# sourceMappingURL=oauth-state-store.js.map