/**
 * Security Test Utilities
 *
 * Helper functions for testing security features.
 * Provides mock request builders, CSRF token utilities, and environment setup.
 */

import { NextRequest, NextResponse } from 'next/server'
import { createHmac, randomBytes } from 'crypto'

// ============================================================================
// CSRF Token Utilities
// ============================================================================

const DEFAULT_CSRF_SECRET = 'test-csrf-secret-for-testing-only'

/**
 * Generate a valid CSRF token for testing
 */
export function generateTestCsrfToken(secret: string = DEFAULT_CSRF_SECRET): string {
  const timestamp = Date.now().toString(36)
  const random = randomBytes(16).toString('hex')
  const data = `${timestamp}:${random}`

  const hmac = createHmac('sha256', secret)
  hmac.update(data)
  const signature = hmac.digest('hex').substring(0, 16)

  return `${data}:${signature}`
}

/**
 * Generate an expired CSRF token for testing
 */
export function generateExpiredCsrfToken(secret: string = DEFAULT_CSRF_SECRET): string {
  // Use timestamp from 2 hours ago
  const timestamp = (Date.now() - 2 * 60 * 60 * 1000).toString(36)
  const random = randomBytes(16).toString('hex')
  const data = `${timestamp}:${random}`

  const hmac = createHmac('sha256', secret)
  hmac.update(data)
  const signature = hmac.digest('hex').substring(0, 16)

  return `${data}:${signature}`
}

/**
 * Generate an invalid CSRF token (wrong signature)
 */
export function generateInvalidCsrfToken(): string {
  const timestamp = Date.now().toString(36)
  const random = randomBytes(16).toString('hex')
  return `${timestamp}:${random}:invalidsignature`
}

// ============================================================================
// Mock Request Builders
// ============================================================================

export interface MockRequestOptions {
  method?: string
  url?: string
  headers?: Record<string, string>
  body?: unknown
  cookies?: Record<string, string>
  ip?: string
}

/**
 * Create a mock NextRequest for testing
 */
export function createMockRequest(options: MockRequestOptions = {}): NextRequest {
  const {
    method = 'GET',
    url = 'https://test.example.com/api/test',
    headers = {},
    ip,
  } = options

  const requestHeaders = new Headers()

  // Set IP headers if provided
  if (ip) {
    requestHeaders.set('x-forwarded-for', ip)
    requestHeaders.set('x-real-ip', ip)
  }

  // Set custom headers
  Object.entries(headers).forEach(([key, value]) => {
    requestHeaders.set(key, value)
  })

  const request = new NextRequest(url, {
    method,
    headers: requestHeaders,
  })

  // Mock cookies
  if (options.cookies) {
    Object.defineProperty(request, 'cookies', {
      value: {
        get: (name: string) => {
          const value = options.cookies?.[name]
          return value ? { value } : undefined
        },
        getAll: () =>
          Object.entries(options.cookies || {}).map(([name, value]) => ({ name, value })),
      },
    })
  }

  return request
}

/**
 * Create a mock request with CSRF protection
 */
export function createCsrfProtectedRequest(
  method: string,
  options: Omit<MockRequestOptions, 'method'> = {},
): NextRequest {
  const token = generateTestCsrfToken()

  return createMockRequest({
    ...options,
    method,
    headers: {
      ...options.headers,
      'X-CSRF-Token': token,
      origin: 'https://test.example.com',
    },
    cookies: {
      ...options.cookies,
      'csrf-token': token,
    },
  })
}

/**
 * Create a mock request simulating server-to-server call
 */
export function createServerToServerRequest(
  method: string,
  options: Omit<MockRequestOptions, 'method'> = {},
): NextRequest {
  return createMockRequest({
    ...options,
    method,
    headers: {
      ...options.headers,
      authorization: 'Bearer test-api-token',
      'content-type': 'application/json',
      // No origin header for server-to-server
    },
  })
}

/**
 * Create a mock request from specific IP
 */
export function createRequestFromIp(ip: string, options: MockRequestOptions = {}): NextRequest {
  return createMockRequest({
    ...options,
    ip,
  })
}

// ============================================================================
// Environment Setup
// ============================================================================

export interface SecurityTestEnv {
  CSRF_SECRET?: string
  PAYLOAD_SECRET?: string
  BLOCKED_IPS?: string
  SEND_EMAIL_ALLOWED_IPS?: string
  ADMIN_ALLOWED_IPS?: string
  WEBHOOK_ALLOWED_IPS?: string
  REDIS_HOST?: string
  REDIS_PORT?: string
}

/**
 * Setup environment variables for security tests
 */
export function setupSecurityEnv(env: SecurityTestEnv): void {
  Object.entries(env).forEach(([key, value]) => {
    if (value !== undefined) {
      process.env[key] = value
    }
  })
}

/**
 * Clear security-related environment variables
 */
export function clearSecurityEnv(): void {
  const securityEnvVars = [
    'CSRF_SECRET',
    'BLOCKED_IPS',
    'SEND_EMAIL_ALLOWED_IPS',
    'ADMIN_ALLOWED_IPS',
    'WEBHOOK_ALLOWED_IPS',
  ]

  securityEnvVars.forEach((key) => {
    delete process.env[key]
  })
}

// ============================================================================
// Response Helpers
// ============================================================================

/**
 * Extract rate limit info from response headers
 */
export function extractRateLimitInfo(response: NextResponse): {
  limit: number | null
  remaining: number | null
  reset: number | null
  retryAfter: number | null
} {
  return {
    limit: response.headers.get('X-RateLimit-Limit')
      ? parseInt(response.headers.get('X-RateLimit-Limit')!, 10)
      : null,
    remaining: response.headers.get('X-RateLimit-Remaining')
      ? parseInt(response.headers.get('X-RateLimit-Remaining')!, 10)
      : null,
    reset: response.headers.get('X-RateLimit-Reset')
      ? parseInt(response.headers.get('X-RateLimit-Reset')!, 10)
      : null,
    retryAfter: response.headers.get('Retry-After')
      ? parseInt(response.headers.get('Retry-After')!, 10)
      : null,
  }
}

/**
 * Parse JSON response body
 */
export async function parseJsonResponse<T>(response: NextResponse): Promise<T> {
  const text = await response.text()
  return JSON.parse(text) as T
}

// ============================================================================
// Rate Limit Testing
// ============================================================================

/**
 * Make multiple requests to test rate limiting
 */
export async function exhaustRateLimit(
  requestFn: () => Promise<NextResponse>,
  maxRequests: number,
): Promise<NextResponse[]> {
  const responses: NextResponse[] = []

  for (let i = 0; i <= maxRequests; i++) {
    responses.push(await requestFn())
  }

  return responses
}

// ============================================================================
// Assertion Helpers
// ============================================================================

/**
 * Assert response has security headers
 */
export function assertSecurityHeaders(response: NextResponse): void {
  const headers = response.headers

  // Rate limit headers should be present
  const hasRateLimitHeaders =
    headers.has('X-RateLimit-Limit') || headers.has('X-RateLimit-Remaining')

  if (!hasRateLimitHeaders) {
    throw new Error('Response missing rate limit headers')
  }
}

/**
 * Assert response is rate limited (429)
 */
export function assertRateLimited(response: NextResponse): void {
  if (response.status !== 429) {
    throw new Error(`Expected 429 status, got ${response.status}`)
  }

  if (!response.headers.has('Retry-After')) {
    throw new Error('Rate limited response missing Retry-After header')
  }
}

/**
 * Assert response is CSRF blocked (403)
 */
export function assertCsrfBlocked(response: NextResponse): void {
  if (response.status !== 403) {
    throw new Error(`Expected 403 status for CSRF block, got ${response.status}`)
  }
}

/**
 * Assert response is IP blocked (403)
 */
export function assertIpBlocked(response: NextResponse): void {
  if (response.status !== 403) {
    throw new Error(`Expected 403 status for IP block, got ${response.status}`)
  }
}

// ============================================================================
// Test Data Generators
// ============================================================================

/**
 * Generate a random IP address
 */
export function randomIp(): string {
  return `${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}`
}

/**
 * Generate a unique test identifier
 */
export function uniqueTestId(prefix: string = 'test'): string {
  return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
}

/**
 * Generate test email
 */
export function testEmail(domain: string = 'test.example.com'): string {
  return `${uniqueTestId('user')}@${domain}`
}

// ============================================================================
// Mock Payload Instance
// ============================================================================

export interface MockPayloadInstance {
  auth: ReturnType<typeof import('vitest').vi.fn>
  find: ReturnType<typeof import('vitest').vi.fn>
  create: ReturnType<typeof import('vitest').vi.fn>
  update: ReturnType<typeof import('vitest').vi.fn>
  delete: ReturnType<typeof import('vitest').vi.fn>
  login: ReturnType<typeof import('vitest').vi.fn>
}

/**
 * Create a mock Payload instance for testing
 */
export function createMockPayload(
  vi: typeof import('vitest').vi,
): MockPayloadInstance {
  return {
    auth: vi.fn().mockResolvedValue({ user: null }),
    find: vi.fn().mockResolvedValue({ docs: [], totalDocs: 0 }),
    create: vi.fn().mockResolvedValue({ id: 1 }),
    update: vi.fn().mockResolvedValue({ id: 1 }),
    delete: vi.fn().mockResolvedValue({ id: 1 }),
    login: vi.fn().mockResolvedValue({
      user: { id: 1, email: 'test@example.com' },
      token: 'mock-token',
      exp: Math.floor(Date.now() / 1000) + 7200,
    }),
  }
}
