import { describe, expect, it } from 'vitest'
import { AccessKeys, deriveAccessKeyCatalog } from '../access-keys'
import { checkAccess } from '../access-model'
import type { AccessContext } from '../access-model'
import type { OrganizationModel } from '../../organization-model/types'

const organizationModel = {
  systems: {
    sales: {
      id: 'sales',
      label: 'Sales',
      lifecycle: 'active',
      order: 10,
      systems: {
        crm: {
          id: 'crm',
          label: 'CRM',
          lifecycle: 'active',
          order: 10
        },
        'lead-gen': {
          id: 'lead-gen',
          label: 'Lead Gen',
          lifecycle: 'active',
          order: 20
        }
      }
    },
    operations: {
      id: 'operations',
      label: 'Operations',
      lifecycle: 'beta',
      order: 20
    },
    drafts: {
      id: 'drafts',
      label: 'Drafts',
      lifecycle: 'draft',
      order: 30
    },
    retired: {
      id: 'retired',
      label: 'Retired',
      lifecycle: 'deprecated',
      order: 40
    },
    missingLifecycle: {
      id: 'missingLifecycle',
      label: 'Missing Lifecycle',
      order: 50
    }
  }
} as OrganizationModel

const baseContext: AccessContext = {
  organizationId: 'org-1',
  organizationModel,
  membership: {
    id: 'membership-1',
    organizationId: 'org-1',
    effectivePermissions: ['sales.crm.manage']
  },
  profile: {
    id: 'user-1',
    isPlatformAdmin: false
  }
}

function context(overrides: Partial<AccessContext> = {}): AccessContext {
  return { ...baseContext, ...overrides }
}

describe('checkAccess', () => {
  it('allows active system view access by lifecycle alone', () => {
    expect(checkAccess('sales.crm', context())).toEqual({
      allowed: true,
      restrictedBy: null,
      reason: 'allowed'
    })
  })

  it('denies unknown keys through catalog validation', () => {
    expect(checkAccess('unknown.system', context())).toEqual({
      allowed: false,
      restrictedBy: 'catalog',
      reason: 'unknown-access-key'
    })
  })

  it('denies missing membership before evaluating lifecycle or permissions', () => {
    expect(checkAccess('sales.crm', context({ membership: null }))).toEqual({
      allowed: false,
      restrictedBy: 'membership',
      reason: 'missing-membership'
    })
  })

  it('denies organization mismatches', () => {
    expect(
      checkAccess(
        'sales.crm',
        context({
          membership: { id: 'membership-2', organizationId: 'org-2', effectivePermissions: ['sales.crm.manage'] }
        })
      )
    ).toEqual({
      allowed: false,
      restrictedBy: 'membership',
      reason: 'organization-mismatch'
    })
  })

  it('denies draft, deprecated, archived, and missing lifecycle systems', () => {
    const archivedModel = {
      systems: {
        archived: {
          id: 'archived',
          label: 'Archived',
          lifecycle: 'archived',
          order: 10
        }
      }
    } as OrganizationModel

    expect(checkAccess('drafts', context())).toMatchObject({
      allowed: false,
      restrictedBy: 'system-lifecycle',
      reason: 'system-not-active'
    })
    expect(checkAccess('retired', context())).toMatchObject({
      allowed: false,
      restrictedBy: 'system-lifecycle',
      reason: 'system-not-active'
    })
    expect(checkAccess('missingLifecycle', context())).toMatchObject({
      allowed: false,
      restrictedBy: 'system-lifecycle',
      reason: 'system-not-active'
    })
    expect(checkAccess('archived', context({ organizationModel: archivedModel }))).toMatchObject({
      allowed: false,
      restrictedBy: 'system-lifecycle',
      reason: 'system-not-active'
    })
  })

  it('allows beta systems only when beta access or development mode is enabled', () => {
    expect(checkAccess('operations', context())).toEqual({
      allowed: false,
      restrictedBy: 'system-lifecycle',
      reason: 'system-not-active'
    })

    expect(checkAccess('operations', context({ betaAccessEnabled: true }))).toEqual({
      allowed: true,
      restrictedBy: null,
      reason: 'allowed'
    })

    expect(checkAccess('operations', context({ isDevelopment: true }))).toEqual({
      allowed: true,
      restrictedBy: null,
      reason: 'allowed'
    })
  })

  it('requires matching role permission for manage when permissions are supplied', () => {
    expect(checkAccess({ systemPath: 'sales.crm', action: 'manage' }, context())).toEqual({
      allowed: true,
      restrictedBy: null,
      reason: 'allowed'
    })

    expect(
      checkAccess(
        { systemPath: 'sales.crm', action: 'manage' },
        context({
          membership: { id: 'membership-1', organizationId: 'org-1', effectivePermissions: [] }
        })
      )
    ).toEqual({
      allowed: false,
      restrictedBy: 'role-permission',
      reason: 'role-permission-denied'
    })
  })

  it('allows Lead Gen manage only with the explicit OM System permission', () => {
    expect(
      checkAccess(
        AccessKeys.leadGenManage,
        context({
          membership: {
            id: 'membership-1',
            organizationId: 'org-1',
            effectivePermissions: ['sales.lead-gen.manage']
          }
        })
      )
    ).toEqual({
      allowed: true,
      restrictedBy: null,
      reason: 'allowed'
    })

    expect(checkAccess(AccessKeys.leadGenManage, context())).toEqual({
      allowed: false,
      restrictedBy: 'role-permission',
      reason: 'role-permission-denied'
    })
  })

  it('requires explicit role permissions for permission-only access keys', () => {
    expect(
      checkAccess(
        AccessKeys.organizationManage,
        context({
          membership: { id: 'membership-1', organizationId: 'org-1', effectivePermissions: ['org.manage'] }
        })
      )
    ).toEqual({
      allowed: true,
      restrictedBy: null,
      reason: 'allowed'
    })

    expect(checkAccess(AccessKeys.organizationManage, context())).toEqual({
      allowed: false,
      restrictedBy: 'role-permission',
      reason: 'role-permission-denied'
    })
  })

  it('checks diagnostic keys against the runtime diagnostic allowlist', () => {
    expect(checkAccess(AccessKeys.operationsOverview, context())).toEqual({
      allowed: false,
      restrictedBy: 'diagnostic-allowlist',
      reason: 'diagnostic-key-not-allowed'
    })

    expect(
      checkAccess(
        AccessKeys.operationsOverview,
        context({ diagnosticAllowlist: [AccessKeys.operationsOverview.systemPath] })
      )
    ).toEqual({
      allowed: true,
      restrictedBy: null,
      reason: 'allowed'
    })
  })

  it('keeps diagnostic catalog validation separate from runtime diagnostic allowlist', () => {
    const accessCatalog = deriveAccessKeyCatalog(organizationModel, { diagnosticKeys: ['diagnostic.custom'] })

    expect(
      checkAccess('diagnostic.custom', context({ accessCatalog, diagnosticAllowlist: ['diagnostic.custom'] }))
    ).toEqual({
      allowed: true,
      restrictedBy: null,
      reason: 'allowed'
    })

    expect(checkAccess('diagnostic.not-cataloged', context({ diagnosticAllowlist: ['diagnostic.not-cataloged'] }))).toEqual({
      allowed: false,
      restrictedBy: 'catalog',
      reason: 'unknown-access-key'
    })
  })

  it('centralizes platform-admin bypass with the platform-admin-bypass reason', () => {
    expect(
      checkAccess('unknown.system', context({ profile: { id: 'admin', isPlatformAdmin: true }, membership: null }))
    ).toEqual({
      allowed: true,
      restrictedBy: null,
      reason: 'platform-admin-bypass'
    })
  })

  it('denies platformAdmin special-case for non-platform admins', () => {
    expect(checkAccess('platformAdmin', context())).toEqual({
      allowed: false,
      restrictedBy: 'role-permission',
      reason: 'role-permission-denied'
    })
  })
})
