/**
 * Tool Registry
 *
 * Metadata for all management tools, used for:
 * - OAuth consent UI (displaying available permissions)
 * - API documentation
 * - Tool discovery
 * - Role permission selection
 *
 * This module is consumed by both the API and web apps. Keep it isomorphic:
 * do not import app runtime values.
 *
 * Keep ALL_TOOL_NAMES in sync with CORE_TOOLS in apps/api/src/tools/index.ts.
 * The API registry metadata test verifies they match.
 */

// ============================================================================
// Types
// ============================================================================

/**
 * High-level categories for grouping tools in the UI
 */
export type ToolCategory =
  | "Organizations"
  | "Connections"
  | "Virtual MCPs"
  | "Threads"
  | "Monitoring"
  | "Users"
  | "API Keys"
  | "Tags"
  | "AI Providers"
  | "Secrets"
  | "File Configs"
  | "Automations"
  | "Object Storage"
  | "Registry"
  | "GitHub"
  | "VM"
  | "Search"
  | "Task Board";

/**
 * All tool names - keep in sync with CORE_TOOLS in apps/api/src/tools/index.ts
 */
const ALL_TOOL_NAMES = [
  // Organization tools
  "ORGANIZATION_CREATE",
  "ORGANIZATION_LIST",
  "ORGANIZATION_GET",
  "ORGANIZATION_UPDATE",
  "ORGANIZATION_DELETE",
  "ORGANIZATION_SETTINGS_GET",
  "ORGANIZATION_SETTINGS_UPDATE",
  "BRAND_CONTEXT_LIST",
  "BRAND_CONTEXT_GET",
  "BRAND_CONTEXT_CREATE",
  "BRAND_CONTEXT_UPDATE",
  "BRAND_CONTEXT_DELETE",
  "BRAND_CONTEXT_EXTRACT",
  "BRAND_GET",
  "BRAND_LIST",
  "ORGANIZATION_DOMAIN_LIST",
  "ORGANIZATION_DOMAIN_ADD",
  "ORGANIZATION_DOMAIN_UPDATE",
  "ORGANIZATION_DOMAIN_VERIFY",
  "ORGANIZATION_DOMAIN_REMOVE",
  "ORGANIZATION_JOIN_REQUEST_LIST",
  "ORGANIZATION_JOIN_REQUEST_APPROVE",
  "ORGANIZATION_JOIN_REQUEST_DENY",
  "ORGANIZATION_MEMBER_ADD",
  "ORGANIZATION_MEMBER_REMOVE",
  "ORGANIZATION_MEMBER_LIST",
  "ORGANIZATION_MEMBER_UPDATE_ROLE",
  "ORGANIZATION_BILLING_CHECKOUT_START",
  "ORGANIZATION_BILLING_PORTAL",
  "ORGANIZATION_TASK_QUOTA_GET",
  // Connection tools
  "COLLECTION_CONNECTIONS_CREATE",
  "COLLECTION_CONNECTIONS_LIST",
  "COLLECTION_CONNECTIONS_GET",
  "COLLECTION_CONNECTIONS_UPDATE",
  "COLLECTION_CONNECTIONS_DELETE",
  "CONNECTION_TEST",
  "COMMERCE_DISCOVERY_SETUP",
  "COMMERCE_DISCOVERY_RUN",
  "COMMERCE_DISCOVERY_BIND",
  "COMMERCE_DISCOVERY_CONNECTION_STATUS",
  // Virtual MCP tools
  "COLLECTION_VIRTUAL_MCP_CREATE",
  "COLLECTION_VIRTUAL_MCP_LIST",
  "COLLECTION_VIRTUAL_MCP_GET",
  "COLLECTION_VIRTUAL_MCP_UPDATE",
  "COLLECTION_VIRTUAL_MCP_DELETE",
  // Database tools
  "DATABASES_RUN_SQL",
  // Monitoring tools
  "MONITORING_LOG_GET",
  "MONITORING_LOGS_LIST",
  "MONITORING_STATS",
  "MONITORING_THREAD_USAGE",
  // API Key tools
  "API_KEY_CREATE",
  "API_KEY_LIST",
  "API_KEY_UPDATE",
  "API_KEY_DELETE",
  // User tools
  "USER_GET",
  "USER_MODEL_PREFERENCES_GET",
  "USER_MODEL_PREFERENCES_UPDATE",
  // Thread tools
  "COLLECTION_THREADS_CREATE",
  "COLLECTION_THREADS_LIST",
  "COLLECTION_THREADS_GET",
  "COLLECTION_THREADS_UPDATE",
  "COLLECTION_THREADS_DELETE",
  "COLLECTION_THREAD_MESSAGES_LIST",
  // Tag tools
  "TAGS_LIST",
  "TAGS_CREATE",
  "TAGS_DELETE",
  "MEMBER_TAGS_GET",
  "MEMBER_TAGS_SET",
  // Automation tools
  "AUTOMATION_CREATE",
  "AUTOMATION_GET",
  "AUTOMATION_LIST",
  "AUTOMATION_UPDATE",
  "AUTOMATION_DELETE",
  "AUTOMATION_TRIGGER_ADD",
  "AUTOMATION_TRIGGER_REMOVE",
  "AUTOMATION_TRIGGER_ROTATE_TOKEN",
  "AUTOMATION_RUN",
  "AUTOMATION_RUN_STATS",
  // Virtual MCP plugin config and pinned views tools
  "VIRTUAL_MCP_PLUGIN_CONFIG_GET",
  "VIRTUAL_MCP_PLUGIN_CONFIG_UPDATE",
  "VIRTUAL_MCP_PINNED_VIEWS_UPDATE",
  "VIRTUAL_MCP_LAST_USED_LIST",

  // Ai providers tools
  "AI_PROVIDERS_LIST",
  "AI_PROVIDERS_LIST_MODELS",
  "AI_PROVIDERS_ACTIVE",
  "AI_PROVIDER_KEY_CREATE",
  "AI_PROVIDER_KEY_LIST",
  "AI_PROVIDER_KEY_DELETE",
  "AI_PROVIDER_KEY_UPDATE",
  "AI_PROVIDER_KEY_PREVIEW",
  "AI_PROVIDER_OAUTH_URL",
  "AI_PROVIDER_OAUTH_EXCHANGE",
  "AI_PROVIDER_PROVISION_KEY",
  "AI_PROVIDER_TOPUP_URL",
  "AI_PROVIDER_CREDITS",

  // Claude subscription (per-user OAuth credential for the claude-code harness)
  "CLAUDE_SUBSCRIPTION_CONNECT",
  "CLAUDE_SUBSCRIPTION_STATUS",
  "CLAUDE_SUBSCRIPTION_DISCONNECT",

  // Secrets vault tools
  "SECRET_CREATE",
  "SECRET_LIST",

  // File config tools (org-scoped S3 bucket configurations)
  "FILE_CONFIG_CREATE",
  "FILE_CONFIG_LIST",
  "FILE_CONFIG_UPDATE",
  "FILE_CONFIG_DELETE",
  "FILE_OBJECTS_LIST",

  // Org filesystem (shared public skill sets)
  "ORG_FS_PUBLIC_SETS_SYNC",

  // Object Storage tools
  "LIST_OBJECTS",
  "GET_OBJECT_METADATA",
  "GET_PRESIGNED_URL",
  "PUT_PRESIGNED_URL",
  "DELETE_OBJECT",
  "DELETE_OBJECTS",

  // Registry tools
  "COLLECTION_REGISTRY_APP_LIST",
  "COLLECTION_REGISTRY_APP_GET",
  "COLLECTION_REGISTRY_APP_VERSIONS",
  "COLLECTION_REGISTRY_APP_FILTERS",
  "REGISTRY_ITEM_LIST",
  "REGISTRY_ITEM_SEARCH",
  "REGISTRY_ITEM_GET",
  "REGISTRY_ITEM_VERSIONS",
  "REGISTRY_ITEM_CREATE",
  "REGISTRY_ITEM_BULK_CREATE",
  "REGISTRY_ITEM_UPDATE",
  "REGISTRY_ITEM_DELETE",
  "REGISTRY_ITEM_FILTERS",
  "REGISTRY_DISCOVER_TOOLS",
  "REGISTRY_AI_GENERATE",
  "REGISTRY_PUBLISH_REQUEST_LIST",
  "REGISTRY_PUBLISH_REQUEST_REVIEW",
  "REGISTRY_PUBLISH_REQUEST_COUNT",
  "REGISTRY_PUBLISH_REQUEST_DELETE",
  "REGISTRY_PUBLISH_API_KEY_GENERATE",
  "REGISTRY_PUBLISH_API_KEY_LIST",
  "REGISTRY_PUBLISH_API_KEY_REVOKE",
  "REGISTRY_MONITOR_RUN_START",
  "REGISTRY_MONITOR_RUN_LIST",
  "REGISTRY_MONITOR_RUN_GET",
  "REGISTRY_MONITOR_RUN_CANCEL",
  "REGISTRY_MONITOR_RESULT_LIST",
  "REGISTRY_MONITOR_CONNECTION_LIST",
  "REGISTRY_MONITOR_CONNECTION_SYNC",
  "REGISTRY_MONITOR_CONNECTION_UPDATE_AUTH",

  // VM tools (app-only)
  "SANDBOX_START",
  "SANDBOX_DELETE",

  // GitHub tools (app-only)
  "GITHUB_LIST_USER_ORGS",

  // Search tools
  "GLOBAL_SEARCH",

  // Task board tools
  "TASK_BOARD_ITEM_CREATE",
  "TASK_BOARD_ITEM_LIST",
  "TASK_BOARD_ITEM_UPDATE",
  "TASK_BOARD_ITEM_DELETE",
  "TASK_BOARD_ITEM_PRS_GET",
  "TASK_BOARD_ITEM_PR_LINK",
  "TASK_BOARD_ITEM_RERUN",
  "TASK_BOARD_REVIEW_DECISION",
  "TASK_BOARD_PROMOTE_TO_PRODUCTION",
  "TASK_BOARD_ACTIVITY_LIST",
  "TASK_BOARD_COMMENT_LIST",
  "TASK_BOARD_COMMENT_CREATE",
  "TASK_BOARD_COMMENT_UPDATE",
  "TASK_BOARD_COMMENT_DELETE",
  "TASK_BOARD_DISMISSED_LIST",
  "TASK_BOARD_DISMISSED_RESTORE",
  "TASK_ADD_REPO",
] as const;

/**
 * ToolName type derived from ALL_TOOL_NAMES
 */
export type ToolName = (typeof ALL_TOOL_NAMES)[number];

/**
 * Metadata for a single MCP management tool — name, description, category, and danger flag.
 */
export interface ToolMetadata {
  name: ToolName;
  description: string;
  category: ToolCategory;
  dangerous?: boolean; // Requires extra confirmation
}

// ============================================================================
// Tool Metadata (static - no server imports)
// ============================================================================

/**
 * All management tools with metadata
 * Defined statically to avoid importing server-side tool implementations
 */
export const MANAGEMENT_TOOLS: ToolMetadata[] = [
  // Organization tools
  {
    name: "ORGANIZATION_CREATE",
    description: "Create a new organization",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_LIST",
    description: "List organizations",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_GET",
    description: "View organization details",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_UPDATE",
    description: "Update organization",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_DELETE",
    description: "Delete organization",
    category: "Organizations",
    dangerous: true,
  },
  {
    name: "ORGANIZATION_SETTINGS_GET",
    description: "View organization settings",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_SETTINGS_UPDATE",
    description: "Update organization settings",
    category: "Organizations",
  },
  {
    name: "BRAND_CONTEXT_LIST",
    description: "List brand contexts",
    category: "Organizations",
  },
  {
    name: "BRAND_CONTEXT_GET",
    description: "View brand context",
    category: "Organizations",
  },
  {
    name: "BRAND_CONTEXT_CREATE",
    description: "Create brand context",
    category: "Organizations",
  },
  {
    name: "BRAND_CONTEXT_UPDATE",
    description: "Update brand context",
    category: "Organizations",
  },
  {
    name: "BRAND_CONTEXT_DELETE",
    description: "Delete brand context",
    category: "Organizations",
    dangerous: true,
  },
  {
    name: "BRAND_CONTEXT_EXTRACT",
    description: "Extract brand context from website",
    category: "Organizations",
  },
  {
    name: "BRAND_GET",
    description: "Get brand (binding)",
    category: "Organizations",
  },
  {
    name: "BRAND_LIST",
    description: "List brands (binding)",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_DOMAIN_LIST",
    description: "List organization domain claims",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_DOMAIN_ADD",
    description: "Claim an email domain for the organization",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_DOMAIN_UPDATE",
    description: "Set a domain's join mode",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_DOMAIN_VERIFY",
    description: "Verify a domain via DNS TXT record",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_DOMAIN_REMOVE",
    description: "Remove an organization domain claim",
    category: "Organizations",
    dangerous: true,
  },
  {
    name: "ORGANIZATION_JOIN_REQUEST_LIST",
    description: "List pending requests to join the organization",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_JOIN_REQUEST_APPROVE",
    description: "Approve a request to join the organization",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_JOIN_REQUEST_DENY",
    description: "Deny a request to join the organization",
    category: "Organizations",
    dangerous: true,
  },
  {
    name: "ORGANIZATION_MEMBER_ADD",
    description: "Add members",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_MEMBER_REMOVE",
    description: "Remove members",
    category: "Organizations",
    dangerous: true,
  },
  {
    name: "ORGANIZATION_MEMBER_LIST",
    description: "List members",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_MEMBER_UPDATE_ROLE",
    description: "Update member roles",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_BILLING_CHECKOUT_START",
    description: "Start the org subscription checkout",
    category: "Organizations",
    dangerous: true,
  },
  {
    name: "ORGANIZATION_BILLING_PORTAL",
    description: "Open the Stripe billing portal",
    category: "Organizations",
  },
  {
    name: "ORGANIZATION_TASK_QUOTA_GET",
    description: "Get the org's auto-task quota usage",
    category: "Organizations",
  },
  // Connection tools
  {
    name: "COLLECTION_CONNECTIONS_CREATE",
    description: "Create connections",
    category: "Connections",
  },
  {
    name: "COLLECTION_CONNECTIONS_LIST",
    description: "List connections",
    category: "Connections",
  },
  {
    name: "COLLECTION_CONNECTIONS_GET",
    description: "View connection details",
    category: "Connections",
  },
  {
    name: "COLLECTION_CONNECTIONS_UPDATE",
    description: "Update connections",
    category: "Connections",
  },
  {
    name: "COLLECTION_CONNECTIONS_DELETE",
    description: "Delete connections",
    category: "Connections",
    dangerous: true,
  },
  {
    name: "CONNECTION_TEST",
    description: "Test connections",
    category: "Connections",
  },
  {
    name: "COMMERCE_DISCOVERY_SETUP",
    description: "Set up Commerce Discovery",
    category: "Connections",
  },
  {
    name: "COMMERCE_DISCOVERY_RUN",
    description: "Run Commerce Discovery",
    category: "Connections",
  },
  {
    name: "COMMERCE_DISCOVERY_BIND",
    description: "Bind Commerce Discovery data source",
    category: "Connections",
  },
  {
    name: "COMMERCE_DISCOVERY_CONNECTION_STATUS",
    description: "Read Commerce Discovery connection status",
    category: "Connections",
  },
  {
    name: "DATABASES_RUN_SQL",
    description: "Run SQL queries",
    category: "Connections",
    dangerous: true,
  },
  // Virtual MCP tools
  {
    name: "COLLECTION_VIRTUAL_MCP_CREATE",
    description: "Create virtual MCPs",
    category: "Virtual MCPs",
  },
  {
    name: "COLLECTION_VIRTUAL_MCP_LIST",
    description: "List virtual MCPs",
    category: "Virtual MCPs",
  },
  {
    name: "COLLECTION_VIRTUAL_MCP_GET",
    description: "View virtual MCP details",
    category: "Virtual MCPs",
  },
  {
    name: "COLLECTION_VIRTUAL_MCP_UPDATE",
    description: "Update virtual MCPs",
    category: "Virtual MCPs",
  },
  {
    name: "COLLECTION_VIRTUAL_MCP_DELETE",
    description: "Delete virtual MCPs",
    category: "Virtual MCPs",
    dangerous: true,
  },
  // Monitoring tools
  {
    name: "MONITORING_LOG_GET",
    description: "View monitoring log details",
    category: "Monitoring",
  },
  {
    name: "MONITORING_LOGS_LIST",
    description: "List monitoring logs",
    category: "Monitoring",
  },
  {
    name: "MONITORING_STATS",
    description: "View monitoring statistics",
    category: "Monitoring",
  },
  {
    name: "MONITORING_THREAD_USAGE",
    description: "View per-thread token usage and cost",
    category: "Monitoring",
  },
  {
    name: "API_KEY_CREATE",
    description: "Create API key",
    category: "API Keys",
  },
  {
    name: "API_KEY_LIST",
    description: "List API keys",
    category: "API Keys",
  },
  {
    name: "API_KEY_UPDATE",
    description: "Update API key",
    category: "API Keys",
  },
  {
    name: "API_KEY_DELETE",
    description: "Delete API key",
    category: "API Keys",
    dangerous: true,
  },
  // User tools
  {
    name: "USER_GET",
    description: "Get a user by id",
    category: "Users",
  },
  {
    name: "USER_MODEL_PREFERENCES_GET",
    description: "Get the calling user's chat tier → model overrides",
    category: "Users",
  },
  {
    name: "USER_MODEL_PREFERENCES_UPDATE",
    description: "Set the calling user's chat tier → model overrides",
    category: "Users",
  },
  // Thread tools
  {
    name: "COLLECTION_THREADS_CREATE",
    description: "Create threads",
    category: "Threads",
  },
  {
    name: "COLLECTION_THREADS_LIST",
    description: "List threads",
    category: "Threads",
  },
  {
    name: "COLLECTION_THREADS_GET",
    description: "View thread details",
    category: "Threads",
  },
  {
    name: "COLLECTION_THREADS_UPDATE",
    description: "Update threads",
    category: "Threads",
  },
  {
    name: "COLLECTION_THREADS_DELETE",
    description: "Delete threads",
    category: "Threads",
    dangerous: true,
  },
  {
    name: "COLLECTION_THREAD_MESSAGES_LIST",
    description: "List thread messages",
    category: "Threads",
  },
  // Tag tools
  {
    name: "TAGS_LIST",
    description: "List organization tags",
    category: "Tags",
  },
  {
    name: "TAGS_CREATE",
    description: "Create organization tag",
    category: "Tags",
  },
  {
    name: "TAGS_DELETE",
    description: "Delete organization tag",
    category: "Tags",
    dangerous: true,
  },
  {
    name: "MEMBER_TAGS_GET",
    description: "Get member tags",
    category: "Tags",
  },
  {
    name: "MEMBER_TAGS_SET",
    description: "Set member tags",
    category: "Tags",
  },
  // Automation tools
  {
    name: "AUTOMATION_CREATE",
    description: "Create automation",
    category: "Automations",
  },
  {
    name: "AUTOMATION_GET",
    description: "View automation details",
    category: "Automations",
  },
  {
    name: "AUTOMATION_LIST",
    description: "List automations",
    category: "Automations",
  },
  {
    name: "AUTOMATION_UPDATE",
    description: "Update automation",
    category: "Automations",
  },
  {
    name: "AUTOMATION_DELETE",
    description: "Delete automation",
    category: "Automations",
    dangerous: true,
  },
  {
    name: "AUTOMATION_TRIGGER_ADD",
    description: "Add trigger to automation",
    category: "Automations",
  },
  {
    name: "AUTOMATION_TRIGGER_REMOVE",
    description: "Remove trigger from automation",
    category: "Automations",
  },
  {
    name: "AUTOMATION_TRIGGER_ROTATE_TOKEN",
    description: "Rotate the secret token for a webhook automation trigger",
    category: "Automations",
  },
  {
    name: "AUTOMATION_RUN",
    description: "Manually trigger an automation run",
    category: "Automations",
  },
  {
    name: "AUTOMATION_RUN_STATS",
    description: "View aggregated run counts and token/cost for an automation",
    category: "Automations",
  },
  // Virtual MCP plugin config and pinned views tools
  {
    name: "VIRTUAL_MCP_PLUGIN_CONFIG_GET",
    description: "View virtual MCP plugin configuration",
    category: "Virtual MCPs",
  },
  {
    name: "VIRTUAL_MCP_PLUGIN_CONFIG_UPDATE",
    description: "Update virtual MCP plugin configuration",
    category: "Virtual MCPs",
  },
  {
    name: "VIRTUAL_MCP_PINNED_VIEWS_UPDATE",
    description: "Update virtual MCP pinned sidebar views",
    category: "Virtual MCPs",
  },
  {
    name: "VIRTUAL_MCP_LAST_USED_LIST",
    description: "Get last-used info for one or more virtual MCPs",
    category: "Virtual MCPs",
  },
  {
    name: "AI_PROVIDERS_LIST",
    description: "List available AI providers",
    category: "AI Providers",
  },
  {
    name: "AI_PROVIDERS_LIST_MODELS",
    description: "List AI provider models",
    category: "AI Providers",
  },
  {
    name: "AI_PROVIDERS_ACTIVE",
    description: "List active AI providers",
    category: "AI Providers",
  },
  {
    name: "AI_PROVIDER_KEY_CREATE",
    description: "Store AI provider API key",
    category: "AI Providers",
  },
  {
    name: "AI_PROVIDER_KEY_LIST",
    description: "List AI provider API keys",
    category: "AI Providers",
  },
  {
    name: "AI_PROVIDER_KEY_DELETE",
    description: "Delete AI provider API key",
    category: "AI Providers",
    dangerous: true,
  },
  {
    name: "AI_PROVIDER_KEY_UPDATE",
    description: "Update AI provider API key label or credential",
    category: "AI Providers",
  },
  {
    name: "AI_PROVIDER_KEY_PREVIEW",
    description: "Get masked preview of AI provider API key",
    category: "AI Providers",
  },
  {
    name: "AI_PROVIDER_OAUTH_URL",
    description: "Get OAuth URL for provider",
    category: "AI Providers",
  },
  {
    name: "AI_PROVIDER_OAUTH_EXCHANGE",
    description: "Exchange OAuth code for API key",
    category: "AI Providers",
  },
  {
    name: "AI_PROVIDER_PROVISION_KEY",
    description: "Auto-provision API key for a provider",
    category: "AI Providers",
  },
  {
    name: "AI_PROVIDER_TOPUP_URL",
    description: "Get checkout URL to top up provider credits",
    category: "AI Providers",
  },
  {
    name: "CLAUDE_SUBSCRIPTION_CONNECT",
    description: "Link your own Claude subscription with a setup token",
    category: "AI Providers",
  },
  {
    name: "CLAUDE_SUBSCRIPTION_STATUS",
    description: "Whether your Claude subscription is linked, and since when",
    category: "AI Providers",
  },
  {
    name: "CLAUDE_SUBSCRIPTION_DISCONNECT",
    description: "Unlink your Claude subscription",
    category: "AI Providers",
  },
  {
    name: "AI_PROVIDER_CREDITS",
    description: "Get current credit balance for a provider",
    category: "AI Providers",
  },
  // Secrets tools
  {
    name: "SECRET_CREATE",
    description: "Create a secret in the credential vault",
    category: "Secrets",
  },
  {
    name: "SECRET_LIST",
    description: "List secrets visible to the caller (no values returned)",
    category: "Secrets",
  },
  // File config tools
  {
    name: "FILE_CONFIG_CREATE",
    description: "Create an S3-compatible bucket configuration for the org",
    category: "File Configs",
  },
  {
    name: "FILE_CONFIG_LIST",
    description:
      "List S3 bucket configurations for the org (no creds returned)",
    category: "File Configs",
  },
  {
    name: "ORG_FS_PUBLIC_SETS_SYNC",
    description:
      "Re-sync the shared public skill-set volumes from their GitHub sources",
    category: "File Configs",
  },
  {
    name: "FILE_CONFIG_UPDATE",
    description:
      "Update an S3 bucket configuration, optionally rotating credentials",
    category: "File Configs",
  },
  {
    name: "FILE_CONFIG_DELETE",
    description: "Delete an S3 bucket configuration",
    category: "File Configs",
  },
  {
    name: "FILE_OBJECTS_LIST",
    description: "List existing objects in a configured bucket",
    category: "File Configs",
  },

  // Object Storage tools
  {
    name: "LIST_OBJECTS",
    description: "List objects in storage",
    category: "Object Storage",
  },
  {
    name: "GET_OBJECT_METADATA",
    description: "Get object metadata",
    category: "Object Storage",
  },
  {
    name: "GET_PRESIGNED_URL",
    description: "Generate download URL",
    category: "Object Storage",
  },
  {
    name: "PUT_PRESIGNED_URL",
    description: "Generate upload URL",
    category: "Object Storage",
  },
  {
    name: "DELETE_OBJECT",
    description: "Delete object",
    category: "Object Storage",
    dangerous: true,
  },
  {
    name: "DELETE_OBJECTS",
    description: "Delete multiple objects",
    category: "Object Storage",
    dangerous: true,
  },
  // Registry tools
  {
    name: "COLLECTION_REGISTRY_APP_LIST",
    description: "List registry apps",
    category: "Registry",
  },
  {
    name: "COLLECTION_REGISTRY_APP_GET",
    description: "Get registry app details",
    category: "Registry",
  },
  {
    name: "COLLECTION_REGISTRY_APP_VERSIONS",
    description: "List registry app versions",
    category: "Registry",
  },
  {
    name: "COLLECTION_REGISTRY_APP_FILTERS",
    description: "Get registry app filters",
    category: "Registry",
  },
  {
    name: "REGISTRY_ITEM_LIST",
    description: "List private registry items",
    category: "Registry",
  },
  {
    name: "REGISTRY_ITEM_SEARCH",
    description: "Search registry items",
    category: "Registry",
  },
  {
    name: "REGISTRY_ITEM_GET",
    description: "Get registry item details",
    category: "Registry",
  },
  {
    name: "REGISTRY_ITEM_VERSIONS",
    description: "List registry item versions",
    category: "Registry",
  },
  {
    name: "REGISTRY_ITEM_CREATE",
    description: "Create registry item",
    category: "Registry",
  },
  {
    name: "REGISTRY_ITEM_BULK_CREATE",
    description: "Bulk create registry items",
    category: "Registry",
  },
  {
    name: "REGISTRY_ITEM_UPDATE",
    description: "Update registry item",
    category: "Registry",
  },
  {
    name: "REGISTRY_ITEM_DELETE",
    description: "Delete registry item",
    category: "Registry",
    dangerous: true,
  },
  {
    name: "REGISTRY_ITEM_FILTERS",
    description: "Get registry item filters",
    category: "Registry",
  },
  {
    name: "REGISTRY_DISCOVER_TOOLS",
    description: "Discover tools from MCP server",
    category: "Registry",
  },
  {
    name: "REGISTRY_AI_GENERATE",
    description: "AI-generate registry content",
    category: "Registry",
  },
  {
    name: "REGISTRY_PUBLISH_REQUEST_LIST",
    description: "List publish requests",
    category: "Registry",
  },
  {
    name: "REGISTRY_PUBLISH_REQUEST_REVIEW",
    description: "Review publish request",
    category: "Registry",
  },
  {
    name: "REGISTRY_PUBLISH_REQUEST_COUNT",
    description: "Count pending publish requests",
    category: "Registry",
  },
  {
    name: "REGISTRY_PUBLISH_REQUEST_DELETE",
    description: "Delete publish request",
    category: "Registry",
    dangerous: true,
  },
  {
    name: "REGISTRY_PUBLISH_API_KEY_GENERATE",
    description: "Generate publish API key",
    category: "Registry",
  },
  {
    name: "REGISTRY_PUBLISH_API_KEY_LIST",
    description: "List publish API keys",
    category: "Registry",
  },
  {
    name: "REGISTRY_PUBLISH_API_KEY_REVOKE",
    description: "Revoke publish API key",
    category: "Registry",
    dangerous: true,
  },
  {
    name: "REGISTRY_MONITOR_RUN_START",
    description: "Start monitor run",
    category: "Registry",
  },
  {
    name: "REGISTRY_MONITOR_RUN_LIST",
    description: "List monitor runs",
    category: "Registry",
  },
  {
    name: "REGISTRY_MONITOR_RUN_GET",
    description: "Get monitor run details",
    category: "Registry",
  },
  {
    name: "REGISTRY_MONITOR_RUN_CANCEL",
    description: "Cancel monitor run",
    category: "Registry",
  },
  {
    name: "REGISTRY_MONITOR_RESULT_LIST",
    description: "List monitor results",
    category: "Registry",
  },
  {
    name: "REGISTRY_MONITOR_CONNECTION_LIST",
    description: "List monitor connections",
    category: "Registry",
  },
  {
    name: "REGISTRY_MONITOR_CONNECTION_SYNC",
    description: "Sync monitor connections",
    category: "Registry",
  },
  {
    name: "REGISTRY_MONITOR_CONNECTION_UPDATE_AUTH",
    description: "Update monitor connection auth",
    category: "Registry",
  },
  {
    name: "SANDBOX_START",
    description: "Start a sandbox VM with dev server preview",
    category: "VM",
  },
  {
    name: "SANDBOX_DELETE",
    description: "Stop and delete a sandbox VM",
    category: "VM",
  },
  {
    name: "GITHUB_LIST_USER_ORGS",
    description: "List GitHub user's personal account and organizations",
    category: "GitHub",
  },
  // Search tools
  {
    name: "GLOBAL_SEARCH",
    description:
      "Search across organization resources (currently threads). Returns a typed union of matches.",
    category: "Search",
  },
  // Task board tools
  {
    name: "TASK_BOARD_ITEM_CREATE",
    description: "Create task board item",
    category: "Task Board",
  },
  {
    name: "TASK_BOARD_ITEM_LIST",
    description: "List task board items",
    category: "Task Board",
  },
  {
    name: "TASK_BOARD_ITEM_UPDATE",
    description: "Update task board item",
    category: "Task Board",
  },
  {
    name: "TASK_BOARD_ITEM_DELETE",
    description: "Delete task board item",
    category: "Task Board",
    dangerous: true,
  },
  {
    name: "TASK_BOARD_ITEM_PRS_GET",
    description: "Get a task board item's linked pull requests with live state",
    category: "Task Board",
  },
  {
    name: "TASK_BOARD_ITEM_PR_LINK",
    description: "Link a pull request a task run opened to its task board item",
    category: "Task Board",
  },
  {
    name: "TASK_BOARD_ITEM_RERUN",
    description:
      "Re-run a task with the Super Agent, failing any run still holding it open",
    category: "Task Board",
    dangerous: true,
  },
  {
    name: "TASK_BOARD_REVIEW_DECISION",
    description:
      "Record a reviewer's decision (approve or request changes) for a task under review",
    category: "Task Board",
    dangerous: true,
  },
  {
    name: "TASK_BOARD_PROMOTE_TO_PRODUCTION",
    description:
      "Merge a reviewed task's pull request and move the task to Done",
    category: "Task Board",
    dangerous: true,
  },
  {
    name: "TASK_BOARD_ACTIVITY_LIST",
    description: "List a task board item's change history",
    category: "Task Board",
  },
  {
    name: "TASK_BOARD_COMMENT_LIST",
    description: "List a task board item's comment threads",
    category: "Task Board",
  },
  {
    name: "TASK_BOARD_COMMENT_CREATE",
    description: "Post a comment or a reply on a task board item",
    category: "Task Board",
  },
  {
    name: "TASK_BOARD_COMMENT_UPDATE",
    description: "Edit a comment, or resolve/unresolve a comment thread",
    category: "Task Board",
  },
  {
    name: "TASK_BOARD_COMMENT_DELETE",
    description: "Delete a comment (a thread root takes its replies)",
    category: "Task Board",
  },
  {
    name: "TASK_BOARD_DISMISSED_LIST",
    description:
      "List diagnostic findings dismissed by deleting their task board cards",
    category: "Task Board",
  },
  {
    name: "TASK_BOARD_DISMISSED_RESTORE",
    description:
      "Un-dismiss diagnostic findings so the next report import pushes them again",
    category: "Task Board",
  },
  {
    name: "TASK_ADD_REPO",
    description:
      "Clone an organization repository into the sandbox of the task run calling it",
    category: "Task Board",
  },
];

// ============================================================================
// Permission Capabilities (high-level, user-facing permissions)
// ============================================================================

/**
 * A gated permission capability — a user-facing feature grouping that maps to a set of MCP tools.
 * Used for role editing and OAuth consent screens.
 */
export interface PermissionCapability {
  id: string;
  label: string;
  description: string;
  section: string;
  tools: ToolName[];
  dangerous?: boolean;
}

/**
 * Capability id for tools all authenticated org members can use by default.
 * The role editor hides this capability and bakes its tools into every
 * custom role's saved permission set at submit time.
 */
const BASIC_USAGE_CAPABILITY_ID = "basic-usage";

const PERMISSION_CAPABILITIES: PermissionCapability[] = [
  // Basic usage — granted to all org members, hidden from UI
  {
    id: BASIC_USAGE_CAPABILITY_ID,
    label: "Basic Usage",
    description: "Tools all org members can access by default",
    section: "Basic Usage",
    tools: [
      // View connections
      "COLLECTION_CONNECTIONS_LIST",
      "COLLECTION_CONNECTIONS_GET",
      "CONNECTION_TEST",
      // View agents
      "COLLECTION_VIRTUAL_MCP_LIST",
      "COLLECTION_VIRTUAL_MCP_GET",
      "VIRTUAL_MCP_PLUGIN_CONFIG_GET",
      "VIRTUAL_MCP_LAST_USED_LIST",
      // View automations
      "AUTOMATION_GET",
      "AUTOMATION_LIST",
      "AUTOMATION_RUN_STATS",
      // View AI providers (read-only — every member needs to know which
      // providers are configured so chat / agents can use them). KEY_LIST
      // returns metadata only (no secret material); CREDITS is the balance
      // shown in the chat header. TOPUP_URL returns a checkout link surfaced
      // in the chat credits-exhausted banner so any member can self-serve.
      "AI_PROVIDERS_LIST",
      "AI_PROVIDERS_LIST_MODELS",
      "AI_PROVIDERS_ACTIVE",
      "AI_PROVIDER_KEY_LIST",
      "AI_PROVIDER_CREDITS",
      "AI_PROVIDER_TOPUP_URL",
      // Object storage access
      "LIST_OBJECTS",
      "GET_OBJECT_METADATA",
      "GET_PRESIGNED_URL",
      "PUT_PRESIGNED_URL",
      // Browse files in a configured bucket (file picker in the sandbox /
      // content editor). Lists object keys only — no credentials returned.
      "FILE_OBJECTS_LIST",
      // Sandbox previews
      "SANDBOX_START",
      "SANDBOX_DELETE",
      // Cross-resource discovery / command palette
      "GLOBAL_SEARCH",
      // App-shell essentials (read-only) — every member hits these on first
      // paint or in the global chrome:
      //   SETTINGS_GET → sidebar / plugins / model tiers loaded at shell boot
      //   USER_GET     → resolve member display ("created by" on agents, etc.);
      //                  handler scopes to shared-org members, no secrets
      //   BRAND_CONTEXT_LIST → org branding for the chat empty state
      "ORGANIZATION_SETTINGS_GET",
      "USER_GET",
      "BRAND_CONTEXT_LIST",
      // Chat threads — talking to an agent is the most basic usage of the
      // product, so every member can CRUD their OWN threads. Per-thread access
      // is scoped at the handler level (you only see your own threads unless
      // you also hold the threads:view-all capability).
      "COLLECTION_THREADS_CREATE",
      "COLLECTION_THREADS_LIST",
      "COLLECTION_THREADS_GET",
      "COLLECTION_THREADS_UPDATE",
      "COLLECTION_THREADS_DELETE",
      "COLLECTION_THREAD_MESSAGES_LIST",
      // Task board — org-scoped, usable by every member, same basic-usage
      // tier as chat threads.
      "TASK_BOARD_ITEM_CREATE",
      "TASK_BOARD_ITEM_LIST",
      "TASK_BOARD_ITEM_UPDATE",
      "TASK_BOARD_ITEM_DELETE",
      "TASK_BOARD_ITEM_PRS_GET",
      "TASK_BOARD_ITEM_PR_LINK",
      "TASK_BOARD_ITEM_RERUN",
      "TASK_BOARD_REVIEW_DECISION",
      "TASK_BOARD_PROMOTE_TO_PRODUCTION",
      "TASK_BOARD_ACTIVITY_LIST",
      "TASK_BOARD_COMMENT_LIST",
      "TASK_BOARD_COMMENT_CREATE",
      "TASK_BOARD_COMMENT_UPDATE",
      "TASK_BOARD_COMMENT_DELETE",
      "TASK_BOARD_DISMISSED_LIST",
      "TASK_BOARD_DISMISSED_RESTORE",
    ],
  },
  // Organization
  {
    id: "org:manage",
    label: "Manage organization",
    description:
      "Edit organization settings, brand context, and domain configuration",
    section: "Organization",
    tools: [
      "ORGANIZATION_GET",
      "ORGANIZATION_LIST",
      "ORGANIZATION_UPDATE",
      "ORGANIZATION_SETTINGS_GET",
      "ORGANIZATION_SETTINGS_UPDATE",
      "BRAND_CONTEXT_LIST",
      "BRAND_CONTEXT_GET",
      "BRAND_CONTEXT_CREATE",
      "BRAND_CONTEXT_UPDATE",
      "BRAND_CONTEXT_DELETE",
      "BRAND_CONTEXT_EXTRACT",
      "BRAND_GET",
      "BRAND_LIST",
      "ORGANIZATION_DOMAIN_LIST",
      "ORGANIZATION_DOMAIN_ADD",
      "ORGANIZATION_DOMAIN_UPDATE",
      "ORGANIZATION_DOMAIN_VERIFY",
      "ORGANIZATION_DOMAIN_REMOVE",
    ],
  },
  {
    id: "members:manage",
    label: "Manage members",
    description: "Invite members, remove them, and change their roles",
    section: "Organization",
    tools: [
      "ORGANIZATION_MEMBER_LIST",
      "ORGANIZATION_MEMBER_ADD",
      "ORGANIZATION_MEMBER_REMOVE",
      "ORGANIZATION_MEMBER_UPDATE_ROLE",
      // Seats live on the members page and change who the org pays for —
      // same trust tier as adding/removing the member itself. Checkout and
      // preview are the money half of the same surface.
      "ORGANIZATION_BILLING_CHECKOUT_START",
      "ORGANIZATION_BILLING_PORTAL",
      "ORGANIZATION_TASK_QUOTA_GET",
      // Approving/denying join requests adds members, and the UI lives on the
      // members page — keep it under members:manage.
      "ORGANIZATION_JOIN_REQUEST_LIST",
      "ORGANIZATION_JOIN_REQUEST_APPROVE",
      "ORGANIZATION_JOIN_REQUEST_DENY",
    ],
    dangerous: true,
  },
  // Connections
  {
    id: "connections:manage",
    label: "Manage connections",
    description: "Create, update, and delete connections",
    section: "Connections & Agents",
    tools: [
      "COLLECTION_CONNECTIONS_CREATE",
      "COLLECTION_CONNECTIONS_UPDATE",
      "COLLECTION_CONNECTIONS_DELETE",
      "COMMERCE_DISCOVERY_SETUP",
      "COMMERCE_DISCOVERY_RUN",
      "COMMERCE_DISCOVERY_BIND",
      "COMMERCE_DISCOVERY_CONNECTION_STATUS",
    ],
    dangerous: true,
  },
  {
    id: "agents:manage",
    label: "Manage agents",
    description: "Create, configure, and delete agents",
    section: "Connections & Agents",
    tools: [
      "COLLECTION_VIRTUAL_MCP_CREATE",
      "COLLECTION_VIRTUAL_MCP_UPDATE",
      "COLLECTION_VIRTUAL_MCP_DELETE",
      "VIRTUAL_MCP_PLUGIN_CONFIG_UPDATE",
      "VIRTUAL_MCP_PINNED_VIEWS_UPDATE",
    ],
    dangerous: true,
  },
  // Automations
  {
    id: "automations:manage",
    label: "Manage automations",
    description: "Create, update, run, and delete automations",
    section: "Automations",
    tools: [
      "AUTOMATION_CREATE",
      "AUTOMATION_UPDATE",
      "AUTOMATION_DELETE",
      "AUTOMATION_TRIGGER_ADD",
      "AUTOMATION_TRIGGER_REMOVE",
      "AUTOMATION_TRIGGER_ROTATE_TOKEN",
      "AUTOMATION_RUN",
    ],
    dangerous: true,
  },
  // Monitoring
  {
    id: "monitoring:view",
    label: "View monitoring",
    description: "Access logs and usage statistics",
    section: "Monitoring",
    tools: [
      "MONITORING_LOG_GET",
      "MONITORING_LOGS_LIST",
      "MONITORING_STATS",
      "MONITORING_THREAD_USAGE",
    ],
  },
  // Secrets
  {
    id: "secrets:manage",
    label: "Manage secrets",
    description: "Create and list secrets stored in the credential vault",
    section: "Organization",
    tools: ["SECRET_CREATE", "SECRET_LIST"],
  },
  // File Configs
  {
    id: "file-configs:manage",
    label: "Manage file configs",
    description: "Create, list, update and delete S3 bucket configurations",
    section: "Organization",
    tools: [
      "FILE_CONFIG_CREATE",
      "FILE_CONFIG_LIST",
      "FILE_CONFIG_UPDATE",
      "FILE_CONFIG_DELETE",
      "FILE_OBJECTS_LIST",
    ],
  },
  // AI Providers
  {
    id: "ai-providers:manage",
    label: "Manage AI providers",
    description: "Add or remove API keys and provision provider credentials",
    section: "AI Providers",
    tools: [
      "AI_PROVIDER_KEY_CREATE",
      "AI_PROVIDER_KEY_LIST",
      "AI_PROVIDER_KEY_DELETE",
      "AI_PROVIDER_KEY_UPDATE",
      "AI_PROVIDER_KEY_PREVIEW",
      "AI_PROVIDER_OAUTH_URL",
      "AI_PROVIDER_OAUTH_EXCHANGE",
      "AI_PROVIDER_PROVISION_KEY",
      "AI_PROVIDER_TOPUP_URL",
      "AI_PROVIDER_CREDITS",
      "CLAUDE_SUBSCRIPTION_CONNECT",
      "CLAUDE_SUBSCRIPTION_STATUS",
      "CLAUDE_SUBSCRIPTION_DISCONNECT",
    ],
  },
  // Organization (tags moved here from Developer)
  {
    id: "tags:manage",
    label: "Manage tags",
    description: "Create, assign, and delete organization tags",
    section: "Organization",
    tools: [
      "TAGS_LIST",
      "TAGS_CREATE",
      "TAGS_DELETE",
      "MEMBER_TAGS_GET",
      "MEMBER_TAGS_SET",
    ],
  },
  // Store & Registry
  {
    id: "registry:manage",
    label: "Manage registry",
    description: "Browse, publish, and manage items in the registry",
    section: "Store & Registry",
    tools: [
      "COLLECTION_REGISTRY_APP_LIST",
      "COLLECTION_REGISTRY_APP_GET",
      "COLLECTION_REGISTRY_APP_VERSIONS",
      "COLLECTION_REGISTRY_APP_FILTERS",
      "REGISTRY_ITEM_LIST",
      "REGISTRY_ITEM_SEARCH",
      "REGISTRY_ITEM_GET",
      "REGISTRY_ITEM_VERSIONS",
      "REGISTRY_ITEM_FILTERS",
      "REGISTRY_DISCOVER_TOOLS",
      "REGISTRY_ITEM_CREATE",
      "REGISTRY_ITEM_BULK_CREATE",
      "REGISTRY_ITEM_UPDATE",
      "REGISTRY_ITEM_DELETE",
      "REGISTRY_AI_GENERATE",
      "REGISTRY_PUBLISH_REQUEST_LIST",
      "REGISTRY_PUBLISH_REQUEST_REVIEW",
      "REGISTRY_PUBLISH_REQUEST_COUNT",
      "REGISTRY_PUBLISH_REQUEST_DELETE",
      "REGISTRY_PUBLISH_API_KEY_GENERATE",
      "REGISTRY_PUBLISH_API_KEY_LIST",
      "REGISTRY_PUBLISH_API_KEY_REVOKE",
    ],
    dangerous: true,
  },
  {
    id: "registry:monitor",
    label: "Monitor registry health",
    description: "Run health checks on registry connections and view results",
    section: "Store & Registry",
    tools: [
      "REGISTRY_MONITOR_RUN_START",
      "REGISTRY_MONITOR_RUN_LIST",
      "REGISTRY_MONITOR_RUN_GET",
      "REGISTRY_MONITOR_RUN_CANCEL",
      "REGISTRY_MONITOR_RESULT_LIST",
      "REGISTRY_MONITOR_CONNECTION_LIST",
      "REGISTRY_MONITOR_CONNECTION_SYNC",
      "REGISTRY_MONITOR_CONNECTION_UPDATE_AUTH",
    ],
  },
  // Developer
  {
    id: "api-keys:manage",
    label: "Manage API keys",
    description: "Create, update, and revoke API keys",
    section: "Developer",
    tools: [
      "API_KEY_CREATE",
      "API_KEY_LIST",
      "API_KEY_UPDATE",
      "API_KEY_DELETE",
    ],
  },
  {
    id: "storage:delete",
    label: "Delete from storage",
    description: "Permanently delete files from object storage",
    section: "Developer",
    tools: ["DELETE_OBJECT", "DELETE_OBJECTS"],
    dangerous: true,
  },
  {
    id: "connections:sql",
    label: "Run SQL queries",
    description: "Execute raw SQL against connected databases",
    section: "Developer",
    tools: ["DATABASES_RUN_SQL"],
    dangerous: true,
  },
];

/**
 * Tools every authenticated org member can use by default, plus non-MCP resource keys.
 *
 * Granted at runtime by AccessControl (`checkResource`) to every member
 * regardless of role — NOT persisted into stored role permissions. To change
 * the set, edit the `basic-usage` capability above; it takes effect
 * immediately for all existing and new roles, with no backfill migration.
 *
 * (Migrations 073 and 093 backfilled basic-usage tools into roles under the
 * older bake-in model. They are now redundant but harmless — the runtime
 * grant supersedes them. No new backfill migrations are needed.)
 */
export const BASIC_USAGE_TOOLS: ReadonlySet<string> = new Set([
  ...(PERMISSION_CAPABILITIES.find((c) => c.id === BASIC_USAGE_CAPABILITY_ID)
    ?.tools ?? []),
  // Organization filesystem HTTP-API resource keys (NOT MCP tools, so they
  // can't live in the typed capability `tools` list). Decision A: every org
  // member may read+write every volume. Moving to per-volume ACL later =
  // drop these from the basic set and gate by role/permission, no route
  // changes. See `.context/org-filesystem-proposal.md`.
  "ORG_FS_READ",
  "ORG_FS_WRITE",
]);

/**
 * Gated capability ids additionally granted to the built-in `user` role, beyond
 * basic-usage. EMPTY by default — every member is otherwise enforced down to
 * basic-usage only. Add a capability id here (e.g. "agents:manage") to grant ALL
 * of its tools to every member of every org. This is a GLOBAL change with no
 * migration; for per-org grants use a custom role instead.
 */
const USER_ROLE_CAPABILITY_IDS: string[] = [
  "agents:manage",
  "connections:manage",
];

/**
 * Tools the built-in `user` role gets beyond basic-usage, derived from
 * USER_ROLE_CAPABILITY_IDS. Single source of truth for the two layers that must
 * stay in sync:
 *   - enforcement: baked into the `user` role's `self` grant (auth/index.ts)
 *   - UI gating: the MY_CAPABILITIES endpoint (api/routes/auth.ts)
 *
 * List specific tool names only — never `"*"` — so the wildcard fallback in
 * `createBoundAuthClient` can't be tricked into granting everything.
 */
export const USER_ROLE_TOOLS: ReadonlySet<string> = new Set(
  PERMISSION_CAPABILITIES.filter((c) =>
    USER_ROLE_CAPABILITY_IDS.includes(c.id),
  ).flatMap((c) => c.tools),
);

/**
 * Group gated capabilities by their UI section (e.g., "Organization", "Automations").
 * Used to render the role editor's capability picker.
 */
export function getCapabilitySections(): Array<{
  section: string;
  capabilities: PermissionCapability[];
}> {
  const map = new Map<string, PermissionCapability[]>();
  for (const cap of PERMISSION_CAPABILITIES) {
    if (cap.id === BASIC_USAGE_CAPABILITY_ID) continue;
    const arr = map.get(cap.section) ?? [];
    arr.push(cap);
    map.set(cap.section, arr);
  }
  return Array.from(map.entries()).map(([section, capabilities]) => ({
    section,
    capabilities,
  }));
}

/**
 * Check whether all tools in a capability are granted.
 */
export function isCapabilityEnabled(
  cap: PermissionCapability,
  enabledTools: string[],
  allowAll: boolean,
): boolean {
  if (allowAll) return true;
  return cap.tools.every((tool) => enabledTools.includes(tool));
}

/**
 * Enable or disable all tools in a capability within a tool set.
 */
export function toggleCapabilityInTools(
  cap: PermissionCapability,
  currentTools: string[],
  enable: boolean,
): string[] {
  if (enable) {
    const toolSet = new Set(currentTools);
    for (const tool of cap.tools) toolSet.add(tool);
    return Array.from(toolSet);
  }
  const toolSet = new Set(currentTools);
  for (const tool of cap.tools) toolSet.delete(tool);
  return Array.from(toolSet);
}

/**
 * Map of gated capability id → granted, for the privileged built-in roles
 * (owner / admin) which bypass every permission check. The hidden basic-usage
 * capability is never part of the map — it's always-on and not UI-gated.
 */
export function allCapabilitiesGranted(): Record<string, boolean> {
  const out: Record<string, boolean> = {};
  for (const cap of PERMISSION_CAPABILITIES) {
    if (cap.id === BASIC_USAGE_CAPABILITY_ID) continue;
    out[cap.id] = true;
  }
  return out;
}

/**
 * Resolve which gated capabilities a stored role permission grants, as a
 * capability id → boolean map. Pure: mirrors the wildcard rules AccessControl
 * applies, so the UI can gate actions without re-deriving role logic.
 *
 * A capability is granted when every tool it lists appears in some resource
 * bucket of the permission, or when the role holds an org-wide / self wildcard.
 * The hidden basic-usage capability is excluded (always-on, never gated).
 *
 * Defensive by design: `permission` is JSON stored on the role, so buckets may
 * be malformed despite the type — non-array buckets are treated as empty and
 * never throw.
 */
export function resolveCapabilities(
  permission: Record<string, string[]>,
): Record<string, boolean> {
  const arrayBucket = (key: string): string[] => {
    const value = permission[key];
    return Array.isArray(value) ? value : [];
  };

  // ONLY an org-wide (`*`) or full org-tool (`self`) wildcard is a GLOBAL grant
  // across every capability. A `["*"]` inside any other bucket (e.g. a
  // connection id) means "all tools on that resource" and must NOT light up
  // unrelated management capabilities.
  const hasGlobalGrant =
    arrayBucket("*").includes("*") || arrayBucket("self").includes("*");

  const grantedActions = new Set<string>();
  if (!hasGlobalGrant) {
    for (const actions of Object.values(permission)) {
      if (!Array.isArray(actions)) continue;
      for (const action of actions) grantedActions.add(action);
    }
  }

  const out: Record<string, boolean> = {};
  for (const cap of PERMISSION_CAPABILITIES) {
    if (cap.id === BASIC_USAGE_CAPABILITY_ID) continue;
    out[cap.id] =
      hasGlobalGrant || cap.tools.every((tool) => grantedActions.has(tool));
  }
  return out;
}

// ============================================================================
// Exports
// ============================================================================

/**
 * Get tools grouped by category
 */
export function getToolsByCategory(): Record<ToolCategory, ToolMetadata[]> {
  const grouped: Record<ToolCategory, ToolMetadata[]> = {
    Organizations: [],
    Connections: [],
    "Virtual MCPs": [],
    Threads: [],
    Monitoring: [],
    Users: [],
    "API Keys": [],
    Tags: [],
    "AI Providers": [],
    Secrets: [],
    "File Configs": [],
    Automations: [],
    "Object Storage": [],
    Registry: [],
    GitHub: [],
    VM: [],
    Search: [],
    "Task Board": [],
  };

  for (const tool of MANAGEMENT_TOOLS) {
    grouped[tool.category].push(tool);
  }

  return grouped;
}
