/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { PolicyDecision, MessageBusType, Storage, } from 'sixth-cli-core';
import { getSystemSettingsPath } from './settings.js';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { loadPoliciesFromToml, } from './policy-toml-loader.js';
// Get the directory name of the current module
const __dirname = path.dirname(fileURLToPath(import.meta.url));
// Store policy loading errors to be displayed after UI is ready
let storedPolicyErrors = [];
function getPolicyDirectories() {
    const DEFAULT_POLICIES_DIR = path.resolve(__dirname, 'policies');
    const USER_POLICIES_DIR = Storage.getUserPoliciesDir();
    const systemSettingsPath = getSystemSettingsPath();
    const ADMIN_POLICIES_DIR = path.join(path.dirname(systemSettingsPath), 'policies');
    return [
        DEFAULT_POLICIES_DIR,
        USER_POLICIES_DIR,
        ADMIN_POLICIES_DIR,
    ].reverse();
}
/**
 * Determines the policy tier (1=default, 2=user, 3=admin) for a given directory.
 * This is used by the TOML loader to assign priority bands.
 */
function getPolicyTier(dir) {
    const DEFAULT_POLICIES_DIR = path.resolve(__dirname, 'policies');
    const USER_POLICIES_DIR = Storage.getUserPoliciesDir();
    const systemSettingsPath = getSystemSettingsPath();
    const ADMIN_POLICIES_DIR = path.join(path.dirname(systemSettingsPath), 'policies');
    // Normalize paths for comparison
    const normalizedDir = path.resolve(dir);
    const normalizedDefault = path.resolve(DEFAULT_POLICIES_DIR);
    const normalizedUser = path.resolve(USER_POLICIES_DIR);
    const normalizedAdmin = path.resolve(ADMIN_POLICIES_DIR);
    if (normalizedDir === normalizedDefault)
        return 1;
    if (normalizedDir === normalizedUser)
        return 2;
    if (normalizedDir === normalizedAdmin)
        return 3;
    // Default to tier 1 if unknown
    return 1;
}
/**
 * Formats a policy file error for console logging.
 */
function formatPolicyError(error) {
    const tierLabel = error.tier.toUpperCase();
    let message = `[${tierLabel}] Policy file error in ${error.fileName}:\n`;
    message += `  ${error.message}`;
    if (error.details) {
        message += `\n${error.details}`;
    }
    if (error.suggestion) {
        message += `\n  Suggestion: ${error.suggestion}`;
    }
    return message;
}
export async function createPolicyEngineConfig(settings, approvalMode) {
    const policyDirs = getPolicyDirectories();
    // Load policies from TOML files
    const { rules: tomlRules, errors } = await loadPoliciesFromToml(approvalMode, policyDirs, getPolicyTier);
    // Store any errors encountered during TOML loading
    // These will be emitted by getPolicyErrorsForUI() after the UI is ready.
    if (errors.length > 0) {
        storedPolicyErrors = errors.map((error) => formatPolicyError(error));
    }
    const rules = [...tomlRules];
    // Priority system for policy rules:
    // - Higher priority numbers win over lower priority numbers
    // - When multiple rules match, the highest priority rule is applied
    // - Rules are evaluated in order of priority (highest first)
    //
    // Priority bands (tiers):
    // - Default policies (TOML): 1 + priority/1000 (e.g., priority 100 → 1.100)
    // - User policies (TOML): 2 + priority/1000 (e.g., priority 100 → 2.100)
    // - Admin policies (TOML): 3 + priority/1000 (e.g., priority 100 → 3.100)
    //
    // This ensures Admin > User > Default hierarchy is always preserved,
    // while allowing user-specified priorities to work within each tier.
    //
    // Settings-based and dynamic rules (all in user tier 2.x):
    //   2.95: Tools that the user has selected as "Always Allow" in the interactive UI
    //   2.9:  MCP servers excluded list (security: persistent server blocks)
    //   2.4:  Command line flag --exclude-tools (explicit temporary blocks)
    //   2.3:  Command line flag --allowed-tools (explicit temporary allows)
    //   2.2:  MCP servers with trust=true (persistent trusted servers)
    //   2.1:  MCP servers allowed list (persistent general server allows)
    //
    // TOML policy priorities (before transformation):
    //   10: Write tools default to ASK_USER (becomes 1.010 in default tier)
    //   15: Auto-edit tool override (becomes 1.015 in default tier)
    //   50: Read-only tools (becomes 1.050 in default tier)
    //   999: YOLO mode allow-all (becomes 1.999 in default tier)
    // MCP servers that are explicitly excluded in settings.mcp.excluded
    // Priority: 2.9 (highest in user tier for security - persistent server blocks)
    if (settings.mcp?.excluded) {
        for (const serverName of settings.mcp.excluded) {
            rules.push({
                toolName: `${serverName}__*`,
                decision: PolicyDecision.DENY,
                priority: 2.9,
            });
        }
    }
    // Tools that are explicitly excluded in the settings.
    // Priority: 2.4 (user tier - explicit temporary blocks)
    if (settings.tools?.exclude) {
        for (const tool of settings.tools.exclude) {
            rules.push({
                toolName: tool,
                decision: PolicyDecision.DENY,
                priority: 2.4,
            });
        }
    }
    // Tools that are explicitly allowed in the settings.
    // Priority: 2.3 (user tier - explicit temporary allows)
    if (settings.tools?.allowed) {
        for (const tool of settings.tools.allowed) {
            rules.push({
                toolName: tool,
                decision: PolicyDecision.ALLOW,
                priority: 2.3,
            });
        }
    }
    // MCP servers that are trusted in the settings.
    // Priority: 2.2 (user tier - persistent trusted servers)
    if (settings.mcpServers) {
        for (const [serverName, serverConfig] of Object.entries(settings.mcpServers)) {
            if (serverConfig.trust) {
                // Trust all tools from this MCP server
                // Using pattern matching for MCP tool names which are formatted as "serverName__toolName"
                rules.push({
                    toolName: `${serverName}__*`,
                    decision: PolicyDecision.ALLOW,
                    priority: 2.2,
                });
            }
        }
    }
    // MCP servers that are explicitly allowed in settings.mcp.allowed
    // Priority: 2.1 (user tier - persistent general server allows)
    if (settings.mcp?.allowed) {
        for (const serverName of settings.mcp.allowed) {
            rules.push({
                toolName: `${serverName}__*`,
                decision: PolicyDecision.ALLOW,
                priority: 2.1,
            });
        }
    }
    return {
        rules,
        defaultDecision: PolicyDecision.ASK_USER,
    };
}
export function createPolicyUpdater(policyEngine, messageBus) {
    messageBus.subscribe(MessageBusType.UPDATE_POLICY, (message) => {
        const toolName = message.toolName;
        policyEngine.addRule({
            toolName,
            decision: PolicyDecision.ALLOW,
            // User tier (2) + high priority (950/1000) = 2.95
            // This ensures user "always allow" selections are high priority
            // but still lose to admin policies (3.xxx) and settings excludes (200)
            priority: 2.95,
        });
    });
}
/**
 * Gets and clears any policy errors that were stored during config loading.
 * This should be called once the UI is ready to display errors.
 *
 * @returns Array of formatted error messages, or empty array if no errors
 */
export function getPolicyErrorsForUI() {
    const errors = [...storedPolicyErrors];
    storedPolicyErrors = []; // Clear after retrieving
    return errors;
}
//# sourceMappingURL=policy.js.map