/**
 * Permission Nodes - Centralized definition of all permission nodes
 * organized by category with descriptions and usage information.
 *
 * Format: resource.action[.modifier]
 * Special tokens: * (matches all), ? (single char wildcard), [a,b,c] (inclusive), <a,b,c> (exclusive)
 */

export interface PermissionNode {
  /** The permission node identifier (e.g., "company.fetch") */
  node: string;
  /** Description of what this permission allows */
  description: string;
  /** File paths where this permission is used */
  usedIn: string[];
  /** Dependencies - other permissions that must be granted alongside this one */
  dependencies?: string[];
  /**
   * When present, indicates this permission gates individual fields on the
   * response object via `processObjectValuePerms`. Each entry is a full
   * permission node in the form `<scope>.<fieldName>`. Only fields whose
   * corresponding permission the user holds are included in the response.
   */
  fieldLevelPermissions?: string[];
}

export interface PermissionCategory {
  /** Category name */
  name: string;
  /** Category description */
  description: string;
  /** Permission nodes in this category */
  permissions: PermissionNode[];
  /** Optional nested sub-categories for hierarchical grouping */
  subCategories?: Record<string, PermissionCategory>;
}

export const PERMISSION_NODES = {
  global: {
    name: "Global Permissions",
    description:
      "Global wildcard permissions that grant access to all resources",
    permissions: [
      {
        node: "*",
        description:
          "Full access to all resources and actions (administrator role)",
        usedIn: [],
      },
    ],
  },

  company: {
    name: "Company Permissions",
    description: "Permissions for accessing and managing company resources",
    permissions: [
      {
        node: "company.fetch",
        description: "Fetch a single company",
        usedIn: ["src/api/companies/[id]/fetch.ts"],
      },
      {
        node: "company.fetch.address",
        description: "View company address information",
        usedIn: ["src/api/companies/[id]/fetch.ts"],
        dependencies: ["company.fetch"],
      },
      {
        node: "company.fetch.contacts",
        description: "View all company contacts",
        usedIn: ["src/api/companies/[id]/fetch.ts"],
        dependencies: ["company.fetch"],
      },
      {
        node: "company.fetch.many",
        description: "Fetch multiple companies",
        usedIn: ["src/api/companies/fetchAll.ts"],
      },
      {
        node: "company.fetch.configurations",
        description: "Fetch company configurations",
        usedIn: ["src/api/companies/[id]/configurations.ts"],
        dependencies: ["company.fetch"],
      },
      {
        node: "company.fetch.sites",
        description: "Fetch company sites from ConnectWise",
        usedIn: ["src/api/companies/[id]/sites.ts"],
        dependencies: ["company.fetch"],
      },
    ],
  },

  credential: {
    name: "Credential Permissions",
    description: "Permissions for managing credentials and their fields",
    permissions: [
      {
        node: "credential.create",
        description: "Create a new credential",
        usedIn: ["src/api/credentials/create.ts"],
      },
      {
        node: "credential.fetch",
        description: "Fetch a single credential",
        usedIn: ["src/api/credentials/fetch.ts"],
      },
      {
        node: "credential.fetch.many",
        description: "Fetch multiple credentials",
        usedIn: ["src/api/credentials/fetchByCompany.ts"],
      },
      {
        node: "credential.update",
        description: "Update a credential",
        usedIn: ["src/api/credentials/update.ts"],
      },
      {
        node: "credential.delete",
        description: "Delete a credential",
        usedIn: ["src/api/credentials/delete.ts"],
      },
      {
        node: "credential.fields.fetch",
        description: "Fetch credential fields",
        usedIn: ["src/api/credentials/fetchFields.ts"],
        dependencies: ["credential.fetch"],
      },
      {
        node: "credential.fields.update",
        description: "Update credential fields",
        usedIn: ["src/api/credentials/updateFields.ts"],
        dependencies: ["credential.update"],
      },
      {
        node: "credential.secure_values.read",
        description: "Read secure values of a credential",
        usedIn: [
          "src/api/credentials/readSecureValues.ts",
          "src/api/credentials/readSecureValue.ts",
        ],
        dependencies: ["credential.fetch"],
      },
      {
        node: "credential.sub_credentials.fetch",
        description: "Fetch sub-credentials of a parent credential",
        usedIn: ["src/api/credentials/fetchSubCredentials.ts"],
        dependencies: ["credential.fetch"],
      },
      {
        node: "credential.sub_credentials.create",
        description: "Create a sub-credential on a parent credential",
        usedIn: ["src/api/credentials/addSubCredential.ts"],
        dependencies: ["credential.fetch"],
      },
      {
        node: "credential.sub_credentials.delete",
        description: "Remove a sub-credential from a parent credential",
        usedIn: ["src/api/credentials/removeSubCredential.ts"],
        dependencies: ["credential.fetch"],
      },
    ],
  },

  credentialType: {
    name: "Credential Type Permissions",
    description: "Permissions for managing credential types and definitions",
    permissions: [
      {
        node: "credential_type.create",
        description: "Create a new credential type",
        usedIn: ["src/api/credential-types/create.ts"],
      },
      {
        node: "credential_type.fetch",
        description: "Fetch a single credential type",
        usedIn: ["src/api/credential-types/fetch.ts"],
      },
      {
        node: "credential_type.fetch.many",
        description: "Fetch multiple credential types",
        usedIn: ["src/api/credential-types/fetchAll.ts"],
      },
      {
        node: "credential_type.update",
        description: "Update a credential type",
        usedIn: ["src/api/credential-types/update.ts"],
      },
      {
        node: "credential_type.delete",
        description: "Delete a credential type",
        usedIn: ["src/api/credential-types/delete.ts"],
      },
    ],
  },

  permission: {
    name: "Permission Permissions",
    description: "Permissions for viewing permission node definitions",
    permissions: [
      {
        node: "role.read",
        description: "Fetch all permission nodes organized by category",
        usedIn: ["src/api/permissions/fetchAll.ts"],
      },
      {
        node: "role.read",
        description: "Fetch a flat list of all permission nodes",
        usedIn: ["src/api/permissions/fetchNodes.ts"],
      },
      {
        node: "role.read",
        description: "Fetch permission nodes by category",
        usedIn: ["src/api/permissions/fetchByCategory.ts"],
      },
    ],
  },

  role: {
    name: "Role Permissions",
    description: "Permissions for managing roles and role assignments",
    permissions: [
      {
        node: "role.create",
        description: "Create a new role",
        usedIn: ["src/api/roles/create.ts"],
      },
      {
        node: "role.read",
        description: "Fetch a single role or view role information",
        usedIn: ["src/api/roles/fetch.ts"],
      },
      {
        node: "role.list",
        description: "Fetch all roles",
        usedIn: ["src/api/roles/fetchAll.ts"],
        dependencies: ["role.read"],
      },
      {
        node: "role.update",
        description: "Update role properties or manage role permissions",
        usedIn: [
          "src/api/roles/update.ts",
          "src/api/roles/addPermissions.ts",
          "src/api/roles/removePermissions.ts",
        ],
      },
      {
        node: "role.delete",
        description: "Delete a role",
        usedIn: ["src/api/roles/delete.ts"],
      },
      {
        node: "user.read",
        description: "View users assigned to a role",
        usedIn: ["src/api/roles/getUsers.ts"],
        dependencies: ["role.read"],
      },
    ],
  },

  user: {
    name: "User Permissions",
    description: "Permissions for user profile and information management",
    permissions: [
      {
        node: "user.read",
        description: "Read user information",
        usedIn: ["src/api/user/@me/fetch.ts"],
      },
      {
        node: "user.write",
        description: "Update user information",
        usedIn: ["src/api/user/@me/update.ts"],
      },
      {
        node: "user.read.other",
        description: "Read other users' information",
        usedIn: [
          "src/api/user/fetch.ts",
          "src/api/user/fetchRoles.ts",
          "src/api/user/checkPermission.ts",
        ],
      },
      {
        node: "user.list.other",
        description: "List all users",
        usedIn: ["src/api/user/fetchAll.ts"],
        dependencies: ["user.read.other"],
      },
      {
        node: "user.write.other",
        description: "Update other users' information",
        usedIn: ["src/api/user/update.ts"],
      },
      {
        node: "user.roles.other",
        description: "Modify roles assigned to other users",
        usedIn: ["src/api/user/update.ts"],
        dependencies: ["user.write.other"],
      },
      {
        node: "user.permissions.other",
        description: "Modify direct permissions assigned to other users",
        usedIn: ["src/api/user/update.ts"],
        dependencies: ["user.write.other"],
      },
      {
        node: "user.delete.other",
        description: "Delete other users",
        usedIn: ["src/api/user/delete.ts"],
      },
    ],
  },

  uiNavigation: {
    name: "UI Navigation Permissions",
    description: "Permissions for controlling navigation visibility",
    permissions: [
      {
        node: "ui.navigation.*.view",
        description:
          "View specific navigation sections (e.g., ui.navigation.admin.view)",
        usedIn: [],
      },
    ],
  },

  adminUI: {
    name: "Admin UI Permissions",
    description:
      "Admin-specific UI permissions that control visibility of admin sub-tabs",
    permissions: [
      {
        node: "admin.users.view",
        description: "Show the Users tab and load user data",
        usedIn: [],
      },
      {
        node: "admin.roles.view",
        description: "Show the Roles tab and load role data",
        usedIn: [],
      },
      {
        node: "admin.credential-types.view",
        description: "Show the Credential Types tab and load type data",
        usedIn: [],
      },
    ],
  },

  procurement: {
    name: "Procurement Permissions",
    description:
      "Permissions for accessing and managing procurement catalog items",
    permissions: [
      {
        node: "procurement.catalog.fetch",
        description: "Fetch a single catalog item",
        usedIn: ["src/api/procurement/[id]/fetch.ts"],
      },
      {
        node: "procurement.catalog.fetch.many",
        description:
          "Fetch multiple catalog items, count, categories/ecosystems, or filter values",
        usedIn: [
          "src/api/procurement/fetchAll.ts",
          "src/api/procurement/count.ts",
          "src/api/procurement/categories.ts",
          "src/api/procurement/filters.ts",
        ],
      },
      {
        node: "procurement.catalog.inventory.refresh",
        description:
          "Refresh on-hand inventory for a catalog item from ConnectWise",
        usedIn: ["src/api/procurement/[id]/refreshInventory.ts"],
        dependencies: ["procurement.catalog.fetch"],
      },
      {
        node: "procurement.catalog.link",
        description: "Link or unlink catalog items to each other",
        usedIn: [
          "src/api/procurement/[id]/link.ts",
          "src/api/procurement/[id]/unlink.ts",
        ],
        dependencies: ["procurement.catalog.fetch"],
      },
    ],
  },

  cwCallbacks: {
    name: "ConnectWise Callback Routes",
    description:
      "Inbound ConnectWise callback endpoints. These routes are intentionally unauthenticated and do not require permission nodes.",
    permissions: [],
  },

  sales: {
    name: "Sales Permissions",
    description: "Permissions for accessing and managing sales opportunities",
    permissions: [
      {
        node: "sales.opportunity.fetch",
        description:
          "Fetch a single opportunity and its sub-resources (products, notes, contacts)",
        usedIn: [
          "src/api/sales/opportunities/[id]/fetch.ts",
          "src/api/sales/opportunities/[id]/products/fetchAll.ts",
          "src/api/sales/opportunities/[id]/notes/fetchAll.ts",
          "src/api/sales/opportunities/[id]/notes/fetch.ts",
          "src/api/sales/opportunities/[id]/contacts.ts",
          "src/api/sockets/events/liveQuotePreview.ts",
        ],
      },
      {
        node: "sales.opportunity.fetch.many",
        description:
          "Fetch multiple opportunities, count, or opportunity types",
        usedIn: [
          "src/api/sales/opportunities/fetchAll.ts",
          "src/api/sales/opportunities/count.ts",
          "src/api/sales/fetchOpportunityTypes.ts",
        ],
      },
      {
        node: "sales.opportunity.fetch.@me",
        description:
          "View the personal sales dashboard showing opportunities assigned to the current user (UI-only gate)",
        usedIn: ["UI-only (client-side gate)"],
      },
      {
        node: "sales.opportunity.fetch.all",
        description:
          "View all opportunities across all users (All Opportunities tab and View All button in the sales dashboard UI)",
        usedIn: ["UI-only (client-side gate)"],
        dependencies: ["sales.opportunity.fetch.many"],
      },
      {
        node: "sales.opportunity.metrics.all",
        description:
          "Allow `scope=all` on sales opportunity metrics endpoint to read cached metrics for all active members",
        usedIn: ["src/api/sales/opportunities/metrics.ts"],
        dependencies: ["sales.opportunity.fetch.many"],
      },
      {
        node: "sales.opportunity.metrics.identifier.override",
        description:
          "Allow `identifier=<cwIdentifier>` override on sales opportunity metrics endpoint for querying another member",
        usedIn: ["src/api/sales/opportunities/metrics.ts"],
        dependencies: ["sales.opportunity.fetch.many"],
      },
      {
        node: "sales.opportunity.refresh",
        description: "Refresh a single opportunity from ConnectWise",
        usedIn: ["src/api/sales/opportunities/[id]/refresh.ts"],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.update",
        description:
          "Update an opportunity's fields (rating, sales rep, company, contact, site, description, etc.) in ConnectWise",
        usedIn: ["src/api/sales/opportunities/[id]/update.ts"],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.create",
        description: "Create a new opportunity in ConnectWise",
        usedIn: ["src/api/sales/opportunities/create.ts"],
      },
      {
        node: "sales.opportunity.delete",
        description:
          "Delete an opportunity from ConnectWise and the local database",
        usedIn: ["src/api/sales/opportunities/[id]/delete.ts"],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.note.create",
        description: "Create a new note on an opportunity",
        usedIn: ["src/api/sales/opportunities/[id]/notes/create.ts"],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.note.update",
        description: "Update an existing note on an opportunity",
        usedIn: ["src/api/sales/opportunities/[id]/notes/update.ts"],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.note.delete",
        description: "Delete a note from an opportunity",
        usedIn: ["src/api/sales/opportunities/[id]/notes/delete.ts"],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.product.update",
        description:
          "Update products (forecast items) on an opportunity, including resequencing",
        usedIn: [
          "src/api/sales/opportunities/[id]/products/resequence.ts",
          "src/api/sales/opportunities/[id]/products/update.ts",
          "src/api/sales/opportunities/[id]/products/cancel.ts",
        ],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.product.delete",
        description: "Delete a product (forecast item) from an opportunity",
        usedIn: ["src/api/sales/opportunities/[id]/products/delete.ts"],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.product.add",
        description:
          "Add a new product (forecast item) to an opportunity. Individual fields are gated by sales.opportunity.product.field.<field> permissions.",
        usedIn: ["src/api/sales/opportunities/[id]/products/add.ts"],
        dependencies: ["sales.opportunity.fetch"],
        fieldLevelPermissions: [
          "sales.opportunity.product.field.catalogItem",
          "sales.opportunity.product.field.forecastDescription",
          "sales.opportunity.product.field.productDescription",
          "sales.opportunity.product.field.quantity",
          "sales.opportunity.product.field.status",
          "sales.opportunity.product.field.productClass",
          "sales.opportunity.product.field.forecastType",
          "sales.opportunity.product.field.revenue",
          "sales.opportunity.product.field.cost",
          "sales.opportunity.product.field.includeFlag",
          "sales.opportunity.product.field.linkFlag",
          "sales.opportunity.product.field.recurringFlag",
          "sales.opportunity.product.field.taxableFlag",
          "sales.opportunity.product.field.recurringRevenue",
          "sales.opportunity.product.field.recurringCost",
          "sales.opportunity.product.field.cycles",
          "sales.opportunity.product.field.sequenceNumber",
        ],
      },
      {
        node: "sales.opportunity.product.add.specialOrder",
        description:
          'Add one or more "SPECIAL ORDER" products to an opportunity via the dedicated special-order route.',
        usedIn: [
          "src/api/sales/opportunities/[id]/products/addSpecialOrder.ts",
        ],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.product.add.labor",
        description:
          "Add labor products to an opportunity using the dedicated labor route with Field/Tech catalog selection and pricing inputs.",
        usedIn: [
          "src/api/sales/opportunities/[id]/products/addLabor.ts",
          "src/api/sales/opportunities/[id]/products/laborOptions.ts",
        ],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.quote.fetch",
        description: "Fetch all committed quotes for an opportunity.",
        usedIn: ["src/api/sales/opportunities/[id]/quotes/fetchAll.ts"],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.quote.commit",
        description:
          "Generate and store a finalized quote PDF for an opportunity with regeneration metadata and creator attribution.",
        usedIn: ["src/api/sales/opportunities/[id]/quotes/commit.ts"],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.quote.commit.backgenerate",
        description:
          "Generate a quote on an opportunity that is in a workflow state other than New or Active (e.g. PendingWon, QuoteSent). Requires sales.opportunity.quote.commit as a base.",
        usedIn: ["src/api/sales/opportunities/[id]/quotes/commit.ts"],
        dependencies: ["sales.opportunity.quote.commit"],
      },
      {
        node: "sales.opportunity.quote.preview",
        description:
          "Generate a preview-stamped quote PDF for an opportunity without storing it.",
        usedIn: ["src/api/sales/opportunities/[id]/quotes/preview.ts"],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.quote.download",
        description:
          "Download a committed quote PDF. Each download is recorded with timestamp and user info.",
        usedIn: ["src/api/sales/opportunities/[id]/quotes/download.ts"],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.quote.fetch_downloads",
        description:
          "Fetch download/print history for all quotes on an opportunity. Admin-level permission.",
        usedIn: ["src/api/sales/opportunities/[id]/quotes/fetchDownloads.ts"],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.view_margin",
        description:
          "View margin and markup data on opportunity products. Controls visibility of margin %, markup %, and related progress bars in the UI.",
        usedIn: [],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.view_cost",
        description:
          "View cost data on opportunity products. Controls visibility of unit cost, total cost, and recurring cost in the UI.",
        usedIn: [],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.view_profit",
        description:
          "View profit data on opportunity products. Controls visibility of profit values in the UI.",
        usedIn: [],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.opportunity.finalize",
        description:
          "Finalize an opportunity as Won or Lost. Without this permission, win/lose actions route to PendingWon/PendingLost instead.",
        usedIn: [
          "src/workflows/wf.opportunity.ts",
          "src/api/sales/opportunities/[id]/workflow/dispatch.ts",
        ],
        dependencies: ["sales.opportunity.workflow"],
      },
      {
        node: "sales.opportunity.cancel",
        description:
          "Cancel an opportunity. Required to transition any eligible opportunity to the Canceled status.",
        usedIn: [
          "src/workflows/wf.opportunity.ts",
          "src/api/sales/opportunities/[id]/workflow/dispatch.ts",
        ],
        dependencies: ["sales.opportunity.workflow"],
      },
      {
        node: "sales.opportunity.review",
        description:
          "Submit an opportunity for internal review. Required to transition an opportunity into the InternalReview status.",
        usedIn: ["src/workflows/wf.opportunity.ts"],
        dependencies: ["sales.opportunity.workflow"],
      },
      {
        node: "sales.opportunity.send",
        description:
          "Send a quote to the customer. Required to transition an opportunity to QuoteSent (and its compound transitions like immediate won/lost/confirmed).",
        usedIn: ["src/workflows/wf.opportunity.ts"],
        dependencies: ["sales.opportunity.workflow"],
      },
      {
        node: "sales.opportunity.reopen",
        description:
          "Re-open a cancelled opportunity. Required to transition an opportunity from Canceled back to Active.",
        usedIn: ["src/workflows/wf.opportunity.ts"],
        dependencies: ["sales.opportunity.workflow"],
      },
      {
        node: "sales.opportunity.win",
        description:
          "Mark an opportunity as won (or pending won). Gates the win button in the UI. Required for finalize(won), sendQuote(won), and transitionToPending(won).",
        usedIn: ["src/workflows/wf.opportunity.ts"],
        dependencies: ["sales.opportunity.workflow"],
      },
      {
        node: "sales.opportunity.lose",
        description:
          "Mark an opportunity as lost (or pending lost). Gates the lose button in the UI. Required for finalize(lost), sendQuote(lost), and transitionToPending(lost).",
        usedIn: ["src/workflows/wf.opportunity.ts"],
        dependencies: ["sales.opportunity.workflow"],
      },
      {
        node: "sales.opportunity.workflow",
        description:
          "Execute opportunity workflow actions (status transitions, review decisions, quote sending, etc.). Base gate for the workflow dispatch endpoint.",
        usedIn: ["src/api/sales/opportunities/[id]/workflow/dispatch.ts"],
        dependencies: ["sales.opportunity.fetch"],
      },
      {
        node: "sales.isRepresentative",
        description:
          "Designates the user as a sales representative; used for reporting and filtering purposes.",
        usedIn: [],
      },
    ],
  },

  unifi: {
    name: "UniFi Permissions",
    description:
      "Permissions for accessing and managing UniFi network infrastructure",
    permissions: [
      {
        node: "unifi.access",
        description:
          "Gate permission for the entire UniFi API — required for all UniFi routes",
        usedIn: [
          "src/api/unifi/sites/fetchAll.ts",
          "src/api/unifi/sites/sync.ts",
          "src/api/unifi/site/fetch.ts",
          "src/api/unifi/site/overview.ts",
          "src/api/unifi/site/devices.ts",
          "src/api/unifi/site/wifi/fetchAll.ts",
          "src/api/unifi/site/wifi/update.ts",
          "src/api/unifi/site/wifi/ppskFetchAll.ts",
          "src/api/unifi/site/wifi/ppskCreate.ts",
          "src/api/unifi/site/networks.ts",
          "src/api/unifi/site/link.ts",
          "src/api/unifi/site/unlink.ts",
          "src/api/companies/[id]/unifiSites.ts",
          "src/api/unifi/sites/create.ts",
          "src/api/unifi/site/wlanGroups.ts",
          "src/api/unifi/site/accessPoints.ts",
          "src/api/unifi/site/wifiLimits.ts",
          "src/api/unifi/site/speedProfilesFetchAll.ts",
          "src/api/unifi/site/speedProfilesCreate.ts",
        ],
      },
      {
        node: "unifi.sites.create",
        description: "Create a new site on the UniFi controller",
        usedIn: ["src/api/unifi/sites/create.ts"],
        dependencies: ["unifi.access"],
      },
      {
        node: "unifi.sites.fetch",
        description: "Fetch a single UniFi site",
        usedIn: ["src/api/unifi/site/fetch.ts"],
        dependencies: ["unifi.access"],
      },
      {
        node: "unifi.sites.fetch.many",
        description: "Fetch all UniFi sites",
        usedIn: ["src/api/unifi/sites/fetchAll.ts"],
        dependencies: ["unifi.access"],
      },
      {
        node: "unifi.sites.sync",
        description: "Sync sites from the UniFi controller into the database",
        usedIn: ["src/api/unifi/sites/sync.ts"],
        dependencies: ["unifi.access"],
      },
      {
        node: "unifi.sites.link",
        description: "Link or unlink a UniFi site to/from a company",
        usedIn: ["src/api/unifi/site/link.ts", "src/api/unifi/site/unlink.ts"],
        dependencies: ["unifi.access"],
      },
      {
        node: "unifi.site.overview",
        description: "View live site overview from the UniFi controller",
        usedIn: ["src/api/unifi/site/overview.ts"],
        dependencies: ["unifi.access"],
      },
      {
        node: "unifi.site.devices",
        description: "View live device list from the UniFi controller",
        usedIn: ["src/api/unifi/site/devices.ts"],
        dependencies: ["unifi.access"],
      },
      {
        node: "unifi.site.wifi",
        description: "View WiFi networks (WLANs) from the UniFi controller",
        usedIn: ["src/api/unifi/site/wifi/fetchAll.ts"],
        dependencies: ["unifi.access"],
      },
      {
        node: "unifi.site.wifi.read",
        description:
          "Field-level gate for WiFi network response data. Each key on the WlanConf object is checked as unifi.site.wifi.read.<field>. Only fields the user has permission for are included in the response.",
        usedIn: ["src/api/unifi/site/wifi/fetchAll.ts"],
        dependencies: ["unifi.access", "unifi.site.wifi"],
        fieldLevelPermissions: [
          "unifi.site.wifi.read.id",
          "unifi.site.wifi.read.name",
          "unifi.site.wifi.read.siteId",
          "unifi.site.wifi.read.enabled",
          "unifi.site.wifi.read.security",
          "unifi.site.wifi.read.wpaMode",
          "unifi.site.wifi.read.wpaEnc",
          "unifi.site.wifi.read.wpa3Support",
          "unifi.site.wifi.read.wpa3Transition",
          "unifi.site.wifi.read.wpa3FastRoaming",
          "unifi.site.wifi.read.wpa3Enhanced192",
          "unifi.site.wifi.read.passphrase",
          "unifi.site.wifi.read.passphraseAutogenerated",
          "unifi.site.wifi.read.hideSSID",
          "unifi.site.wifi.read.isGuest",
          "unifi.site.wifi.read.band",
          "unifi.site.wifi.read.bands",
          "unifi.site.wifi.read.networkconfId",
          "unifi.site.wifi.read.usergroupId",
          "unifi.site.wifi.read.apGroupIds",
          "unifi.site.wifi.read.apGroupMode",
          "unifi.site.wifi.read.pmfMode",
          "unifi.site.wifi.read.groupRekey",
          "unifi.site.wifi.read.dtimMode",
          "unifi.site.wifi.read.dtimNg",
          "unifi.site.wifi.read.dtimNa",
          "unifi.site.wifi.read.dtim6e",
          "unifi.site.wifi.read.l2Isolation",
          "unifi.site.wifi.read.fastRoamingEnabled",
          "unifi.site.wifi.read.bssTransition",
          "unifi.site.wifi.read.uapsdEnabled",
          "unifi.site.wifi.read.iappEnabled",
          "unifi.site.wifi.read.proxyArp",
          "unifi.site.wifi.read.mcastenhanceEnabled",
          "unifi.site.wifi.read.macFilterEnabled",
          "unifi.site.wifi.read.macFilterPolicy",
          "unifi.site.wifi.read.macFilterList",
          "unifi.site.wifi.read.radiusDasEnabled",
          "unifi.site.wifi.read.radiusMacAuthEnabled",
          "unifi.site.wifi.read.radiusMacaclFormat",
          "unifi.site.wifi.read.minrateSettingPreference",
          "unifi.site.wifi.read.minrateNgEnabled",
          "unifi.site.wifi.read.minrateNgDataRateKbps",
          "unifi.site.wifi.read.minrateNgAdvertisingRates",
          "unifi.site.wifi.read.minrateNaEnabled",
          "unifi.site.wifi.read.minrateNaDataRateKbps",
          "unifi.site.wifi.read.minrateNaAdvertisingRates",
          "unifi.site.wifi.read.settingPreference",
          "unifi.site.wifi.read.no2ghzOui",
          "unifi.site.wifi.read.privatePreSharedKeysEnabled",
          "unifi.site.wifi.read.privatePreSharedKeys",
          "unifi.site.wifi.read.saeGroups",
          "unifi.site.wifi.read.saePsk",
          "unifi.site.wifi.read.schedule",
          "unifi.site.wifi.read.scheduleWithDuration",
          "unifi.site.wifi.read.bcFilterList",
          "unifi.site.wifi.read.externalId",
        ],
      },
      {
        node: "unifi.site.wifi.update",
        description: "Update a WiFi network on the UniFi controller",
        usedIn: ["src/api/unifi/site/wifi/update.ts"],
        dependencies: ["unifi.access", "unifi.site.wifi"],
      },
      {
        node: "unifi.site.networks",
        description: "View network configurations from the UniFi controller",
        usedIn: ["src/api/unifi/site/networks.ts"],
        dependencies: ["unifi.access"],
      },
      {
        node: "unifi.site.wlan-groups",
        description:
          "View WLAN groups (AP broadcasting groups) from the UniFi controller for a site",
        usedIn: [
          "src/api/unifi/site/wlanGroups.ts",
          "src/api/unifi/site/wlanGroupsCreate.ts",
        ],
        dependencies: ["unifi.access"],
      },
      {
        node: "unifi.site.wlan-groups.create",
        description:
          "Create a new WLAN group (AP broadcasting group) on the UniFi controller",
        usedIn: ["src/api/unifi/site/wlanGroupsCreate.ts"],
        dependencies: ["unifi.access", "unifi.site.wlan-groups"],
      },
      {
        node: "unifi.site.access-points",
        description:
          "View access points (UAPs only) from the UniFi controller for a site",
        usedIn: ["src/api/unifi/site/accessPoints.ts"],
        dependencies: ["unifi.access"],
      },
      {
        node: "unifi.site.ap-groups",
        description:
          "View AP groups from the UniFi controller — shows which access points are grouped together for SSID broadcasting",
        usedIn: ["src/api/unifi/site/apGroups.ts"],
        dependencies: ["unifi.access"],
      },
      {
        node: "unifi.site.wifi-limits",
        description:
          "View WiFi SSID limits per access point per radio band — shows how many SSIDs each AP radio can still accept",
        usedIn: ["src/api/unifi/site/wifiLimits.ts"],
        dependencies: ["unifi.access"],
      },
      {
        node: "unifi.site.speed-profiles",
        description:
          "View speed limit profiles (user groups) from the UniFi controller",
        usedIn: [
          "src/api/unifi/site/speedProfilesFetchAll.ts",
          "src/api/unifi/site/speedProfilesCreate.ts",
        ],
        dependencies: ["unifi.access"],
      },
      {
        node: "unifi.site.speed-profiles.create",
        description:
          "Create a new speed limit profile (user group) on the UniFi controller",
        usedIn: ["src/api/unifi/site/speedProfilesCreate.ts"],
        dependencies: ["unifi.access", "unifi.site.speed-profiles"],
      },
      {
        node: "unifi.site.wifi.ppsk",
        description:
          "View private pre-shared keys (PPSKs) for a specific WiFi network",
        usedIn: [
          "src/api/unifi/site/wifi/ppskFetchAll.ts",
          "src/api/unifi/site/wifi/ppskCreate.ts",
        ],
        dependencies: ["unifi.access", "unifi.site.wifi"],
      },
      {
        node: "unifi.site.wifi.ppsk.create",
        description:
          "Create a private pre-shared key on a specific WiFi network",
        usedIn: ["src/api/unifi/site/wifi/ppskCreate.ts"],
        dependencies: [
          "unifi.access",
          "unifi.site.wifi",
          "unifi.site.wifi.ppsk",
        ],
      },
    ],
  },
  objectTypes: {
    name: "Object Types",
    description:
      "Field-level read permissions that control which keys are visible on API response objects. Each sub-category corresponds to a domain object type. Use <scope>.* to grant access to all fields.",
    permissions: [],
    subCategories: {
      company: {
        name: "Company",
        description:
          "Field-level read permissions for Company response objects",
        permissions: [
          {
            node: "obj.company",
            description:
              "Field-level gate for Company objects. Each key on the response is checked as obj.company.<field>. Only fields the user has permission for are included.",
            usedIn: [
              "src/api/companies/[id]/fetch.ts",
              "src/api/companies/fetchAll.ts",
            ],
            fieldLevelPermissions: [
              "obj.company.id",
              "obj.company.name",
              "obj.company.cw_Identifier",
              "obj.company.cw_CompanyId",
              "obj.company.cw_Data",
              "obj.company.createdAt",
              "obj.company.updatedAt",
            ],
          },
        ],
      },

      credential: {
        name: "Credential",
        description:
          "Field-level read permissions for Credential response objects",
        permissions: [
          {
            node: "obj.credential",
            description:
              "Field-level gate for Credential objects. Each key on the response is checked as obj.credential.<field>. Only fields the user has permission for are included.",
            usedIn: [
              "src/api/credentials/fetch.ts",
              "src/api/credentials/fetchByCompany.ts",
              "src/api/credentials/fetchSubCredentials.ts",
              "src/api/credential-types/fetchCredentials.ts",
            ],
            fieldLevelPermissions: [
              "obj.credential.id",
              "obj.credential.name",
              "obj.credential.notes",
              "obj.credential.typeId",
              "obj.credential.companyId",
              "obj.credential.subCredentialOfId",
              "obj.credential.fields",
              "obj.credential.type",
              "obj.credential.company",
              "obj.credential.subCredentials",
              "obj.credential.secureFieldIds",
              "obj.credential.createdAt",
              "obj.credential.updatedAt",
            ],
          },
        ],
      },

      credentialType: {
        name: "Credential Type",
        description:
          "Field-level read permissions for Credential Type response objects",
        permissions: [
          {
            node: "obj.credentialType",
            description:
              "Field-level gate for Credential Type objects. Each key on the response is checked as obj.credentialType.<field>. Only fields the user has permission for are included.",
            usedIn: [
              "src/api/credential-types/fetch.ts",
              "src/api/credential-types/fetchAll.ts",
            ],
            fieldLevelPermissions: [
              "obj.credentialType.id",
              "obj.credentialType.name",
              "obj.credentialType.permissionScope",
              "obj.credentialType.icon",
              "obj.credentialType.fields",
              "obj.credentialType.credentialCount",
              "obj.credentialType.createdAt",
              "obj.credentialType.updatedAt",
            ],
          },
        ],
      },

      user: {
        name: "User",
        description: "Field-level read permissions for User response objects",
        permissions: [
          {
            node: "obj.user",
            description:
              "Field-level gate for User objects. Each key on the response is checked as obj.user.<field>. Only fields the user has permission for are included.",
            usedIn: [
              "src/api/user/@me/fetch.ts",
              "src/api/user/fetch.ts",
              "src/api/user/fetchAll.ts",
              "src/api/roles/getUsers.ts",
            ],
            fieldLevelPermissions: [
              "obj.user.id",
              "obj.user.name",
              "obj.user.roles",
              "obj.user.permissions",
              "obj.user.login",
              "obj.user.email",
              "obj.user.cwMemberId",
              "obj.user.image",
              "obj.user.createdAt",
              "obj.user.updatedAt",
            ],
          },
        ],
      },

      role: {
        name: "Role",
        description: "Field-level read permissions for Role response objects",
        permissions: [
          {
            node: "obj.role",
            description:
              "Field-level gate for Role objects. Each key on the response is checked as obj.role.<field>. Only fields the user has permission for are included.",
            usedIn: [
              "src/api/roles/fetch.ts",
              "src/api/roles/fetchAll.ts",
              "src/api/user/fetchRoles.ts",
            ],
            fieldLevelPermissions: [
              "obj.role.id",
              "obj.role.title",
              "obj.role.moniker",
              "obj.role.permissions",
              "obj.role.users",
              "obj.role.createdAt",
              "obj.role.updatedAt",
            ],
          },
        ],
      },

      catalogItem: {
        name: "Catalog Item",
        description:
          "Field-level read permissions for Catalog Item (procurement) response objects",
        permissions: [
          {
            node: "obj.catalogItem",
            description:
              "Field-level gate for Catalog Item objects. Each key on the response is checked as obj.catalogItem.<field>. Only fields the user has permission for are included.",
            usedIn: [
              "src/api/procurement/fetchAll.ts",
              "src/api/procurement/[id]/fetch.ts",
              "src/api/procurement/[id]/fetchLinked.ts",
            ],
            fieldLevelPermissions: [
              "obj.catalogItem.id",
              "obj.catalogItem.cwCatalogId",
              "obj.catalogItem.identifier",
              "obj.catalogItem.name",
              "obj.catalogItem.description",
              "obj.catalogItem.customerDescription",
              "obj.catalogItem.internalNotes",
              "obj.catalogItem.manufacturer",
              "obj.catalogItem.manufactureCwId",
              "obj.catalogItem.partNumber",
              "obj.catalogItem.vendorName",
              "obj.catalogItem.vendorSku",
              "obj.catalogItem.vendorCwId",
              "obj.catalogItem.price",
              "obj.catalogItem.cost",
              "obj.catalogItem.inactive",
              "obj.catalogItem.salesTaxable",
              "obj.catalogItem.onHand",
              "obj.catalogItem.cwLastUpdated",
              "obj.catalogItem.linkedItems",
              "obj.catalogItem.createdAt",
              "obj.catalogItem.updatedAt",
            ],
          },
        ],
      },

      opportunity: {
        name: "Opportunity",
        description:
          "Field-level read permissions for Opportunity (sales) response objects",
        permissions: [
          {
            node: "obj.opportunity",
            description:
              "Field-level gate for Opportunity objects. Each key on the response is checked as obj.opportunity.<field>. Only fields the user has permission for are included.",
            usedIn: [
              "src/api/sales/fetchAll.ts",
              "src/api/sales/[id]/fetch.ts",
            ],
            fieldLevelPermissions: [
              "obj.opportunity.id",
              "obj.opportunity.cwOpportunityId",
              "obj.opportunity.name",
              "obj.opportunity.notes",
              "obj.opportunity.type",
              "obj.opportunity.stage",
              "obj.opportunity.status",
              "obj.opportunity.priority",
              "obj.opportunity.rating",
              "obj.opportunity.source",
              "obj.opportunity.campaign",
              "obj.opportunity.primarySalesRep",
              "obj.opportunity.secondarySalesRep",
              "obj.opportunity.company",
              "obj.opportunity.contact",
              "obj.opportunity.site",
              "obj.opportunity.customerPO",
              "obj.opportunity.totalSalesTax",
              "obj.opportunity.probability",
              "obj.opportunity.location",
              "obj.opportunity.department",
              "obj.opportunity.expectedCloseDate",
              "obj.opportunity.pipelineChangeDate",
              "obj.opportunity.dateBecameLead",
              "obj.opportunity.closedDate",
              "obj.opportunity.closedFlag",
              "obj.opportunity.closedBy",
              "obj.opportunity.companyId",
              "obj.opportunity.cwLastUpdated",
              "obj.opportunity.createdAt",
              "obj.opportunity.updatedAt",
            ],
          },
        ],
      },

      unifiSite: {
        name: "UniFi Site",
        description:
          "Field-level read permissions for UniFi Site response objects",
        permissions: [
          {
            node: "obj.unifiSite",
            description:
              "Field-level gate for UniFi Site objects. Each key on the response is checked as obj.unifiSite.<field>. Only fields the user has permission for are included.",
            usedIn: [
              "src/api/unifi/sites/fetchAll.ts",
              "src/api/unifi/site/fetch.ts",
              "src/api/companies/[id]/unifiSites.ts",
            ],
            fieldLevelPermissions: [
              "obj.unifiSite.id",
              "obj.unifiSite.name",
              "obj.unifiSite.siteId",
              "obj.unifiSite.companyId",
              "obj.unifiSite.company",
              "obj.unifiSite.createdAt",
              "obj.unifiSite.updatedAt",
            ],
          },
        ],
      },

      wifiNetwork: {
        name: "WiFi Network",
        description:
          "Field-level read permissions for UniFi WiFi Network (WLAN) response objects. See the unifi category for the full field-level permission list under unifi.site.wifi.read.",
        permissions: [
          {
            node: "unifi.site.wifi.read",
            description:
              "Field-level gate for WiFi network response data (defined in the unifi category). Each key on the WlanConf object is checked as unifi.site.wifi.read.<field>.",
            usedIn: ["src/api/unifi/site/wifi/fetchAll.ts"],
            dependencies: ["unifi.access", "unifi.site.wifi"],
          },
        ],
      },
    },
  },
} as const satisfies Record<string, PermissionCategory>;

/**
 * Recursively collects permission nodes from a category and its sub-categories.
 */
function collectPermissions(category: PermissionCategory): PermissionNode[] {
  const direct = category.permissions as PermissionNode[];
  const nested = category.subCategories
    ? Object.values(category.subCategories).flatMap(collectPermissions)
    : [];
  return [...direct, ...nested];
}

/**
 * Utility function to get all permission nodes flattened into a single array
 */
export function getAllPermissionNodes(): PermissionNode[] {
  return Object.values(PERMISSION_NODES).flatMap((category) =>
    collectPermissions(category)
  );
}

/**
 * Utility function to get all permission node strings
 */
export function getAllPermissionStrings(): string[] {
  return getAllPermissionNodes().map((p) => p.node);
}

/**
 * Utility function to get a specific permission node by its identifier
 */
export function getPermissionNode(nodeId: string): PermissionNode | undefined {
  return getAllPermissionNodes().find((p) => p.node === nodeId);
}

/**
 * Utility function to get all permissions in a specific category
 */
export function getPermissionsByCategory(
  categoryKey: keyof typeof PERMISSION_NODES
): PermissionNode[] {
  return PERMISSION_NODES[categoryKey].permissions;
}
