import { z } from 'zod'
import { listAllSystems } from '../organization-model/helpers'
import type { OrganizationModel } from '../organization-model/types'
import { PERMISSIONS, type PermissionKey } from './multi-tenancy/permissions'

export const DEFAULT_ACCESS_ACTION = 'view' as const
export const PLATFORM_ADMIN_ACCESS_KEY = 'platform.admin' as const
export const PLATFORM_ADMIN_ACCESS_KEY_SHORTHAND = 'platformAdmin' as const

export const AccessActionSchema = z.enum(['view', 'manage'])

export const AccessKeyObjectSchema = z
  .object({
    systemPath: z.string().trim().min(1),
    action: AccessActionSchema.default(DEFAULT_ACCESS_ACTION)
  })
  .strict()

export const AccessKeyInputSchema = z.union([z.string().trim().min(1), AccessKeyObjectSchema])
export const NormalizedAccessKeySchema = AccessKeyObjectSchema
export const AccessKeySchema = AccessKeyInputSchema

export type AccessAction = z.infer<typeof AccessActionSchema>
export type AccessKeyObject = z.infer<typeof AccessKeyObjectSchema>
export type AccessKeyInput = z.input<typeof AccessKeyInputSchema>
export type NormalizedAccessKey = z.infer<typeof NormalizedAccessKeySchema>
export type AccessKey = NormalizedAccessKey

export type AccessCatalogEntrySource = 'om-system' | 'diagnostic' | 'platform' | 'permission'

export interface AccessCatalogEntry {
  key: NormalizedAccessKey
  source: AccessCatalogEntrySource
  rolePermission?: string
}

export interface AccessKeyCatalog {
  bySystemPath: ReadonlyMap<string, readonly AccessCatalogEntry[]>
  entries: readonly AccessCatalogEntry[]
}

export const DIAGNOSTIC_VIEW_ACCESS_KEYS = [
  'diagnostic.operations.overview',
  'diagnostic.operations.recent-executions',
  'diagnostic.monitoring.execution-logs',
  'diagnostic.monitoring.notifications'
] as const

export const AccessKeys = {
  platformAdmin: { systemPath: PLATFORM_ADMIN_ACCESS_KEY, action: DEFAULT_ACCESS_ACTION },
  organizationManage: { systemPath: 'permission.org', action: 'manage' },
  membersManage: { systemPath: 'permission.members', action: 'manage' },
  rolesManage: { systemPath: 'permission.roles', action: 'manage' },
  /** @reserved Reserved for first-class secret administration routes and command surfaces. */
  secretsManage: { systemPath: 'permission.secrets', action: 'manage' },
  operationsRead: { systemPath: 'permission.operations', action: DEFAULT_ACCESS_ACTION },
  /** @reserved Reserved for write-level operations administration beyond read-only monitoring. */
  operationsManage: { systemPath: 'permission.operations', action: 'manage' },
  leadGenManage: { systemPath: 'sales.lead-gen', action: 'manage' },
  /** @reserved Reserved for acquisition administration once that permission namespace is wired. */
  acquisitionManage: { systemPath: 'permission.acquisition', action: 'manage' },
  /** @reserved Reserved for project administration permission catalog compatibility. */
  projectsManage: { systemPath: 'permission.projects', action: 'manage' },
  /** @reserved Reserved for client administration permission catalog compatibility. */
  clientsManage: { systemPath: 'permission.clients', action: 'manage' },
  operationsOverview: { systemPath: 'diagnostic.operations.overview', action: DEFAULT_ACCESS_ACTION },
  /** @reserved Reserved for a diagnostics surface that lists recent operations executions. */
  operationsRecentExecutions: { systemPath: 'diagnostic.operations.recent-executions', action: DEFAULT_ACCESS_ACTION },
  monitoringExecutionLogs: { systemPath: 'diagnostic.monitoring.execution-logs', action: DEFAULT_ACCESS_ACTION },
  monitoringNotifications: { systemPath: 'diagnostic.monitoring.notifications', action: DEFAULT_ACCESS_ACTION }
} as const satisfies Record<string, NormalizedAccessKey>

const PERMISSION_ACCESS_KEY_DEFINITIONS: readonly {
  key: NormalizedAccessKey
  rolePermission: PermissionKey
}[] = [
  { key: AccessKeys.organizationManage, rolePermission: PERMISSIONS.ORG_MANAGE },
  { key: AccessKeys.membersManage, rolePermission: PERMISSIONS.MEMBERS_MANAGE },
  { key: AccessKeys.rolesManage, rolePermission: PERMISSIONS.ROLES_MANAGE },
  { key: AccessKeys.secretsManage, rolePermission: PERMISSIONS.SECRETS_MANAGE },
  { key: AccessKeys.operationsRead, rolePermission: PERMISSIONS.OPERATIONS_READ },
  { key: AccessKeys.operationsManage, rolePermission: PERMISSIONS.OPERATIONS_MANAGE },
  { key: AccessKeys.acquisitionManage, rolePermission: PERMISSIONS.ACQUISITION_MANAGE },
  { key: AccessKeys.projectsManage, rolePermission: PERMISSIONS.PROJECTS_MANAGE },
  { key: AccessKeys.clientsManage, rolePermission: PERMISSIONS.CLIENTS_MANAGE }
]

export function normalizeAccessKey(input: AccessKeyInput): NormalizedAccessKey {
  const parsed = AccessKeyInputSchema.parse(input)
  const normalized =
    typeof parsed === 'string'
      ? {
          systemPath: parsed === PLATFORM_ADMIN_ACCESS_KEY_SHORTHAND ? PLATFORM_ADMIN_ACCESS_KEY : parsed,
          action: DEFAULT_ACCESS_ACTION
        }
      : parsed

  return NormalizedAccessKeySchema.parse(normalized)
}

export function accessKeyToString(input: AccessKeyInput): string {
  const key = normalizeAccessKey(input)
  return `${key.systemPath}:${key.action}`
}

export function rolePermissionForAccessKey(key: NormalizedAccessKey): string | undefined {
  if (key.action === DEFAULT_ACCESS_ACTION) return undefined
  return `${key.systemPath}.${key.action}`
}

export interface DeriveAccessKeyCatalogOptions {
  diagnosticKeys?: readonly string[]
  includeManageActions?: boolean
}

function groupCatalogEntries(entries: readonly AccessCatalogEntry[]): ReadonlyMap<string, readonly AccessCatalogEntry[]> {
  const grouped = new Map<string, AccessCatalogEntry[]>()

  for (const entry of entries) {
    const existing = grouped.get(entry.key.systemPath) ?? []
    existing.push(entry)
    grouped.set(entry.key.systemPath, existing)
  }

  return grouped
}

function buildCatalogEntry(systemPath: string, action: AccessAction, source: AccessCatalogEntrySource): AccessCatalogEntry {
  const key = normalizeAccessKey({ systemPath, action })
  return {
    key,
    source,
    rolePermission: rolePermissionForAccessKey(key)
  }
}

export function deriveAccessKeyCatalog(
  organizationModel: OrganizationModel,
  options: DeriveAccessKeyCatalogOptions = {}
): AccessKeyCatalog {
  const { diagnosticKeys = DIAGNOSTIC_VIEW_ACCESS_KEYS, includeManageActions = true } = options
  const entries: AccessCatalogEntry[] = [
    buildCatalogEntry(PLATFORM_ADMIN_ACCESS_KEY, DEFAULT_ACCESS_ACTION, 'platform')
  ]

  for (const { path } of listAllSystems(organizationModel)) {
    entries.push(buildCatalogEntry(path, DEFAULT_ACCESS_ACTION, 'om-system'))
    if (includeManageActions) {
      entries.push(buildCatalogEntry(path, 'manage', 'om-system'))
    }
  }

  for (const { key, rolePermission } of PERMISSION_ACCESS_KEY_DEFINITIONS) {
    entries.push({
      key,
      source: 'permission',
      rolePermission
    })
  }

  for (const systemPath of diagnosticKeys) {
    entries.push(buildCatalogEntry(systemPath, DEFAULT_ACCESS_ACTION, 'diagnostic'))
  }

  return {
    bySystemPath: groupCatalogEntries(entries),
    entries
  }
}

export function findAccessCatalogEntry(
  catalog: AccessKeyCatalog,
  key: NormalizedAccessKey
): AccessCatalogEntry | undefined {
  return catalog.bySystemPath.get(key.systemPath)?.find((entry) => entry.key.action === key.action)
}

export function listAccessKeys(catalog: AccessKeyCatalog): NormalizedAccessKey[] {
  return catalog.entries.map((entry) => entry.key)
}
