/**
 * CSRF Protection Service
 *
 * Implementiert CSRF-Schutz für Browser-basierte API-Aufrufe.
 * Verwendet Double Submit Cookie Pattern + Origin-Header-Validierung.
 */

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

// CSRF-Token Secret - MUSS in Production konfiguriert sein
const CSRF_TOKEN_HEADER = 'X-CSRF-Token'
const CSRF_COOKIE_NAME = 'csrf-token'
const TOKEN_EXPIRY_MS = 60 * 60 * 1000 // 1 Stunde

/**
 * Ermittelt das CSRF-Secret mit strikter Validierung
 *
 * SECURITY: In Production wird ein Fehler geworfen wenn kein Secret konfiguriert ist.
 * Ein vorhersagbares Secret würde Angreifern erlauben, CSRF-Tokens offline zu generieren.
 */
function getCsrfSecret(): string {
  const secret = process.env.CSRF_SECRET || process.env.PAYLOAD_SECRET

  if (secret) {
    return secret
  }

  // In Production: Fehler werfen - CSRF ohne Secret ist wertlos
  if (process.env.NODE_ENV === 'production') {
    const errorMsg = '[SECURITY CRITICAL] CSRF_SECRET or PAYLOAD_SECRET must be configured in production! ' +
      'Without a secret, CSRF tokens can be forged by attackers. ' +
      'Set CSRF_SECRET in your environment variables.'
    console.error(errorMsg)
    throw new Error(errorMsg)
  }

  // In Development: Warnen und Development-Secret verwenden
  console.warn(
    '[Security Warning] No CSRF_SECRET or PAYLOAD_SECRET configured. ' +
    'Using development-only secret. DO NOT use in production!'
  )
  return 'INSECURE-DEV-ONLY-' + randomBytes(16).toString('hex')
}

// Secret einmalig beim Import ermitteln (wirft in Production wenn nicht konfiguriert)
let CSRF_SECRET: string
try {
  CSRF_SECRET = getCsrfSecret()
} catch (error) {
  // In Production: Re-throw damit der Server nicht startet
  if (process.env.NODE_ENV === 'production') {
    throw error
  }
  // In Development: Fallback für Edge-Cases
  CSRF_SECRET = 'INSECURE-DEV-FALLBACK'
}

/**
 * Generiert ein neues CSRF-Token
 */
export function generateCsrfToken(): string {
  const timestamp = Date.now().toString(36)
  const random = randomBytes(16).toString('hex')
  const data = `${timestamp}:${random}`

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

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

/**
 * Validiert ein CSRF-Token
 */
export function validateCsrfToken(token: string): { valid: boolean; reason?: string } {
  if (!token) {
    return { valid: false, reason: 'No token provided' }
  }

  const parts = token.split(':')
  if (parts.length !== 3) {
    return { valid: false, reason: 'Invalid token format' }
  }

  const [timestamp, random, signature] = parts

  // Signatur prüfen
  const hmac = createHmac('sha256', CSRF_SECRET)
  hmac.update(`${timestamp}:${random}`)
  const expectedSignature = hmac.digest('hex').substring(0, 16)

  if (signature !== expectedSignature) {
    return { valid: false, reason: 'Invalid signature' }
  }

  // Ablaufzeit prüfen
  const tokenTime = parseInt(timestamp, 36)
  if (isNaN(tokenTime)) {
    return { valid: false, reason: 'Invalid timestamp' }
  }

  if (Date.now() - tokenTime > TOKEN_EXPIRY_MS) {
    return { valid: false, reason: 'Token expired' }
  }

  return { valid: true }
}

/**
 * Erlaubte Origins für CSRF-Validierung
 */
function getAllowedOrigins(): string[] {
  const origins = [
    process.env.PAYLOAD_PUBLIC_SERVER_URL,
    process.env.NEXT_PUBLIC_SERVER_URL,
    'http://localhost:3000',
    'http://localhost:3001',
  ].filter((origin): origin is string => !!origin)

  // Zusätzliche Origins aus CORS-Konfiguration
  const corsOrigins = process.env.CORS_ORIGINS?.split(',').map((o) => o.trim()) || []

  return [...new Set([...origins, ...corsOrigins])]
}

/**
 * Validiert den Origin-Header
 */
export function validateOrigin(origin: string | null): { valid: boolean; reason?: string } {
  if (!origin) {
    // Requests ohne Origin (z.B. Server-to-Server) können erlaubt werden
    return { valid: true }
  }

  const allowedOrigins = getAllowedOrigins()

  // Exakte Übereinstimmung
  if (allowedOrigins.includes(origin)) {
    return { valid: true }
  }

  // Subdomain-Matching für Produktions-Domains
  const productionDomains = ['pl.porwoll.tech', 'c2sgmbh.de', 'porwoll.de', 'complexcaresolutions.de', 'gunshin.de']

  for (const domain of productionDomains) {
    if (origin.endsWith(domain) && origin.startsWith('https://')) {
      return { valid: true }
    }
  }

  return { valid: false, reason: `Origin ${origin} not allowed` }
}

/**
 * Hauptvalidierungsfunktion für CSRF
 */
export function validateCsrf(req: NextRequest): {
  valid: boolean
  reason?: string
} {
  // 0. CI/Test-Modus: CSRF-Schutz deaktivieren wenn CI=true
  // Dies gilt für GitHub Actions E2E-Tests, wo CSRF-Token-Handling nicht praktikabel ist
  // BYPASS_CSRF='false' kann gesetzt werden um CSRF in CI zu aktivieren (für Security-Tests)
  if (process.env.CI === 'true' && process.env.BYPASS_CSRF !== 'false') {
    return { valid: true }
  }

  // 1. Safe Methods brauchen keine CSRF-Prüfung
  const safeMethod = ['GET', 'HEAD', 'OPTIONS'].includes(req.method)
  if (safeMethod) {
    return { valid: true }
  }

  // 2. Origin-Header validieren
  const origin = req.headers.get('origin')
  const originResult = validateOrigin(origin)
  if (!originResult.valid) {
    return originResult
  }

  // 3. Payload Admin Panel: Hat eigenen CSRF-Schutz
  // Requests vom Admin-Panel (Referer enthält /admin) werden durchgelassen
  const referer = req.headers.get('referer')
  if (referer) {
    try {
      const refererUrl = new URL(referer)
      if (refererUrl.pathname.startsWith('/admin')) {
        // Admin-Panel-Request - Payload hat eigenen CSRF-Schutz
        return { valid: true }
      }
    } catch {
      // Ungültige Referer-URL ignorieren
    }
  }

  // 4. Für API-Requests ohne Browser (Content-Type: application/json ohne Origin)
  // können wir auf CSRF-Token verzichten wenn Authorization-Header vorhanden
  const hasAuth = req.headers.get('authorization')
  const isJsonRequest = req.headers.get('content-type')?.includes('application/json')

  if (hasAuth && isJsonRequest && !origin) {
    // Server-to-Server Request mit Auth-Token
    return { valid: true }
  }

  // 5. Browser-Requests: CSRF-Token validieren
  const tokenFromHeader = req.headers.get(CSRF_TOKEN_HEADER)
  const tokenFromCookie = req.cookies.get(CSRF_COOKIE_NAME)?.value

  // Double Submit Cookie Pattern: Token muss in Header UND Cookie vorhanden sein
  if (!tokenFromHeader || !tokenFromCookie) {
    return { valid: false, reason: 'CSRF token missing' }
  }

  // Tokens müssen übereinstimmen
  if (tokenFromHeader !== tokenFromCookie) {
    return { valid: false, reason: 'CSRF token mismatch' }
  }

  // Token-Inhalt validieren
  return validateCsrfToken(tokenFromHeader)
}

/**
 * Setzt CSRF-Cookie in der Response
 */
export function setCsrfCookie(response: NextResponse): NextResponse {
  const token = generateCsrfToken()

  response.cookies.set(CSRF_COOKIE_NAME, token, {
    httpOnly: false, // JavaScript muss das Cookie lesen können
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'strict',
    path: '/',
    maxAge: TOKEN_EXPIRY_MS / 1000,
  })

  // Token auch im Header für SPA-Initialisierung
  response.headers.set(CSRF_TOKEN_HEADER, token)

  return response
}

/**
 * API-Endpoint zum Abrufen eines neuen CSRF-Tokens
 * GET /api/csrf-token
 */
export function handleCsrfTokenRequest(): NextResponse {
  const response = NextResponse.json({ success: true })
  return setCsrfCookie(response)
}

/**
 * Middleware-Helper für CSRF-Schutz
 */
export function csrfProtection(
  req: NextRequest,
  options: { skipPaths?: string[] } = {},
): { valid: boolean; reason?: string } {
  // Bestimmte Pfade überspringen (z.B. Webhooks)
  if (options.skipPaths) {
    const url = new URL(req.url)
    for (const path of options.skipPaths) {
      if (url.pathname.startsWith(path)) {
        return { valid: true }
      }
    }
  }

  return validateCsrf(req)
}

// ============================================================================
// Exports für Client-Side-Nutzung
// ============================================================================

export const CSRF_HEADER_NAME = CSRF_TOKEN_HEADER
export const CSRF_COOKIE = CSRF_COOKIE_NAME
