import { createMcpClient } from '../core/factory.js';
import { ClientError, NetworkError, AuthError, isAuthenticationError, createServerAuthError } from '../lib/errors.js';
import { normalizeServerUrl, isValidSessionName, getServerHost } from '../lib/utils.js';
import { setVerbose, createLogger } from '../lib/logger.js';
import { loadConfig, getServerConfig, validateServerConfig } from '../lib/config.js';
import { OAuthProvider } from '../lib/auth/oauth-provider.js';
import { OAuthTokenManager } from '../lib/auth/oauth-token-manager.js';
import { getAuthProfile, listAuthProfiles } from '../lib/auth/profiles.js';
import { readKeychainOAuthTokenInfo, readKeychainOAuthClientInfo } from '../lib/auth/keychain.js';
import { logTarget } from './output.js';
import packageJson from '../../package.json' with { type: 'json' };
import { DEFAULT_AUTH_PROFILE } from '../lib/auth/oauth-utils.js';
import { parseHeaderFlags } from './parser.js';
const logger = createLogger('cli');
async function createAuthProviderForServer(url, profileName = DEFAULT_AUTH_PROFILE) {
    try {
        const profile = await getAuthProfile(url, profileName);
        if (!profile) {
            logger.debug(`No auth profile found for ${url} (profile: ${profileName})`);
            return undefined;
        }
        const tokens = await readKeychainOAuthTokenInfo(url, profileName);
        if (!tokens?.refreshToken) {
            logger.debug(`No refresh token in keychain for profile: ${profileName}`);
            return undefined;
        }
        const clientInfo = await readKeychainOAuthClientInfo(url, profileName);
        if (!clientInfo?.clientId) {
            logger.warn(`OAuth client ID not found in keychain for profile: ${profileName}`);
            return undefined;
        }
        const tokenManagerOptions = {
            serverUrl: url,
            profileName,
            clientId: clientInfo.clientId,
            refreshToken: tokens.refreshToken,
            accessToken: tokens.accessToken,
        };
        if (tokens.expiresAt !== undefined) {
            tokenManagerOptions.accessTokenExpiresAt = tokens.expiresAt;
        }
        const tokenManager = new OAuthTokenManager(tokenManagerOptions);
        logger.debug(`Created OAuthProvider for profile: ${profileName}`);
        return new OAuthProvider({
            serverUrl: url,
            profileName,
            tokenManager,
            clientId: clientInfo.clientId,
        });
    }
    catch (error) {
        if (error instanceof AuthError) {
            throw error;
        }
        logger.warn(`Failed to create auth provider: ${error.message}`);
        return undefined;
    }
}
export async function resolveAuthProfile(serverUrl, target, specifiedProfile, context) {
    const host = getServerHost(serverUrl);
    if (specifiedProfile) {
        const profile = await getAuthProfile(serverUrl, specifiedProfile);
        if (!profile) {
            throw new ClientError(`Authentication profile "${specifiedProfile}" not found for ${host}.\n\n` +
                `To create this profile, run:\n` +
                `  mcpc ${target} login --profile ${specifiedProfile}`);
        }
        return specifiedProfile;
    }
    const defaultProfile = await getAuthProfile(serverUrl, DEFAULT_AUTH_PROFILE);
    if (defaultProfile) {
        logger.debug(`Using default auth profile for ${host}`);
        return DEFAULT_AUTH_PROFILE;
    }
    const allProfiles = await listAuthProfiles();
    const serverProfiles = allProfiles.filter(p => getServerHost(p.serverUrl) === host);
    if (serverProfiles.length === 0) {
        logger.debug(`No auth profiles for ${host}, attempting unauthenticated connection`);
        return undefined;
    }
    else {
        const profileNames = serverProfiles.map(p => p.name).join(', ');
        const commandHint = context?.sessionName
            ? `mcpc ${target} connect ${context.sessionName} --profile <name>`
            : `mcpc ${target} <command> --profile <name>`;
        throw new ClientError(`No default authentication profile for ${host}.\n\n` +
            `Available profiles: ${profileNames}\n\n` +
            `To use a profile, run:\n` +
            `  ${commandHint}`);
    }
}
export async function resolveTarget(target, options = {}) {
    if (options.verbose) {
        setVerbose(true);
    }
    if (isValidSessionName(target)) {
        throw new ClientError(`Session target should be handled by withMcpClient: ${target}`);
    }
    if (options.config) {
        logger.debug(`Loading config file: ${options.config}`);
        const mcpConfig = loadConfig(options.config);
        const serverConfig = getServerConfig(mcpConfig, target);
        validateServerConfig(serverConfig);
        const cliHeaders = parseHeaderFlags(options.headers);
        const mergedHeaders = { ...serverConfig.headers, ...cliHeaders };
        return {
            ...serverConfig,
            ...(Object.keys(mergedHeaders).length > 0 && { headers: mergedHeaders }),
            ...(options.timeout && { timeout: options.timeout }),
        };
    }
    let url;
    try {
        url = normalizeServerUrl(target);
    }
    catch (error) {
        throw new ClientError(`Failed to resolve target: ${target}\n` +
            `Target must be one of:\n` +
            `  - Named session (@name)\n` +
            `  - Server URL (e.g., mcp.apify.com or https://mcp.apify.com)\n` +
            `  - Entry in JSON config file specified by --config flag\n\n` +
            `Error: ${error.message}`);
    }
    const headers = parseHeaderFlags(options.headers);
    return {
        url,
        ...(Object.keys(headers).length > 0 && { headers }),
        ...(options.timeout && { timeout: options.timeout }),
    };
}
export async function withMcpClient(target, options, callback) {
    if (isValidSessionName(target)) {
        const { withSessionClient } = await import('../lib/session-client.js');
        const { getSession } = await import('../lib/sessions.js');
        logger.debug('Using session:', target);
        const session = await getSession(target);
        const context = {
            sessionName: session?.name,
            profileName: session?.profileName,
            serverConfig: session?.server,
        };
        if (options.outputMode) {
            await logTarget(target, {
                outputMode: options.outputMode,
                hide: options.hideTarget,
            });
        }
        return await withSessionClient(target, (client) => callback(client, context));
    }
    const serverConfig = await resolveTarget(target, options);
    logger.debug('Resolved target:', { target, serverConfig });
    const clientConfig = {
        clientInfo: { name: 'mcpc', version: packageJson.version },
        serverConfig,
        capabilities: {
            roots: { listChanged: true },
            sampling: {},
        },
        autoConnect: true,
    };
    if (options.verbose) {
        clientConfig.verbose = true;
    }
    let profileName;
    if (serverConfig.url) {
        profileName = await resolveAuthProfile(serverConfig.url, target, options.profile);
        const authProvider = await createAuthProviderForServer(serverConfig.url, profileName);
        if (authProvider) {
            clientConfig.authProvider = authProvider;
            logger.debug(`Using auth profile: ${profileName}`);
        }
    }
    let client;
    try {
        client = await createMcpClient(clientConfig);
    }
    catch (error) {
        const errorMessage = error.message || '';
        if (isAuthenticationError(errorMessage)) {
            throw createServerAuthError(target, { originalError: error });
        }
        throw new NetworkError(`Failed to connect to MCP server: ${errorMessage}`, { originalError: error });
    }
    try {
        logger.debug('Connected successfully');
        if (options.outputMode) {
            const serverDetails = await client.getServerDetails();
            await logTarget(target, {
                outputMode: options.outputMode,
                hide: options.hideTarget,
                profileName,
                serverConfig,
                protocolVersion: serverDetails.protocolVersion,
            });
        }
        const context = { serverConfig, profileName };
        const result = await callback(client, context);
        return result;
    }
    catch (error) {
        logger.error('MCP operation failed:', error);
        if (error instanceof NetworkError || error instanceof ClientError || error instanceof AuthError) {
            throw error;
        }
        const errorMessage = error.message || '';
        if (isAuthenticationError(errorMessage)) {
            throw createServerAuthError(target, { originalError: error });
        }
        throw new NetworkError(`Failed to communicate with MCP server: ${error.message}`, { originalError: error });
    }
    finally {
        try {
            logger.debug('Closing connection...');
            await client.close();
            logger.debug('Connection closed');
        }
        catch (error) {
            logger.warn('Error closing connection:', error);
        }
    }
}
//# sourceMappingURL=helpers.js.map