/**
 * Tenant Access Control Unit Tests
 *
 * Tests for the tenant access control functions in src/lib/tenantAccess.ts
 * Covers: getTenantIdFromHost, tenantScopedPublicRead, authenticatedOnly
 */

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

// ============================================================================
// Import the actual functions to test
// ============================================================================

import {
  getTenantIdFromHost,
  tenantScopedPublicRead,
  authenticatedOnly,
} from '@/lib/tenantAccess'

// ============================================================================
// getTenantIdFromHost Tests
// ============================================================================

describe('getTenantIdFromHost', () => {
  describe('Host Header Extraction', () => {
    it('extracts tenant ID from valid domain', async () => {
      const request = createAnonymousRequest('porwoll.de', [TEST_TENANTS.porwoll])
      const tenantId = await getTenantIdFromHost(request as unknown as PayloadRequest)

      expect(tenantId).toBe(1)
    })

    it('extracts tenant ID with port in host', async () => {
      const request = createAnonymousRequest('porwoll.de:3000', [TEST_TENANTS.porwoll])
      const tenantId = await getTenantIdFromHost(request as unknown as PayloadRequest)

      expect(tenantId).toBe(1)
    })

    it('extracts tenant ID with www prefix', async () => {
      const tenant = createMockTenant({
        id: 1,
        domains: [{ domain: 'porwoll.de' }],
      })
      const request = createAnonymousRequest('www.porwoll.de', [tenant])
      const tenantId = await getTenantIdFromHost(request as unknown as PayloadRequest)

      expect(tenantId).toBe(1)
    })

    it('handles uppercase domain', async () => {
      const request = createAnonymousRequest('PORWOLL.DE', [TEST_TENANTS.porwoll])
      const tenantId = await getTenantIdFromHost(request as unknown as PayloadRequest)

      expect(tenantId).toBe(1)
    })

    it('returns null for missing host header', async () => {
      const request = createMockPayloadRequest(null, { tenants: [TEST_TENANTS.porwoll] })
      const tenantId = await getTenantIdFromHost(request as unknown as PayloadRequest)

      expect(tenantId).toBeNull()
    })

    it('returns null for unknown domain', async () => {
      const request = createAnonymousRequest('unknown-domain.com', [TEST_TENANTS.porwoll])
      const tenantId = await getTenantIdFromHost(request as unknown as PayloadRequest)

      expect(tenantId).toBeNull()
    })

    it('returns null for empty host header', async () => {
      const request = createMockPayloadRequest(null, { host: '', tenants: [TEST_TENANTS.porwoll] })
      const tenantId = await getTenantIdFromHost(request as unknown as PayloadRequest)

      expect(tenantId).toBeNull()
    })
  })

  describe('Multiple Tenants', () => {
    const allTenants = [TEST_TENANTS.porwoll, TEST_TENANTS.c2s, TEST_TENANTS.gunshin]

    it('resolves correct tenant from multiple options', async () => {
      const request = createAnonymousRequest('complexcaresolutions.de', allTenants)
      const tenantId = await getTenantIdFromHost(request as unknown as PayloadRequest)

      expect(tenantId).toBe(4) // c2s tenant ID
    })

    it('resolves each tenant correctly', async () => {
      const porwollReq = createAnonymousRequest('porwoll.de', allTenants)
      const gunshinReq = createAnonymousRequest('gunshin.de', allTenants)

      const porwollId = await getTenantIdFromHost(porwollReq as unknown as PayloadRequest)
      const gunshinId = await getTenantIdFromHost(gunshinReq as unknown as PayloadRequest)

      expect(porwollId).toBe(1)
      expect(gunshinId).toBe(5)
    })
  })

  describe('Error Handling', () => {
    it('returns null when payload.find throws', async () => {
      const request = createMockPayloadRequest(null, { host: 'test.com' })
      request.payload.find = vi.fn().mockRejectedValue(new Error('Database error'))

      const tenantId = await getTenantIdFromHost(request as unknown as PayloadRequest)

      expect(tenantId).toBeNull()
    })

    it('returns null for tenant without ID', async () => {
      const request = createMockPayloadRequest(null, { host: 'test.com' })
      request.payload.find = vi.fn().mockResolvedValue({
        docs: [{ name: 'Test Tenant' }], // Missing ID
        totalDocs: 1,
      })

      const tenantId = await getTenantIdFromHost(request as unknown as PayloadRequest)

      expect(tenantId).toBeNull()
    })
  })
})

// ============================================================================
// tenantScopedPublicRead Tests
// ============================================================================

describe('tenantScopedPublicRead', () => {
  describe('Authenticated Users', () => {
    it('grants full access to super admin', async () => {
      const request = createMockPayloadRequest(TEST_USERS.superAdmin)
      const result = await executeAccess(tenantScopedPublicRead, request)

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

    it('grants full access to regular authenticated user', async () => {
      const request = createMockPayloadRequest(TEST_USERS.porwollUser)
      const result = await executeAccess(tenantScopedPublicRead, request)

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

    it('grants full access to multi-tenant user', async () => {
      const request = createMockPayloadRequest(TEST_USERS.multiTenantUser)
      const result = await executeAccess(tenantScopedPublicRead, request)

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

  describe('Anonymous Users', () => {
    it('returns tenant filter for valid domain', async () => {
      const request = createAnonymousRequest('porwoll.de', [TEST_TENANTS.porwoll])
      const result = await executeAccess(tenantScopedPublicRead, request)

      expect(hasFilteredAccess(result)).toBe(true)
      expect(getTenantIdFromFilter(result as Where)).toBe(1)
    })

    it('returns different tenant filter for different domain', async () => {
      const request = createAnonymousRequest('complexcaresolutions.de', [TEST_TENANTS.c2s])
      const result = await executeAccess(tenantScopedPublicRead, request)

      expect(hasFilteredAccess(result)).toBe(true)
      expect(getTenantIdFromFilter(result as Where)).toBe(4)
    })

    it('denies access for unknown domain', async () => {
      const request = createAnonymousRequest('malicious-site.com', [TEST_TENANTS.porwoll])
      const result = await executeAccess(tenantScopedPublicRead, request)

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

    it('denies access when no host header', async () => {
      const request = createMockPayloadRequest(null)
      const result = await executeAccess(tenantScopedPublicRead, request)

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

  describe('Filter Structure', () => {
    it('returns correct where clause structure', async () => {
      const request = createAnonymousRequest('gunshin.de', [TEST_TENANTS.gunshin])
      const result = await executeAccess(tenantScopedPublicRead, request)

      expect(result).toEqual({
        tenant: {
          equals: 5,
        },
      })
    })
  })
})

// ============================================================================
// authenticatedOnly Tests
// ============================================================================

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

      expect(result).toBe(true)
    })

    it('grants access to regular user', async () => {
      const request = createMockPayloadRequest(TEST_USERS.porwollUser)
      const result = await executeAccess(authenticatedOnly, request)

      expect(result).toBe(true)
    })

    it('grants access to user with minimal data', async () => {
      const minimalUser = { id: 99, email: 'minimal@test.com' }
      const request = createMockPayloadRequest(minimalUser)
      const result = await executeAccess(authenticatedOnly, request)

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

  describe('Denies Access', () => {
    it('denies access to anonymous user', async () => {
      const request = createMockPayloadRequest(null)
      const result = await executeAccess(authenticatedOnly, request)

      expect(result).toBe(false)
    })

    it('denies access when user is undefined', async () => {
      const request = createMockPayloadRequest(undefined as unknown as null)
      const result = await executeAccess(authenticatedOnly, request)

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

// ============================================================================
// Edge Cases & Integration Scenarios
// ============================================================================

describe('Access Control Integration Scenarios', () => {
  describe('Tenant Assignment Formats', () => {
    it('handles tenant object format { tenant: { id } }', () => {
      const user = createTenantUser([1, 4])

      expect(user.tenants).toEqual([{ tenant: { id: 1 } }, { tenant: { id: 4 } }])
    })

    it('handles tenant primitive format { tenant: number }', () => {
      const user = createTenantUserPrimitive([1, 4])

      expect(user.tenants).toEqual([{ tenant: 1 }, { tenant: 4 }])
    })
  })

  describe('Real-World Scenarios', () => {
    it('public blog post access from tenant domain', async () => {
      // Anonymous user visiting porwoll.de/blog
      const request = createAnonymousRequest('porwoll.de', [TEST_TENANTS.porwoll])
      const result = await executeAccess(tenantScopedPublicRead, request)

      // Should only see porwoll.de posts
      expect(hasFilteredAccess(result)).toBe(true)
      expect(getTenantIdFromFilter(result as Where)).toBe(1)
    })

    it('admin editing posts from any tenant', async () => {
      // Super admin in admin panel
      const request = createMockPayloadRequest(TEST_USERS.superAdmin)
      const result = await executeAccess(tenantScopedPublicRead, request)

      // Should see all posts
      expect(hasFullAccess(result)).toBe(true)
    })

    it('tenant user creating content', async () => {
      // User assigned to c2s tenant creating a post
      const request = createMockPayloadRequest(TEST_USERS.c2sUser)
      const result = await executeAccess(authenticatedOnly, request)

      // Should be allowed to create
      expect(result).toBe(true)
    })

    it('cross-origin attack prevention', async () => {
      // Request from malicious site
      const request = createAnonymousRequest('evil-site.com', [
        TEST_TENANTS.porwoll,
        TEST_TENANTS.c2s,
        TEST_TENANTS.gunshin,
      ])
      const result = await executeAccess(tenantScopedPublicRead, request)

      // Should be denied
      expect(hasNoAccess(result)).toBe(true)
    })
  })
})
