"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.CredentialManager = void 0;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const dotenv = __importStar(require("dotenv"));
const profile_config_1 = require("./profile-config");
class CredentialManager {
    configDir;
    dotenvLoaded = false;
    profileManager;
    homeDir;
    constructor(homeDir) {
        this.homeDir = homeDir || os.homedir();
        this.configDir = path.join(this.homeDir, '.majk-chat');
        this.profileManager = new profile_config_1.ProfileConfigManager(homeDir);
    }
    /**
     * Load environment variables from .env files with precedence:
     * 1. .env.local (highest priority)
     * 2. .env
     * 3. Global .majk-chat/.env
     */
    loadDotenvFiles(customEnvFile) {
        if (this.dotenvLoaded && !customEnvFile) {
            return; // Already loaded, don't reload unless custom file specified
        }
        const envFiles = [];
        if (customEnvFile) {
            // Custom env file has highest priority
            if (path.isAbsolute(customEnvFile)) {
                envFiles.push(customEnvFile);
            }
            else {
                envFiles.push(path.resolve(process.cwd(), customEnvFile));
            }
        }
        // Standard env files in order of precedence
        envFiles.push(path.join(process.cwd(), '.env.local'), path.join(process.cwd(), '.env'), path.join(this.configDir, '.env'));
        // Load each file, later files won't override earlier ones
        for (const envFile of envFiles) {
            if (fs.existsSync(envFile)) {
                try {
                    dotenv.config({ path: envFile, override: false });
                }
                catch (error) {
                    // Ignore errors loading env files
                }
            }
        }
        this.dotenvLoaded = true;
    }
    async loadProviders(options) {
        // Load .env files first
        this.loadDotenvFiles(options?.envFile);
        const providers = {};
        // Check for OpenAI credentials (CLI options, env vars, config, or profile)
        if (options?.openaiApiKey || options?.apiKey ||
            process.env.OPENAI_API_KEY ||
            await this.hasProviderConfigAnywhere('openai', options?.profile)) {
            providers.openai = true;
        }
        // Check for Anthropic credentials
        if (options?.anthropicApiKey || options?.apiKey ||
            process.env.ANTHROPIC_API_KEY ||
            await this.hasProviderConfigAnywhere('anthropic', options?.profile)) {
            providers.anthropic = true;
        }
        // Check for Azure OpenAI credentials
        if (options?.azureOpenaiApiKey || options?.apiKey ||
            process.env.AZURE_OPENAI_API_KEY ||
            await this.hasProviderConfigAnywhere('azure-openai', options?.profile)) {
            providers.azureOpenai = true;
        }
        // Check for AWS/Bedrock credentials (bearer token or traditional credentials)
        if (options?.bedrockApiKey ||
            options?.awsAccessKeyId ||
            process.env.AWS_BEARER_TOKEN_BEDROCK ||
            process.env.AWS_ACCESS_KEY_ID ||
            await this.hasProviderConfigAnywhere('bedrock', options?.profile)) {
            providers.bedrock = true;
        }
        return providers;
    }
    async getCredentials(provider, options) {
        // Load .env files first to ensure they're available
        this.loadDotenvFiles(options?.envFile);
        const credentials = {};
        // PRECEDENCE ORDER (highest to lowest):
        // 1. Command line options (provider-specific or generic)
        // 2. API key file (if specified)
        // 3. Environment variables (after .env loading)
        // 4. Standard provider locations (~/.openai/api_key, etc.)
        // 5. Config file
        // 1. Check provider-specific command line options first
        const cliApiKey = this.getCliApiKey(provider, options);
        if (cliApiKey) {
            credentials.apiKey = cliApiKey;
        }
        // 2. Check generic API key file
        if (!credentials.apiKey && options?.apiKeyFile) {
            credentials.apiKey = await this.readApiKeyFromFile(options.apiKeyFile);
        }
        // 3. Check environment variables (includes .env files)
        if (!credentials.apiKey) {
            credentials.apiKey = this.getEnvironmentKey(provider);
        }
        // 4. Check standard provider locations
        if (!credentials.apiKey) {
            credentials.apiKey = await this.getProviderStandardKey(provider);
        }
        // 5. Check config file
        if (!credentials.apiKey) {
            const config = await this.loadProviderConfig(provider);
            if (config) {
                Object.assign(credentials, config);
            }
        }
        // 6. Check profile-based config (new fallback option)
        // Always check profile to fill in missing fields, not just when no API key exists
        const profileName = options?.profile || 'default';
        const profileConfig = await this.profileManager.getProviderCredentials(provider, profileName);
        if (profileConfig) {
            // Use profile config to fill in missing credentials
            Object.keys(profileConfig).forEach(key => {
                if (!credentials[key]) {
                    credentials[key] = profileConfig[key];
                }
            });
        }
        // Add provider-specific configurations with CLI precedence (but don't override profile values)
        this.addProviderSpecificConfig(provider, credentials, options);
        return credentials;
    }
    /**
     * Get API key from CLI options with provider-specific precedence
     */
    getCliApiKey(provider, options) {
        if (!options)
            return undefined;
        switch (provider) {
            case 'openai':
                return options.openaiApiKey || options.apiKey;
            case 'anthropic':
                return options.anthropicApiKey || options.apiKey;
            case 'azure-openai':
                return options.azureOpenaiApiKey || options.apiKey;
            case 'bedrock':
                // Bedrock doesn't use a simple API key
                return undefined;
            default:
                return options.apiKey;
        }
    }
    /**
     * Add provider-specific configuration with CLI option precedence
     */
    addProviderSpecificConfig(provider, credentials, options) {
        const creds = credentials; // Cast to any to allow provider-specific properties
        switch (provider) {
            case 'openai':
                // Only set if not already set (from profile or other sources)
                if (!creds.organizationId) {
                    creds.organizationId = options?.openaiOrgId || process.env.OPENAI_ORG_ID;
                }
                break;
            case 'azure-openai':
                // Only set if not already set (from profile or other sources)
                if (!creds.endpoint) {
                    creds.endpoint = options?.azureOpenaiEndpoint || process.env.AZURE_OPENAI_ENDPOINT;
                }
                if (!creds.deploymentName) {
                    creds.deploymentName = options?.azureOpenaiDeployment || process.env.AZURE_OPENAI_DEPLOYMENT;
                }
                break;
            case 'bedrock':
                // Only set if not already set (from profile or other sources)
                if (!creds.region) {
                    creds.region = options?.awsRegion || process.env.AWS_REGION || 'us-east-1';
                }
                if (!creds.bearerToken) {
                    creds.bearerToken = options?.bedrockApiKey || process.env.AWS_BEARER_TOKEN_BEDROCK;
                }
                if (!creds.accessKeyId) {
                    creds.accessKeyId = options?.awsAccessKeyId || process.env.AWS_ACCESS_KEY_ID;
                }
                if (!creds.secretAccessKey) {
                    creds.secretAccessKey = options?.awsSecretAccessKey || process.env.AWS_SECRET_ACCESS_KEY;
                }
                if (!creds.sessionToken) {
                    creds.sessionToken = options?.awsSessionToken || process.env.AWS_SESSION_TOKEN;
                }
                break;
        }
    }
    async readApiKeyFromFile(filepath) {
        try {
            const content = await fs.readFile(filepath, 'utf-8');
            return content.trim();
        }
        catch {
            return undefined;
        }
    }
    getEnvironmentKey(provider) {
        switch (provider) {
            case 'openai':
                return process.env.OPENAI_API_KEY;
            case 'anthropic':
                return process.env.ANTHROPIC_API_KEY;
            case 'azure-openai':
                return process.env.AZURE_OPENAI_API_KEY;
            case 'bedrock':
                // Bedrock uses AWS credentials, not an API key
                return undefined;
            default:
                return undefined;
        }
    }
    async getProviderStandardKey(provider) {
        const standardPaths = {
            openai: [
                path.join(this.configDir, 'openai-api-key')
            ],
            anthropic: [
                path.join(this.configDir, 'anthropic-api-key')
            ]
        };
        const paths = standardPaths[provider];
        if (!paths)
            return undefined;
        for (const filepath of paths) {
            try {
                if (await fs.pathExists(filepath)) {
                    const content = await fs.readFile(filepath, 'utf-8');
                    return content.trim();
                }
            }
            catch {
                // Continue to next path
            }
        }
        return undefined;
    }
    async hasProviderConfig(provider) {
        const configPath = path.join(this.configDir, 'providers', `${provider}.json`);
        return fs.pathExists(configPath);
    }
    /**
     * Check if provider has config in any location including profiles
     */
    async hasProviderConfigAnywhere(provider, profileName) {
        // Check traditional config
        if (await this.hasProviderConfig(provider)) {
            return true;
        }
        // Check profile config
        const profile = profileName || 'default';
        const profileConfig = await this.profileManager.getProviderCredentials(provider, profile);
        return profileConfig !== null;
    }
    async loadProviderConfig(provider) {
        try {
            const configPath = path.join(this.configDir, 'providers', `${provider}.json`);
            if (await fs.pathExists(configPath)) {
                return await fs.readJson(configPath);
            }
        }
        catch {
            // Ignore errors
        }
        return null;
    }
    async saveProviderConfig(provider, config) {
        const configPath = path.join(this.configDir, 'providers', `${provider}.json`);
        await fs.ensureDir(path.dirname(configPath));
        await fs.writeJson(configPath, config, { spaces: 2 });
    }
}
exports.CredentialManager = CredentialManager;
//# sourceMappingURL=credentials.js.map