import { AiMode } from "@superblocksteam/library-shared/types";
import { SubagentType } from "../subagents/types.js";
import { ToolCategory, PermissionLevel, } from "./types.js";
/**
 * Access control utilities and common patterns.
 */
/**
 * Block tools by name pattern.
 *
 * @param pattern - String or regex pattern to match tool names
 * @returns Access control rule that blocks matching tools
 *
 * @example
 * registry.addAccessRule(blockTools(/^delete-/));
 * registry.addAccessRule(blockTools("drop-database"));
 */
export function blockTools(pattern) {
    const patterns = Array.isArray(pattern) ? pattern : [pattern];
    return (tool) => {
        for (const p of patterns) {
            if (typeof p === "string" && tool.name === p) {
                return PermissionLevel.BLOCK;
            }
            if (p instanceof RegExp && p.test(tool.name)) {
                return PermissionLevel.BLOCK;
            }
        }
        return PermissionLevel.ALLOW;
    };
}
/**
 * Require prompt for tools by name pattern.
 *
 * @param pattern - String or regex pattern to match tool names
 * @returns Access control rule that prompts for matching tools
 *
 * @example
 * registry.addAccessRule(promptForTools(/^delete-/));
 * registry.addAccessRule(promptForTools(["delete-file", "drop-table"]));
 */
export function promptForTools(pattern) {
    const patterns = Array.isArray(pattern) ? pattern : [pattern];
    return (tool) => {
        for (const p of patterns) {
            if (typeof p === "string" && tool.name === p) {
                return PermissionLevel.PROMPT;
            }
            if (p instanceof RegExp && p.test(tool.name)) {
                return PermissionLevel.PROMPT;
            }
        }
        return PermissionLevel.ALLOW;
    };
}
/**
 * Require specific user role.
 *
 * @param allowedRoles - Role or array of roles allowed to execute
 * @param permission - Permission level to return if role doesn't match
 * @returns Access control rule that checks user role
 *
 * @example
 * registry.addAccessRule(requireRole("admin", PermissionLevel.BLOCK));
 * registry.addAccessRule(requireRole(["admin", "editor"], PermissionLevel.PROMPT));
 */
export function requireRole(allowedRoles, permission = PermissionLevel.BLOCK) {
    const roles = Array.isArray(allowedRoles) ? allowedRoles : [allowedRoles];
    return (_toolName, context) => {
        if (!context.userRole) {
            return permission;
        }
        return roles.includes(context.userRole)
            ? PermissionLevel.ALLOW
            : permission;
    };
}
/**
 * Combine multiple rules with AND logic (most restrictive wins).
 *
 * @param rules - Array of rules to combine
 * @returns Access control rule that evaluates all rules
 *
 * @example
 * const rule = combineRules([
 *   requireRole("admin"),
 *   blockTools(/^delete-/)
 * ]);
 */
export function combineRules(rules) {
    return async (tool, context) => {
        let permission = PermissionLevel.ALLOW;
        for (const rule of rules) {
            const result = await rule(tool, context);
            if (result === PermissionLevel.BLOCK) {
                return PermissionLevel.BLOCK;
            }
            if (result === PermissionLevel.PROMPT) {
                permission = PermissionLevel.PROMPT;
            }
        }
        return permission;
    };
}
/**
 * Create a rule that only applies to specific tools.
 *
 * @param toolNames - Tool name or array of tool names
 * @param rule - Rule to apply to those tools
 * @returns Access control rule that conditionally applies
 *
 * @example
 * const rule = forTools(["delete-file", "delete-api"], requireRole("admin"));
 */
export function forTools(toolNames, rule) {
    const names = Array.isArray(toolNames) ? toolNames : [toolNames];
    return async (tool, context, input) => {
        if (names.includes(tool.name)) {
            return rule(tool, context, input);
        }
        return PermissionLevel.ALLOW;
    };
}
/**
 * Create a rule based on tool category.
 *
 * @param category - Category name to match
 * @param permission - Permission level to return for matching category
 * @returns Access control rule that checks tool category
 *
 * @example
 * const rule = forCategory("filesystem", PermissionLevel.PROMPT);
 */
export function forCategory(category, rule) {
    return (tool, context, input) => {
        if (tool.category === category) {
            return rule(tool, context, input);
        }
        return PermissionLevel.ALLOW;
    };
}
/**
 * Create a rule based on AI mode.
 *
 * Requires ToolRegistry to be passed in to look up metadata.
 *
 * @param mode - AI mode to match
 * @param rule - Rule to apply for that mode
 * @returns Access control rule that checks AI mode
 *
 * @example
 * const rule = forMode(AiMode.PLAN, readOnly());
 */
export function forMode(mode, rule) {
    return (tool, context, input) => {
        if (context.mode === mode) {
            return rule(tool, context, input);
        }
        return PermissionLevel.ALLOW;
    };
}
export function require(predicate, permission = PermissionLevel.BLOCK) {
    return (tool, context, input) => {
        return predicate({ tool, context, input })
            ? PermissionLevel.ALLOW
            : permission;
    };
}
/**
 * Read-only access rule.
 * Allows tools marked as readOnly, blocks others.
 */
export function readOnly() {
    return (tool) => {
        if (tool.readOnly) {
            return PermissionLevel.ALLOW;
        }
        return PermissionLevel.BLOCK;
    };
}
export function forSubagent(subagent, rule) {
    return (tool, context, input) => {
        if (context.subagent === subagent) {
            return rule(tool, context, input);
        }
        return PermissionLevel.ALLOW;
    };
}
/**
 * Map subagent types to the tool categories they can access
 *
 * If a subagent is not in the mapping, it has no category restrictions.
 *
 * @param map - Map of subagent types to their allowed categories
 * @returns Access control rule that enforces category restrictions per subagent
 *
 * @example
 * ```typescript
 * registry.addAccessRule(
 *   subagentCategoryFilter({
 *     [SubagentType.MAIN]: [ToolCategory.COMPONENT, ToolCategory.FILE, ToolCategory.GENERAL],
 *     [SubagentType.TEST]: [ToolCategory.TEST],
 *     // Subagents not listed = no restrictions
 *   })
 * );
 * ```
 */
export function subagentCategoryMapping(map) {
    return (tool, context) => {
        const subagent = context.subagent ?? SubagentType.MAIN;
        // Get allowed categories for this subagent
        const allowedCategories = map[subagent];
        // If subagent not in mapping, it has no restrictions
        if (!allowedCategories) {
            return PermissionLevel.ALLOW;
        }
        // Check if tool's category is allowed for this subagent
        return allowedCategories.includes(tool.category)
            ? PermissionLevel.ALLOW
            : PermissionLevel.BLOCK;
    };
}
const ALL_TOOL_CATEGORIES = Object.values(ToolCategory);
/**
 * Common access control presets.
 */
export const presets = {
    /**
     * Base rules applied in all modes.
     *
     * - Primary filtering by subagent and tool category
     * - PLAN mode: only read-only tools are available unless the user has approved execution
     */
    base: () => [
        // Primary filtering by subagent and tool category
        subagentCategoryMapping({
            // Main subagent - has access to most tools except TEST (uses startTestRun instead)
            [SubagentType.MAIN]: ALL_TOOL_CATEGORIES.filter((category) => category !== ToolCategory.TEST),
            // Test subagent - specializes in end-to-end testing with Playwright
            [SubagentType.TEST]: [
                ToolCategory.TEST, // Playwright browser automation tools
                ToolCategory.GENERAL, // General utilities
                ToolCategory.METADATA, // Read metadata
                ToolCategory.FILE, // Read files (important for generating test selectors)
            ],
        }),
        // PLAN mode: only read-only tools are available unless the user has approved execution
        forMode(AiMode.PLAN, require(({ tool, context }) => tool.readOnly ||
            context.planContext?.approved === true ||
            tool.category === ToolCategory.TEST ||
            tool.category === ToolCategory.DEBUG)),
        // FACT mode: only MEMORY tools and read-only tools are available.
        // This prevents Clark from making any app changes while in fact-drafting mode.
        forMode(AiMode.FACT, require(({ tool }) => tool.readOnly || tool.category === ToolCategory.MEMORY)),
        // Tools in the PLAN category are only available in PLAN mode
        forCategory(ToolCategory.PLAN, require(({ context }) => context.mode === AiMode.PLAN)),
        // Tools in the DATA category require customer data access to be enabled
        // When enabled, they are upgraded from BLOCK to PROMPT for entity-level approval
        forCategory(ToolCategory.DATA, (_tool, context) => {
            const { customerDataAccessEnabled, enableClarkAiReadAccessPermission } = context;
            if (customerDataAccessEnabled) {
                // If read access permission is not enabled, require prompt
                if (!enableClarkAiReadAccessPermission) {
                    return PermissionLevel.PROMPT;
                }
                return PermissionLevel.ALLOW;
            }
            // Keep blocked when customer data access is disabled
            return PermissionLevel.BLOCK;
        }),
        // DEBUG tools (console logs, screenshots) require customer data access
        forCategory(ToolCategory.DEBUG, (_tool, context) => {
            const { customerDataAccessEnabled } = context;
            if (customerDataAccessEnabled) {
                return PermissionLevel.ALLOW;
            }
            return PermissionLevel.BLOCK;
        }),
        // TEST tools require customer data access (blocks test subagent when disabled)
        forCategory(ToolCategory.TEST, (_tool, context) => {
            const { customerDataAccessEnabled } = context;
            if (customerDataAccessEnabled) {
                return PermissionLevel.ALLOW;
            }
            return PermissionLevel.BLOCK;
        }),
    ],
    /**
     * Development mode: Allow everything.
     */
    development: () => [],
    /**
     * Production mode: Prompt for destructive operations.
     */
    production: () => [
        promptForTools([
            /^delete-/,
            /^drop-/,
            /^remove-/,
            "bash", // Prompt for bash commands
        ]),
    ],
    /**
     * Restricted mode: Block destructive operations entirely.
     */
    restricted: () => [
        blockTools([/^delete-/, /^drop-/, /^remove-/, "bash"]),
    ],
    /**
     * Guest mode: Read-only access.
     */
    guest: () => [
        blockTools([/^write-/, /^edit-/, /^delete-/, /^create-/, "bash"]),
    ],
};
//# sourceMappingURL=access-control.js.map