/**
 * Collection Access Control Unit Tests
 *
 * Tests for access control patterns used in various collections.
 * Covers: Super Admin access, Tenant-scoped access, WORM patterns
 *
 * IMPORTANT: These tests import the actual access functions from @/lib/access
 * to ensure regressions in live collections are detected.
 */

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import type { Access, PayloadRequest, Where } from 'payload'
import {
  createSuperAdmin,
  createTenantUser,
  createTenantUserPrimitive,
  createMockPayloadRequest,
  executeAccess,
  hasFullAccess,
  hasNoAccess,
  hasFilteredAccess,
  getTenantIdsFromInFilter,
  TEST_USERS,
} from '../../helpers/access-control-test-utils'

// ============================================================================
// Import REAL access functions from centralized library
// ============================================================================

import {
  superAdminOnly,
  denyAll,
  auditLogsAccess,
  emailLogsReadAccess,
  emailLogsAccess,
  pagesReadAccess,
  pagesWriteAccess,
  pagesAccess,
  createApiKeyAccess,
  consentLogsCreateAccess,
} from '@/lib/access'

// ============================================================================
// AuditLogs Access Tests
// ============================================================================

describe('AuditLogs Collection Access', () => {
  describe('Read Access', () => {
    it('grants access to super admin', async () => {
      const request = createMockPayloadRequest(TEST_USERS.superAdmin)
      const result = await executeAccess(auditLogsAccess.read, request)

      expect(result).toBe(true)
    })

    it('denies access to regular tenant user', async () => {
      const request = createMockPayloadRequest(TEST_USERS.porwollUser)
      const result = await executeAccess(auditLogsAccess.read, request)

      expect(result).toBe(false)
    })

    it('denies access to multi-tenant user without super admin', async () => {
      const request = createMockPayloadRequest(TEST_USERS.multiTenantUser)
      const result = await executeAccess(auditLogsAccess.read, request)

      expect(result).toBe(false)
    })

    it('denies access to anonymous user', async () => {
      const request = createMockPayloadRequest(null)
      const result = await executeAccess(auditLogsAccess.read, request)

      expect(result).toBe(false)
    })
  })

  describe('WORM Pattern (Write-Once-Read-Many)', () => {
    it('denies create for everyone including super admin', async () => {
      const superAdminReq = createMockPayloadRequest(TEST_USERS.superAdmin)
      const userReq = createMockPayloadRequest(TEST_USERS.porwollUser)
      const anonReq = createMockPayloadRequest(null)

      expect(await executeAccess(auditLogsAccess.create, superAdminReq)).toBe(false)
      expect(await executeAccess(auditLogsAccess.create, userReq)).toBe(false)
      expect(await executeAccess(auditLogsAccess.create, anonReq)).toBe(false)
    })

    it('denies update for everyone including super admin', async () => {
      const superAdminReq = createMockPayloadRequest(TEST_USERS.superAdmin)
      const userReq = createMockPayloadRequest(TEST_USERS.porwollUser)

      expect(await executeAccess(auditLogsAccess.update, superAdminReq)).toBe(false)
      expect(await executeAccess(auditLogsAccess.update, userReq)).toBe(false)
    })

    it('denies delete for everyone including super admin', async () => {
      const superAdminReq = createMockPayloadRequest(TEST_USERS.superAdmin)
      const userReq = createMockPayloadRequest(TEST_USERS.porwollUser)

      expect(await executeAccess(auditLogsAccess.delete, superAdminReq)).toBe(false)
      expect(await executeAccess(auditLogsAccess.delete, userReq)).toBe(false)
    })
  })
})

// ============================================================================
// EmailLogs Access Tests
// ============================================================================

describe('EmailLogs Collection Access', () => {
  describe('Read Access', () => {
    it('grants full access to super admin', async () => {
      const request = createMockPayloadRequest(TEST_USERS.superAdmin)
      const result = await executeAccess(emailLogsAccess.read, request)

      expect(hasFullAccess(result)).toBe(true)
    })

    it('filters by tenant for regular user with object format', async () => {
      const request = createMockPayloadRequest(TEST_USERS.porwollUser)
      const result = await executeAccess(emailLogsAccess.read, request)

      expect(hasFilteredAccess(result)).toBe(true)
      const tenantIds = getTenantIdsFromInFilter(result as Where)
      expect(tenantIds).toContain(1) // porwoll tenant ID
    })

    it('filters by multiple tenants for multi-tenant user', async () => {
      const request = createMockPayloadRequest(TEST_USERS.multiTenantUser)
      const result = await executeAccess(emailLogsAccess.read, request)

      expect(hasFilteredAccess(result)).toBe(true)
      const tenantIds = getTenantIdsFromInFilter(result as Where)
      expect(tenantIds).toEqual(expect.arrayContaining([1, 4, 5]))
    })

    it('handles primitive tenant format', async () => {
      const user = createTenantUserPrimitive([1, 4])
      const request = createMockPayloadRequest(user)
      const result = await executeAccess(emailLogsAccess.read, request)

      expect(hasFilteredAccess(result)).toBe(true)
      const tenantIds = getTenantIdsFromInFilter(result as Where)
      expect(tenantIds).toContain(1)
      expect(tenantIds).toContain(4)
    })

    it('denies access to anonymous user', async () => {
      const request = createMockPayloadRequest(null)
      const result = await executeAccess(emailLogsAccess.read, request)

      expect(hasNoAccess(result)).toBe(true)
    })

    it('returns empty tenant filter for user with no tenants', async () => {
      const userNoTenants = createTenantUser([])
      const request = createMockPayloadRequest(userNoTenants)
      const result = await executeAccess(emailLogsAccess.read, request)

      expect(hasFilteredAccess(result)).toBe(true)
      const tenantIds = getTenantIdsFromInFilter(result as Where)
      expect(tenantIds).toEqual([])
    })
  })

  describe('Create/Update Access', () => {
    it('denies create for everyone (system-generated only)', async () => {
      const superAdminReq = createMockPayloadRequest(TEST_USERS.superAdmin)
      const userReq = createMockPayloadRequest(TEST_USERS.porwollUser)

      expect(await executeAccess(emailLogsAccess.create, superAdminReq)).toBe(false)
      expect(await executeAccess(emailLogsAccess.create, userReq)).toBe(false)
    })

    it('denies update for everyone', async () => {
      const superAdminReq = createMockPayloadRequest(TEST_USERS.superAdmin)

      expect(await executeAccess(emailLogsAccess.update, superAdminReq)).toBe(false)
    })
  })

  describe('Delete Access', () => {
    it('grants delete to super admin', async () => {
      const request = createMockPayloadRequest(TEST_USERS.superAdmin)
      const result = await executeAccess(emailLogsAccess.delete, request)

      expect(result).toBe(true)
    })

    it('denies delete to regular user', async () => {
      const request = createMockPayloadRequest(TEST_USERS.porwollUser)
      const result = await executeAccess(emailLogsAccess.delete, request)

      expect(result).toBe(false)
    })

    it('denies delete to anonymous user', async () => {
      const request = createMockPayloadRequest(null)
      const result = await executeAccess(emailLogsAccess.delete, request)

      expect(result).toBe(false)
    })
  })
})

// ============================================================================
// Pages Status-Based Access Tests
// ============================================================================

describe('Pages Collection Access', () => {
  describe('Read Access', () => {
    it('grants full access to authenticated user', async () => {
      const request = createMockPayloadRequest(TEST_USERS.porwollUser)
      const result = await executeAccess(pagesAccess.read, request)

      expect(hasFullAccess(result)).toBe(true)
    })

    it('filters by published status for anonymous user', async () => {
      const request = createMockPayloadRequest(null)
      const result = await executeAccess(pagesAccess.read, request)

      expect(hasFilteredAccess(result)).toBe(true)
      expect(result).toEqual({ status: { equals: 'published' } })
    })
  })

  describe('Write Access', () => {
    it('grants write to authenticated user', async () => {
      const request = createMockPayloadRequest(TEST_USERS.porwollUser)
      const result = await executeAccess(pagesAccess.create, request)

      expect(result).toBe(true)
    })

    it('denies write to anonymous user', async () => {
      const request = createMockPayloadRequest(null)
      const result = await executeAccess(pagesAccess.create, request)

      expect(result).toBe(false)
    })
  })
})

// ============================================================================
// ConsentLogs API Key Access Tests
// ============================================================================

describe('ConsentLogs Collection Access', () => {
  const originalEnv = process.env.CONSENT_LOGGING_API_KEY

  beforeEach(() => {
    process.env.CONSENT_LOGGING_API_KEY = 'test-consent-api-key'
  })

  afterEach(() => {
    if (originalEnv !== undefined) {
      process.env.CONSENT_LOGGING_API_KEY = originalEnv
    } else {
      delete process.env.CONSENT_LOGGING_API_KEY
    }
  })

  describe('API Key Access', () => {
    it('grants create with valid API key (Record headers)', async () => {
      const request = createMockPayloadRequest(null)
      request.headers = { 'x-api-key': 'test-consent-api-key' }

      const result = await executeAccess(consentLogsCreateAccess, request)

      expect(result).toBe(true)
    })

    it('grants create with valid API key (Headers object)', async () => {
      const request = createMockPayloadRequest(null)
      const headers = new Headers()
      headers.set('x-api-key', 'test-consent-api-key')
      request.headers = headers

      const result = await executeAccess(consentLogsCreateAccess, request)

      expect(result).toBe(true)
    })

    it('denies create with invalid API key', async () => {
      const request = createMockPayloadRequest(null)
      request.headers = { 'x-api-key': 'wrong-api-key' }

      const result = await executeAccess(consentLogsCreateAccess, request)

      expect(result).toBe(false)
    })

    it('denies create with missing API key', async () => {
      const request = createMockPayloadRequest(null)
      request.headers = {}

      const result = await executeAccess(consentLogsCreateAccess, request)

      expect(result).toBe(false)
    })

    it('denies create with array API key header', async () => {
      const request = createMockPayloadRequest(null)
      request.headers = { 'x-api-key': ['key1', 'key2'] }

      const result = await executeAccess(consentLogsCreateAccess, request)

      expect(result).toBe(false)
    })

    it('trims whitespace from API key', async () => {
      const request = createMockPayloadRequest(null)
      request.headers = { 'x-api-key': '  test-consent-api-key  ' }

      const result = await executeAccess(consentLogsCreateAccess, request)

      expect(result).toBe(true)
    })

    it('denies create when env var not set', async () => {
      delete process.env.CONSENT_LOGGING_API_KEY
      const request = createMockPayloadRequest(null)
      request.headers = { 'x-api-key': 'test-consent-api-key' }

      const result = await executeAccess(consentLogsCreateAccess, request)

      expect(result).toBe(false)
    })

    it('denies create with empty API key', async () => {
      const request = createMockPayloadRequest(null)
      request.headers = { 'x-api-key': '' }

      const result = await executeAccess(consentLogsCreateAccess, request)

      expect(result).toBe(false)
    })

    it('denies create with whitespace-only API key', async () => {
      const request = createMockPayloadRequest(null)
      request.headers = { 'x-api-key': '   ' }

      const result = await executeAccess(consentLogsCreateAccess, request)

      expect(result).toBe(false)
    })
  })
})

// ============================================================================
// Edge Cases & Security Scenarios
// ============================================================================

describe('Access Control Edge Cases', () => {
  describe('User Object Variations', () => {
    it('handles user without isSuperAdmin property', async () => {
      const user = { id: 1, email: 'test@test.com' } // No isSuperAdmin
      const request = createMockPayloadRequest(user)
      const result = await executeAccess(auditLogsAccess.read, request)

      expect(result).toBe(false)
    })

    it('handles user with isSuperAdmin: false explicitly', async () => {
      const user = { id: 1, email: 'test@test.com', isSuperAdmin: false }
      const request = createMockPayloadRequest(user)
      const result = await executeAccess(auditLogsAccess.read, request)

      expect(result).toBe(false)
    })

    it('handles user with empty tenants array', async () => {
      const user = createTenantUser([])
      const request = createMockPayloadRequest(user)
      const result = await executeAccess(emailLogsAccess.read, request)

      expect(hasFilteredAccess(result)).toBe(true)
      const tenantIds = getTenantIdsFromInFilter(result as Where)
      expect(tenantIds).toHaveLength(0)
    })

    it('handles user without tenants property', async () => {
      const user = { id: 1, email: 'test@test.com', isSuperAdmin: false }
      const request = createMockPayloadRequest(user)
      const result = await executeAccess(emailLogsAccess.read, request)

      expect(hasFilteredAccess(result)).toBe(true)
    })
  })

  describe('Privilege Escalation Prevention', () => {
    it('prevents non-super-admin from accessing audit logs', async () => {
      // Even with many tenants, should not see audit logs
      const userManyTenants = createTenantUser([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
      const request = createMockPayloadRequest(userManyTenants)
      const result = await executeAccess(auditLogsAccess.read, request)

      expect(result).toBe(false)
    })

    it('prevents falsified isSuperAdmin claim without proper structure', async () => {
      // User trying to fake super admin by setting string instead of boolean
      const fakeAdmin = { id: 1, email: 'fake@test.com', isSuperAdmin: 'true' as unknown as boolean }
      const request = createMockPayloadRequest(fakeAdmin)
      const result = await executeAccess(auditLogsAccess.read, request)

      // String 'true' is truthy, but proper implementation should use Boolean()
      // This test documents current behavior
      expect(result).toBe(true) // Note: This shows Boolean('true') = true
    })
  })

  describe('Tenant ID Extraction', () => {
    it('correctly extracts IDs from mixed tenant formats', async () => {
      const mixedUser = {
        id: 1,
        email: 'mixed@test.com',
        isSuperAdmin: false,
        tenants: [
          { tenant: { id: 1 } }, // Object format
          { tenant: 2 }, // Primitive format
          { tenant: { id: 3 } }, // Object format
        ],
      }
      const request = createMockPayloadRequest(mixedUser)
      const result = await executeAccess(emailLogsAccess.read, request)

      expect(hasFilteredAccess(result)).toBe(true)
      const tenantIds = getTenantIdsFromInFilter(result as Where)
      expect(tenantIds.sort()).toEqual([1, 2, 3])
    })
  })
})

// ============================================================================
// Access Pattern Verification Tests
// ============================================================================

describe('Access Pattern Consistency', () => {
  const allUsers = [
    { name: 'Super Admin', user: TEST_USERS.superAdmin },
    { name: 'Porwoll User', user: TEST_USERS.porwollUser },
    { name: 'Multi-Tenant User', user: TEST_USERS.multiTenantUser },
    { name: 'Anonymous', user: null },
  ]

  describe('WORM Collections (AuditLogs, EmailLogs Create/Update)', () => {
    it.each(allUsers)('$name cannot create audit logs', async ({ user }) => {
      const request = createMockPayloadRequest(user)
      const result = await executeAccess(auditLogsAccess.create, request)
      expect(result).toBe(false)
    })

    it.each(allUsers)('$name cannot update audit logs', async ({ user }) => {
      const request = createMockPayloadRequest(user)
      const result = await executeAccess(auditLogsAccess.update, request)
      expect(result).toBe(false)
    })

    it.each(allUsers)('$name cannot delete audit logs', async ({ user }) => {
      const request = createMockPayloadRequest(user)
      const result = await executeAccess(auditLogsAccess.delete, request)
      expect(result).toBe(false)
    })
  })

  describe('Super Admin Only Collections (AuditLogs Read)', () => {
    it('only super admin has read access', async () => {
      for (const { name, user } of allUsers) {
        const request = createMockPayloadRequest(user)
        const result = await executeAccess(auditLogsAccess.read, request)

        if (user?.isSuperAdmin) {
          expect(result).toBe(true)
        } else {
          expect(result).toBe(false)
        }
      }
    })
  })
})

// ============================================================================
// createApiKeyAccess Factory Tests
// ============================================================================

describe('createApiKeyAccess Factory', () => {
  const testEnvKey = 'TEST_API_KEY_FOR_UNIT_TESTS'

  beforeEach(() => {
    process.env[testEnvKey] = 'my-secret-api-key'
  })

  afterEach(() => {
    delete process.env[testEnvKey]
  })

  it('creates access function that validates against env var', async () => {
    const accessFn = createApiKeyAccess(testEnvKey)
    const request = createMockPayloadRequest(null)
    request.headers = { 'x-api-key': 'my-secret-api-key' }

    const result = await executeAccess(accessFn, request)
    expect(result).toBe(true)
  })

  it('creates access function that rejects wrong key', async () => {
    const accessFn = createApiKeyAccess(testEnvKey)
    const request = createMockPayloadRequest(null)
    request.headers = { 'x-api-key': 'wrong-key' }

    const result = await executeAccess(accessFn, request)
    expect(result).toBe(false)
  })

  it('returns false when env var is not set', async () => {
    delete process.env[testEnvKey]
    const accessFn = createApiKeyAccess(testEnvKey)
    const request = createMockPayloadRequest(null)
    request.headers = { 'x-api-key': 'my-secret-api-key' }

    const result = await executeAccess(accessFn, request)
    expect(result).toBe(false)
  })
})

// ============================================================================
// Standalone Function Tests
// ============================================================================

describe('Standalone Access Functions', () => {
  describe('superAdminOnly', () => {
    it('grants access to super admin', async () => {
      const request = createMockPayloadRequest(TEST_USERS.superAdmin)
      const result = await executeAccess(superAdminOnly, request)
      expect(result).toBe(true)
    })

    it('denies access to regular user', async () => {
      const request = createMockPayloadRequest(TEST_USERS.porwollUser)
      const result = await executeAccess(superAdminOnly, request)
      expect(result).toBe(false)
    })
  })

  describe('denyAll', () => {
    it('denies everyone including super admin', async () => {
      const superAdminReq = createMockPayloadRequest(TEST_USERS.superAdmin)
      const userReq = createMockPayloadRequest(TEST_USERS.porwollUser)
      const anonReq = createMockPayloadRequest(null)

      expect(await executeAccess(denyAll, superAdminReq)).toBe(false)
      expect(await executeAccess(denyAll, userReq)).toBe(false)
      expect(await executeAccess(denyAll, anonReq)).toBe(false)
    })
  })
})
