/**
 * Policy Manager
 *
 * Singleton manager for tool policies. Coordinates policy resolution,
 * configuration persistence, and event emission.
 *
 * Usage:
 * ```typescript
 * const manager = getPolicyManager();
 * const decision = manager.checkTool('bash', { command: 'npm install' });
 * if (decision.action === 'allow') {
 *   // Execute tool
 * } else if (decision.action === 'confirm') {
 *   // Request user confirmation
 * } else {
 *   // Deny operation
 * }
 * ```
 */
import { EventEmitter } from 'events';
import fs from 'fs';
import path from 'path';
import os from 'os';
import { DEFAULT_POLICY_CONFIG } from './types.js';
import { PolicyResolver } from './policy-resolver.js';
import { getProfile, getProfileNames } from './profiles.js';
import { getToolGroups, registerToolGroups } from './tool-groups.js';
// ============================================================================
// Policy Manager
// ============================================================================
/**
 * Central manager for tool policies
 */
export class PolicyManager extends EventEmitter {
    config;
    resolver;
    configPath;
    sessionOverrides = new Map();
    decisionCache = new Map();
    auditLog = [];
    constructor(configDir) {
        super();
        this.configPath = this.getConfigPath(configDir);
        this.config = this.loadConfig();
        this.resolver = new PolicyResolver(this.config);
    }
    // ============================================================================
    // Core API
    // ============================================================================
    /**
     * Check policy for a tool
     * @param toolName Tool to check
     * @param args Tool arguments (for conditional rules)
     * @returns Policy decision
     */
    checkTool(toolName, args) {
        // Check cache first
        const cacheKey = this.getCacheKey(toolName, args);
        const cached = this.decisionCache.get(cacheKey);
        if (cached) {
            return cached;
        }
        const context = {
            args,
            sessionOverrides: this.sessionOverrides,
        };
        const decision = this.resolver.resolve(toolName, context);
        // Cache the decision
        this.decisionCache.set(cacheKey, decision);
        // Log if audit mode is enabled
        if (this.config.auditLog) {
            this.logDecision(decision, { toolName, groups: getToolGroups(toolName), ...context });
        }
        // Emit event
        this.emit('policy:decision', { ...decision, context: { toolName, groups: getToolGroups(toolName), ...context } });
        if (decision.action === 'deny') {
            this.emit('policy:denied', { ...decision, context: { toolName, groups: getToolGroups(toolName), ...context } });
        }
        return decision;
    }
    /**
     * Check if a tool is allowed (shorthand)
     */
    isAllowed(toolName, args) {
        const decision = this.checkTool(toolName, args);
        return decision.action === 'allow';
    }
    /**
     * Check if a tool requires confirmation
     */
    requiresConfirmation(toolName, args) {
        const decision = this.checkTool(toolName, args);
        return decision.action === 'confirm';
    }
    /**
     * Check if a tool is denied
     */
    isDenied(toolName, args) {
        const decision = this.checkTool(toolName, args);
        return decision.action === 'deny';
    }
    // ============================================================================
    // Profile Management
    // ============================================================================
    /**
     * Get current profile
     */
    getProfile() {
        return this.config.activeProfile;
    }
    /**
     * Set active profile
     */
    setProfile(profile) {
        if (!getProfileNames().includes(profile)) {
            throw new Error(`Invalid profile: ${profile}. Available: ${getProfileNames().join(', ')}`);
        }
        const previousProfile = this.config.activeProfile;
        this.config.activeProfile = profile;
        this.resolver.updateConfig(this.config);
        this.clearCache();
        this.emit('policy:profile-changed', { from: previousProfile, to: profile });
        this.saveConfig();
    }
    /**
     * Get profile info
     */
    getProfileInfo(profile) {
        return getProfile(profile || this.config.activeProfile);
    }
    /**
     * Get all available profiles
     */
    getAvailableProfiles() {
        return getProfileNames();
    }
    // ============================================================================
    // Session Overrides
    // ============================================================================
    /**
     * Set session override for a tool
     */
    setSessionOverride(toolName, action) {
        this.sessionOverrides.set(toolName, action);
        this.clearCache();
    }
    /**
     * Clear session override for a tool
     */
    clearSessionOverride(toolName) {
        this.sessionOverrides.delete(toolName);
        this.clearCache();
    }
    /**
     * Clear all session overrides
     */
    clearAllSessionOverrides() {
        this.sessionOverrides.clear();
        this.clearCache();
    }
    /**
     * Get all session overrides
     */
    getSessionOverrides() {
        return new Map(this.sessionOverrides);
    }
    // ============================================================================
    // Rule Management
    // ============================================================================
    /**
     * Add a global rule
     */
    addGlobalRule(rule) {
        this.config.globalRules.push(rule);
        this.resolver.updateConfig(this.config);
        this.clearCache();
        this.emit('policy:rule-added', { rule, source: 'global' });
        this.saveConfig();
    }
    /**
     * Add an agent-specific rule
     */
    addAgentRule(agentId, rule) {
        if (!this.config.agentRules[agentId]) {
            this.config.agentRules[agentId] = [];
        }
        this.config.agentRules[agentId].push(rule);
        this.resolver.updateConfig(this.config);
        this.clearCache();
        this.emit('policy:rule-added', { rule, source: 'agent' });
        this.saveConfig();
    }
    /**
     * Add a provider-specific rule
     */
    addProviderRule(providerId, rule) {
        if (!this.config.providerRules[providerId]) {
            this.config.providerRules[providerId] = [];
        }
        this.config.providerRules[providerId].push(rule);
        this.resolver.updateConfig(this.config);
        this.clearCache();
        this.emit('policy:rule-added', { rule, source: 'provider' });
        this.saveConfig();
    }
    /**
     * Remove a global rule
     */
    removeGlobalRule(group) {
        const index = this.config.globalRules.findIndex(r => r.group === group);
        if (index >= 0) {
            this.config.globalRules.splice(index, 1);
            this.resolver.updateConfig(this.config);
            this.clearCache();
            this.saveConfig();
            return true;
        }
        return false;
    }
    /**
     * Get all global rules
     */
    getGlobalRules() {
        return [...this.config.globalRules];
    }
    // ============================================================================
    // Tool Registration
    // ============================================================================
    /**
     * Register a tool with its groups
     */
    registerTool(toolName, groups) {
        registerToolGroups(toolName, groups);
        this.clearCache();
    }
    /**
     * Get groups for a tool
     */
    getToolGroups(toolName) {
        return getToolGroups(toolName);
    }
    // ============================================================================
    // Configuration
    // ============================================================================
    /**
     * Get current configuration
     */
    getConfig() {
        return { ...this.config };
    }
    /**
     * Update configuration
     */
    updateConfig(updates) {
        Object.assign(this.config, updates);
        this.resolver.updateConfig(this.config);
        this.clearCache();
        this.saveConfig();
    }
    /**
     * Reset to default configuration
     */
    resetConfig() {
        this.config = { ...DEFAULT_POLICY_CONFIG };
        this.resolver = new PolicyResolver(this.config);
        this.clearCache();
        this.saveConfig();
    }
    /**
     * Enable/disable audit logging
     */
    setAuditLog(enabled) {
        this.config.auditLog = enabled;
        this.saveConfig();
    }
    /**
     * Get audit log
     */
    getAuditLog() {
        return [...this.auditLog];
    }
    /**
     * Clear audit log
     */
    clearAuditLog() {
        this.auditLog = [];
    }
    // ============================================================================
    // Persistence
    // ============================================================================
    /**
     * Get config file path
     */
    getConfigPath(configDir) {
        const dir = configDir || path.join(os.homedir(), '.codebuddy');
        return path.join(dir, 'tool-policy.json');
    }
    /**
     * Load configuration from file
     */
    loadConfig() {
        try {
            if (fs.existsSync(this.configPath)) {
                const content = fs.readFileSync(this.configPath, 'utf-8');
                const loaded = JSON.parse(content);
                // Validate and merge with defaults
                return {
                    ...DEFAULT_POLICY_CONFIG,
                    ...loaded,
                    // Ensure arrays are not undefined
                    globalRules: loaded.globalRules || [],
                    agentRules: loaded.agentRules || {},
                    providerRules: loaded.providerRules || {},
                };
            }
        }
        catch (error) {
            this.emit('policy:error', { error: error });
        }
        return { ...DEFAULT_POLICY_CONFIG };
    }
    /**
     * Save configuration to file
     */
    saveConfig() {
        try {
            const dir = path.dirname(this.configPath);
            if (!fs.existsSync(dir)) {
                fs.mkdirSync(dir, { recursive: true });
            }
            fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
            this.emit('policy:config-saved', { config: this.config });
        }
        catch (error) {
            this.emit('policy:error', { error: error });
        }
    }
    // ============================================================================
    // Helpers
    // ============================================================================
    /**
     * Get cache key for tool/args combination
     */
    getCacheKey(toolName, args) {
        if (!args || Object.keys(args).length === 0) {
            return toolName;
        }
        // Include relevant args in cache key
        const relevantArgs = ['path', 'file', 'target', 'command', 'cmd'];
        const argParts = relevantArgs
            .filter(key => args[key])
            .map(key => `${key}:${args[key]}`)
            .join('|');
        return argParts ? `${toolName}|${argParts}` : toolName;
    }
    /**
     * Clear decision cache
     */
    clearCache() {
        this.decisionCache.clear();
    }
    /**
     * Log decision for audit
     */
    logDecision(decision, context) {
        this.auditLog.push({
            timestamp: new Date(),
            decision,
            context,
        });
        // Keep audit log bounded
        const maxLogSize = 1000;
        if (this.auditLog.length > maxLogSize) {
            this.auditLog = this.auditLog.slice(-maxLogSize);
        }
    }
    /**
     * Format status for display
     */
    formatStatus() {
        const profile = this.getProfileInfo();
        const icons = {
            minimal: '🔒',
            coding: '💻',
            messaging: '💬',
            full: '🔓',
        };
        let output = '🛡️ Tool Policy Status\n' + '═'.repeat(50) + '\n\n';
        output += `Profile: ${icons[this.config.activeProfile]} ${this.config.activeProfile.toUpperCase()}\n`;
        output += `${profile.description}\n\n`;
        output += '📋 Global Rules: ' + (this.config.globalRules.length || 'None') + '\n';
        output += '👤 Agent Rules: ' + Object.keys(this.config.agentRules).length + ' agents\n';
        output += '🔌 Provider Rules: ' + Object.keys(this.config.providerRules).length + ' providers\n';
        output += '📝 Session Overrides: ' + this.sessionOverrides.size + '\n\n';
        output += `Audit Log: ${this.config.auditLog ? '✅ Enabled' : '❌ Disabled'}\n`;
        output += `Default Action: ${this.config.defaultAction}\n`;
        return output;
    }
    /**
     * Dispose and cleanup
     */
    dispose() {
        this.removeAllListeners();
        this.clearCache();
    }
}
// ============================================================================
// Singleton
// ============================================================================
let policyManagerInstance = null;
/**
 * Get or create the PolicyManager singleton
 */
export function getPolicyManager(configDir) {
    if (!policyManagerInstance) {
        policyManagerInstance = new PolicyManager(configDir);
    }
    return policyManagerInstance;
}
/**
 * Reset the PolicyManager singleton
 */
export function resetPolicyManager() {
    if (policyManagerInstance) {
        policyManagerInstance.dispose();
    }
    policyManagerInstance = null;
}
// ============================================================================
// Convenience Functions
// ============================================================================
/**
 * Quick check if tool is allowed
 */
export function isToolAllowed(toolName, args) {
    return getPolicyManager().isAllowed(toolName, args);
}
/**
 * Quick check if tool requires confirmation
 */
export function toolRequiresConfirmation(toolName, args) {
    return getPolicyManager().requiresConfirmation(toolName, args);
}
/**
 * Quick check if tool is denied
 */
export function isToolDenied(toolName, args) {
    return getPolicyManager().isDenied(toolName, args);
}
//# sourceMappingURL=policy-manager.js.map