"use strict";
/**
 * Instance Manager (Simplified)
 *
 * Manages isolated Claude CLI instances per profile for concurrent sessions.
 * Each instance is an isolated CLAUDE_CONFIG_DIR where users login directly.
 * No credential copying/encryption - Claude manages credentials per instance.
 */
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;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.InstanceManager = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const shared_manager_1 = __importDefault(require("./shared-manager"));
const profile_context_sync_lock_1 = __importDefault(require("./profile-context-sync-lock"));
const account_context_1 = require("../auth/account-context");
const config_manager_1 = require("../utils/config-manager");
const logging_1 = require("../services/logging");
const config_loader_facade_1 = require("../config/config-loader-facade");
const instance_directory_1 = require("./instance-directory");
const logger = (0, logging_1.createLogger)('management:instance-manager');
const MANAGED_MCP_SERVER_NAMES = new Set(['ccs-websearch', 'ccs-image-analysis', 'ccs-browser']);
/**
 * Instance Manager Class
 */
class InstanceManager {
    constructor() {
        this.instancesDir = path.join((0, config_loader_facade_1.getCcsDir)(), 'instances');
        this.sharedManager = new shared_manager_1.default();
        this.contextSyncLock = new profile_context_sync_lock_1.default(this.instancesDir);
        this.pluginLayoutLock = new profile_context_sync_lock_1.default(this.instancesDir);
    }
    /**
     * Ensure instance exists for profile (lazy init only)
     */
    async ensureInstance(profileName, contextPolicy = { mode: account_context_1.DEFAULT_ACCOUNT_CONTEXT_MODE }, options = {}) {
        const instancePath = this.getInstancePath(profileName);
        // Serialize context sync operations per profile across processes.
        await this.contextSyncLock.withLock(profileName, async () => {
            // Lazy initialization
            if (!fs.existsSync(instancePath)) {
                logger.stage('route', 'instance.init', 'Initializing new profile instance', {
                    profile: profileName,
                    bare: options.bare === true,
                });
                this.initializeInstance(profileName, instancePath, options);
            }
            // Validate structure (auto-fix missing dirs)
            this.validateInstance(instancePath);
            // Apply context policy (isolated by default, optional shared group).
            await this.sharedManager.syncProjectContext(instancePath, contextPolicy);
            await this.sharedManager.syncAdvancedContinuityArtifacts(instancePath, contextPolicy);
            await this.pluginLayoutLock.withNamedLock('__plugin-layout__', async () => {
                if (!options.bare) {
                    this.sharedManager.linkSharedDirectories(instancePath);
                    return;
                }
                this.sharedManager.detachSharedDirectories(instancePath);
                this.sharedManager.normalizeSharedPluginMetadataPaths();
            });
        });
        // Sync MCP servers from global ~/.claude.json (unless bare)
        if (!options.bare) {
            this.syncMcpServers(instancePath);
        }
        return instancePath;
    }
    /**
     * Get instance path for profile
     */
    getInstancePath(profileName) {
        const safeName = this.sanitizeName(profileName);
        return path.join(this.instancesDir, safeName);
    }
    /**
     * Initialize new instance directory
     */
    initializeInstance(profileName, instancePath, _options = {}) {
        try {
            // Create base directory
            fs.mkdirSync(instancePath, { recursive: true, mode: 0o700 });
            // Create Claude-expected subdirectories (profile-specific only)
            const subdirs = [
                'session-env',
                'todos',
                'logs',
                'file-history',
                'shell-snapshots',
                'debug',
                '.anthropic',
            ];
            subdirs.forEach((dir) => {
                const dirPath = path.join(instancePath, dir);
                if (!fs.existsSync(dirPath)) {
                    fs.mkdirSync(dirPath, { recursive: true, mode: 0o700 });
                }
            });
            // Shared links are created during ensureInstance() under the plugin layout lock.
        }
        catch (error) {
            throw new Error(`Failed to initialize instance for ${profileName}: ${error.message}`);
        }
    }
    /**
     * Validate instance directory structure (auto-fix missing directories)
     */
    validateInstance(instancePath) {
        // Check required directories (auto-create if missing for migration)
        const requiredDirs = [
            'session-env',
            'todos',
            'logs',
            'file-history',
            'shell-snapshots',
            'debug',
            '.anthropic',
        ];
        for (const dir of requiredDirs) {
            const dirPath = path.join(instancePath, dir);
            if (!fs.existsSync(dirPath)) {
                // Auto-create missing directory (migration from older versions)
                fs.mkdirSync(dirPath, { recursive: true, mode: 0o700 });
            }
        }
        // Note: Credentials managed by Claude CLI in instance (no validation needed)
    }
    /**
     * Delete instance for profile
     */
    async deleteInstance(profileName) {
        const instancePath = this.getInstancePath(profileName);
        if (!fs.existsSync(instancePath)) {
            return;
        }
        await this.contextSyncLock.withLock(profileName, async () => {
            await this.pluginLayoutLock.withNamedLock('__plugin-layout__', async () => {
                if (!fs.existsSync(instancePath)) {
                    return;
                }
                fs.rmSync(instancePath, { recursive: true, force: true });
                logger.stage('cleanup', 'instance.deleted', 'Profile instance deleted', {
                    profile: profileName,
                });
            });
        });
    }
    /**
     * List all instance names
     */
    listInstances() {
        return (0, instance_directory_1.listAccountInstanceNames)(this.instancesDir);
    }
    /**
     * Check if instance exists for profile
     */
    hasInstance(profileName) {
        const instancePath = this.getInstancePath(profileName);
        return fs.existsSync(instancePath);
    }
    /**
     * Sync MCP servers from global ~/.claude.json to instance .claude.json.
     * Selectively copies only mcpServers key (not OAuth sessions or caches).
     */
    syncMcpServers(instancePath) {
        const homeDir = (0, config_manager_1.getCcsHome)();
        const globalClaudeJson = path.join(homeDir, '.claude.json');
        if (!fs.existsSync(globalClaudeJson)) {
            return false;
        }
        try {
            const globalContent = JSON.parse(fs.readFileSync(globalClaudeJson, 'utf8'));
            const rawMcpServers = globalContent.mcpServers;
            if (!rawMcpServers ||
                typeof rawMcpServers !== 'object' ||
                Array.isArray(rawMcpServers) ||
                Object.keys(rawMcpServers).length === 0) {
                return false;
            }
            const mcpServers = rawMcpServers;
            const instanceClaudeJson = path.join(instancePath, '.claude.json');
            let instanceContent = {};
            if (fs.existsSync(instanceClaudeJson)) {
                try {
                    instanceContent = JSON.parse(fs.readFileSync(instanceClaudeJson, 'utf8'));
                }
                catch {
                    // Corrupted file, start fresh
                    instanceContent = {};
                }
            }
            // Merge: global MCP servers as base, instance-specific overrides on top,
            // except for CCS-managed entries which must stay aligned with the global runtime.
            const rawExistingMcp = instanceContent.mcpServers;
            const existingMcp = rawExistingMcp && typeof rawExistingMcp === 'object' && !Array.isArray(rawExistingMcp)
                ? rawExistingMcp
                : {};
            const mergedMcpServers = { ...mcpServers, ...existingMcp };
            for (const managedName of MANAGED_MCP_SERVER_NAMES) {
                if (managedName in mcpServers) {
                    mergedMcpServers[managedName] = mcpServers[managedName];
                }
            }
            instanceContent.mcpServers = mergedMcpServers;
            const fileMode = fs.existsSync(instanceClaudeJson)
                ? fs.statSync(instanceClaudeJson).mode & 0o777
                : 0o600;
            fs.writeFileSync(instanceClaudeJson, JSON.stringify(instanceContent, null, 2), {
                encoding: 'utf8',
                mode: fileMode,
            });
            return true;
        }
        catch (error) {
            // Best-effort: don't fail instance creation if MCP sync fails
            console.warn(`[!] MCP sync skipped: ${error.message}`);
            return false;
        }
    }
    /**
     * Sanitize profile name for filesystem
     */
    sanitizeName(name) {
        // Replace unsafe characters with dash
        return name.replace(/[^a-zA-Z0-9_-]/g, '-').toLowerCase();
    }
}
exports.InstanceManager = InstanceManager;
exports.default = InstanceManager;
//# sourceMappingURL=instance-manager.js.map