"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 (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getModelDisplayName = exports.getSettingsPath = exports.loadConfigSafe = exports.readConfig = exports.loadSettings = exports.loadConfig = exports.getActiveConfigPath = exports.getConfigPath = exports.getCcsHooksDir = exports.detectCloudSyncPath = exports.getCcsPathDisplay = exports.getCcsDirDisplay = exports.getCcsDirSource = exports.getCcsDir = exports.getCcsHome = exports.setGlobalConfigDir = exports.runWithScopedConfigDir = exports.runWithScopedCcsHome = exports.runWithScopedConfig = void 0;
const async_hooks_1 = require("async_hooks");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const types_1 = require("../types");
const helpers_1 = require("./helpers");
const ui_1 = require("./ui");
const unified_config_loader_1 = require("../config/unified-config-loader");
// TODO: Replace with proper imports after converting these files
// const { ErrorManager } = require('./error-manager');
// const RecoveryManager = require('./recovery-manager');
// Module-level state for --config-dir CLI flag override
let _globalConfigDir;
const configScopeStorage = new async_hooks_1.AsyncLocalStorage();
function normalizeScopedPath(dir) {
    return dir ? path.resolve(dir) : undefined;
}
async function runWithScopedConfig(scope, fn) {
    return await configScopeStorage.run({
        ccsHome: normalizeScopedPath(scope.ccsHome),
        ccsDir: normalizeScopedPath(scope.ccsDir),
    }, fn);
}
exports.runWithScopedConfig = runWithScopedConfig;
async function runWithScopedCcsHome(ccsHome, fn) {
    return await runWithScopedConfig({ ccsHome }, fn);
}
exports.runWithScopedCcsHome = runWithScopedCcsHome;
async function runWithScopedConfigDir(ccsDir, fn) {
    return await runWithScopedConfig({ ccsDir }, fn);
}
exports.runWithScopedConfigDir = runWithScopedConfigDir;
/**
 * Set global config directory from --config-dir CLI flag.
 * Must be called before any config loading.
 * Pass undefined to clear (useful for tests).
 */
function setGlobalConfigDir(dir) {
    _globalConfigDir = dir ? path.resolve(dir) : undefined;
}
exports.setGlobalConfigDir = setGlobalConfigDir;
/**
 * Get the CCS home directory (respects CCS_HOME env var for test isolation)
 * @returns Home directory path
 */
function getCcsHome() {
    const scopedHome = configScopeStorage.getStore()?.ccsHome;
    if (scopedHome) {
        return scopedHome;
    }
    return process.env.CCS_HOME || os.homedir();
}
exports.getCcsHome = getCcsHome;
/**
 * Resolve the CCS directory with source information.
 * Single source of truth for precedence logic.
 */
function _resolveCcsDir() {
    const scopedConfig = configScopeStorage.getStore();
    if (scopedConfig?.ccsDir)
        return { source: 'scoped:CCS_DIR', dir: scopedConfig.ccsDir };
    if (scopedConfig?.ccsHome)
        return { source: 'scoped:CCS_HOME', dir: path.join(scopedConfig.ccsHome, '.ccs') };
    if (_globalConfigDir)
        return { source: '--config-dir', dir: _globalConfigDir };
    if (process.env.CCS_DIR)
        return { source: 'CCS_DIR', dir: path.resolve(process.env.CCS_DIR) };
    if (process.env.CCS_HOME)
        return { source: 'CCS_HOME', dir: path.join(path.resolve(process.env.CCS_HOME), '.ccs') };
    return { source: 'default', dir: path.join(os.homedir(), '.ccs') };
}
/**
 * Get the CCS directory path.
 * Precedence: --config-dir flag > CCS_DIR env > CCS_HOME env (legacy, appends .ccs) > ~/.ccs default
 * @returns Path to CCS config directory
 */
function getCcsDir() {
    return _resolveCcsDir().dir;
}
exports.getCcsDir = getCcsDir;
/**
 * Get which source determined the CCS directory (for diagnostics).
 * @returns [source_label, resolved_path] tuple
 */
function getCcsDirSource() {
    const r = _resolveCcsDir();
    return [r.source, r.dir];
}
exports.getCcsDirSource = getCcsDirSource;
/**
 * Get the CCS directory as a user-facing display path.
 * Keeps the default path concise while preserving explicit overrides.
 */
function getCcsDirDisplay() {
    return getCcsPathDisplay();
}
exports.getCcsDirDisplay = getCcsDirDisplay;
/**
 * Get a CCS path as a user-facing display path.
 * Keeps the default location concise while preserving explicit overrides.
 */
function getCcsPathDisplay(...segments) {
    const [source, dir] = getCcsDirSource();
    if (source === 'default') {
        return process.platform === 'win32'
            ? path.win32.join('%USERPROFILE%\\.ccs', ...segments)
            : path.posix.join('~/.ccs', ...segments.map((segment) => segment.replace(/\\/g, '/')));
    }
    return path.join(dir, ...segments);
}
exports.getCcsPathDisplay = getCcsPathDisplay;
/**
 * Cloud sync folder patterns for security warning.
 */
const CLOUD_SYNC_PATTERNS = [
    'Dropbox',
    'OneDrive',
    'Google Drive',
    'iCloud Drive',
    'iCloudDrive',
    'pCloud',
    'MEGA',
    'Box Sync',
];
/**
 * Check if a path is under a cloud-synced folder.
 * @returns Detected service name or null
 */
function detectCloudSyncPath(dir) {
    const normalized = dir.replace(/\\/g, '/').toLowerCase();
    const segments = normalized.split('/');
    for (const pattern of CLOUD_SYNC_PATTERNS) {
        const patternLower = pattern.toLowerCase();
        // Exact path segment match to avoid false positives (e.g., "megauser" != "MEGA")
        if (segments.some((s) => s === patternLower))
            return pattern;
    }
    return null;
}
exports.detectCloudSyncPath = detectCloudSyncPath;
/**
 * Get CCS hooks directory (respects CCS_HOME for test isolation)
 * @returns Path to hooks directory
 */
function getCcsHooksDir() {
    return path.join(getCcsDir(), 'hooks');
}
exports.getCcsHooksDir = getCcsHooksDir;
/**
 * Get config file path (legacy JSON path)
 * @deprecated Use getActiveConfigPath() for mode-aware config path
 */
function getConfigPath() {
    return process.env.CCS_CONFIG || path.join(getCcsDir(), 'config.json');
}
exports.getConfigPath = getConfigPath;
/**
 * Get the active config file path based on current mode.
 * Returns config.yaml in unified mode, config.json in legacy mode.
 * @returns Path to the active config file
 */
function getActiveConfigPath() {
    const ccsDir = getCcsDir();
    if ((0, unified_config_loader_1.isUnifiedMode)()) {
        return path.join(ccsDir, 'config.yaml');
    }
    return path.join(ccsDir, 'config.json');
}
exports.getActiveConfigPath = getActiveConfigPath;
/**
 * Load and validate config.json
 */
function loadConfig() {
    const configPath = getConfigPath();
    if (!fs.existsSync(configPath)) {
        // TODO: Add recovery manager logic
        // const recovery = new RecoveryManager();
        // recovery.ensureConfigJson();
        (0, helpers_1.error)(`Config not found: ${configPath}`);
    }
    const raw = fs.readFileSync(configPath, 'utf8');
    const parsed = JSON.parse(raw);
    if (!(0, types_1.isConfig)(parsed)) {
        (0, helpers_1.error)(`Invalid config format: ${configPath}`);
    }
    return parsed;
}
exports.loadConfig = loadConfig;
/**
 * Load and validate settings.json
 */
function loadSettings(settingsPath) {
    if (!fs.existsSync(settingsPath)) {
        (0, helpers_1.error)(`Settings not found: ${settingsPath}`);
    }
    const raw = fs.readFileSync(settingsPath, 'utf8');
    const parsed = JSON.parse(raw);
    if (!(0, types_1.isSettings)(parsed)) {
        (0, helpers_1.error)(`Invalid settings format: ${settingsPath}`);
    }
    return parsed;
}
exports.loadSettings = loadSettings;
/**
 * Read and parse config (legacy compatibility)
 */
function readConfig() {
    return loadConfig();
}
exports.readConfig = readConfig;
/**
 * Load config safely with unified mode support.
 * Returns Config with profiles from unified config.yaml or legacy config.json.
 * Throws on error (catchable) instead of process.exit.
 * Use this in web server routes where try/catch is needed.
 */
function loadConfigSafe() {
    // Unified mode: extract profiles from config.yaml
    if ((0, unified_config_loader_1.isUnifiedMode)()) {
        const unifiedConfig = (0, unified_config_loader_1.loadOrCreateUnifiedConfig)();
        // Convert unified profiles to legacy format for compatibility
        const profiles = {};
        for (const [name, profile] of Object.entries(unifiedConfig.profiles)) {
            if (profile.settings) {
                profiles[name] = profile.settings;
            }
        }
        // Convert unified cliproxy variants to legacy format
        let cliproxy;
        if (unifiedConfig.cliproxy?.variants) {
            cliproxy = {};
            for (const [name, variant] of Object.entries(unifiedConfig.cliproxy.variants)) {
                if ('type' in variant && variant.type === 'composite') {
                    // Composite variants: use default tier's provider
                    cliproxy[name] = {
                        provider: variant.tiers[variant.default_tier].provider,
                        settings: variant.settings,
                        port: variant.port,
                    };
                }
                else {
                    const single = variant;
                    cliproxy[name] = {
                        provider: single.provider,
                        settings: single.settings,
                        account: single.account,
                        port: single.port,
                    };
                }
            }
        }
        return {
            profiles,
            cliproxy,
        };
    }
    // Legacy mode: read config.json
    const configPath = getConfigPath();
    if (!fs.existsSync(configPath)) {
        // Return empty config for graceful degradation (matches unified mode behavior)
        return { profiles: {} };
    }
    const raw = fs.readFileSync(configPath, 'utf8');
    let parsed;
    try {
        parsed = JSON.parse(raw);
    }
    catch (e) {
        throw new Error(`Malformed JSON in config: ${configPath} - ${e.message}`);
    }
    if (!(0, types_1.isConfig)(parsed)) {
        throw new Error(`Invalid config format: ${configPath}`);
    }
    return parsed;
}
exports.loadConfigSafe = loadConfigSafe;
/**
 * Get settings path for profile.
 * In unified mode (config.yaml exists), reads from config.yaml first,
 * then falls back to config.json for backward compatibility.
 */
function getSettingsPath(profile) {
    let settingsPath;
    let availableProfiles = [];
    // Check unified config first (config.yaml)
    if ((0, unified_config_loader_1.isUnifiedMode)()) {
        const unifiedConfig = (0, unified_config_loader_1.loadOrCreateUnifiedConfig)();
        // Check if profile exists in unified config
        const profileConfig = unifiedConfig.profiles[profile];
        if (profileConfig?.settings) {
            settingsPath = profileConfig.settings;
        }
        // Collect available profiles from unified config
        availableProfiles = Object.keys(unifiedConfig.profiles);
        // If not found in unified config, try legacy config.json as fallback
        if (!settingsPath) {
            // Use inline safe logic - loadConfig() calls process.exit() which cannot be caught
            const configPath = getConfigPath();
            if (fs.existsSync(configPath)) {
                try {
                    const raw = fs.readFileSync(configPath, 'utf8');
                    const legacyConfig = JSON.parse(raw);
                    if ((0, types_1.isConfig)(legacyConfig) && legacyConfig.profiles[profile]) {
                        settingsPath = legacyConfig.profiles[profile];
                        // Merge legacy profiles into available list (avoid duplicates)
                        for (const p of Object.keys(legacyConfig.profiles)) {
                            if (!availableProfiles.includes(p)) {
                                availableProfiles.push(p);
                            }
                        }
                    }
                }
                catch {
                    // Legacy config is invalid JSON - that's OK in unified mode
                }
            }
        }
    }
    else {
        // Legacy mode - read from config.json only
        const config = readConfig();
        settingsPath = config.profiles[profile];
        availableProfiles = Object.keys(config.profiles);
    }
    if (!settingsPath) {
        const profileList = availableProfiles.map((p) => `  - ${p}`);
        (0, helpers_1.error)(`Profile '${profile}' not found. Available profiles:\n${profileList.join('\n')}`);
    }
    // Expand path
    const expandedPath = (0, helpers_1.expandPath)(settingsPath);
    // Validate settings file exists
    if (!fs.existsSync(expandedPath)) {
        // Auto-create if it's ~/.claude/settings.json
        if (expandedPath.includes('.claude') && expandedPath.endsWith('settings.json')) {
            // TODO: Add recovery manager logic
            // const recovery = new RecoveryManager();
            // recovery.ensureClaudeSettings();
            console.log((0, ui_1.info)('Auto-created missing settings file'));
        }
        else {
            (0, helpers_1.error)(`Settings file not found: ${expandedPath}`);
        }
    }
    // Validate settings file is valid JSON
    try {
        const settingsContent = fs.readFileSync(expandedPath, 'utf8');
        JSON.parse(settingsContent);
    }
    catch (e) {
        if (e instanceof Error) {
            (0, helpers_1.error)(`Invalid JSON in settings file: ${expandedPath} - ${e.message}`);
        }
        else {
            (0, helpers_1.error)(`Invalid JSON in settings file: ${expandedPath}`);
        }
    }
    return expandedPath;
}
exports.getSettingsPath = getSettingsPath;
/**
 * Get display name for a profile by reading ANTHROPIC_MODEL from settings
 * @param profile - Profile name (glm, km, glmt compatibility, custom, etc.)
 * @returns Formatted display name (e.g., 'GLM-4.7', 'Kimi', 'Custom-Model')
 */
function getModelDisplayName(profile) {
    if (!profile) {
        return '';
    }
    const settingsPath = path.join(getCcsDir(), `${profile}.settings.json`);
    try {
        if (fs.existsSync(settingsPath)) {
            const content = fs.readFileSync(settingsPath, 'utf8');
            const settings = JSON.parse(content);
            const model = settings.env?.ANTHROPIC_MODEL;
            if (model) {
                // Format: 'glm-5' -> 'GLM-5' (uppercase letters, preserve numbers)
                return model
                    .split('-')
                    .map((part) => part.toUpperCase())
                    .join('-');
            }
        }
    }
    catch {
        // Fall through to default
    }
    // Fallback: profile name uppercase
    return profile.toUpperCase();
}
exports.getModelDisplayName = getModelDisplayName;
//# sourceMappingURL=config-manager.js.map