// src/collections/ConsentLogs.ts

import type { CollectionConfig, PayloadRequest } from 'payload'
import crypto from 'crypto'
import { env } from '../lib/envValidation'
import { authenticatedOnly } from '../lib/tenantAccess'

/**
 * Generiert einen täglichen, tenant-spezifischen Salt für IP-Anonymisierung.
 * Verwendet den sicher validierten Pepper aus der Umgebung.
 */
function getDailySalt(tenantId: string): string {
  const date = new Date().toISOString().split('T')[0] // YYYY-MM-DD
  return crypto
    .createHash('sha256')
    .update(`${env.IP_ANONYMIZATION_PEPPER}-${tenantId}-${date}`)
    .digest('hex')
}

/**
 * Anonymisiert eine IP-Adresse mit HMAC-SHA256.
 * Der Salt rotiert täglich und ist tenant-spezifisch.
 */
function anonymizeIp(ip: string, tenantId: string): string {
  const salt = getDailySalt(tenantId)
  return crypto.createHmac('sha256', salt).update(ip).digest('hex').substring(0, 32) // Gekürzt für Lesbarkeit
}

/**
 * Extrahiert die Client-IP aus dem Request.
 * Berücksichtigt Reverse-Proxy-Header.
 */
function extractClientIp(req: PayloadRequest): string {
  // X-Forwarded-For kann mehrere IPs enthalten (Client, Proxies)
  const forwarded = req.headers?.get?.('x-forwarded-for')
  if (typeof forwarded === 'string') {
    return forwarded.split(',')[0].trim()
  }

  // X-Real-IP (einzelne IP)
  const realIp = req.headers?.get?.('x-real-ip')
  if (typeof realIp === 'string') {
    return realIp.trim()
  }

  // Fallback: unknown (PayloadRequest hat keinen direkten IP-Zugriff mehr)
  return 'unknown'
}

/**
 * ConsentLogs Collection - WORM Audit Trail
 *
 * Implementiert das Write-Once-Read-Many Prinzip für DSGVO-Nachweispflicht.
 * Updates und Deletes sind auf API-Ebene deaktiviert.
 */
export const ConsentLogs: CollectionConfig = {
  slug: 'consent-logs',
  admin: {
    useAsTitle: 'consentId',
    group: 'Consent Management',
    description: 'WORM Audit-Trail für Cookie-Einwilligungen (unveränderbar)',
    defaultColumns: ['consentId', 'tenant', 'categories', 'revision', 'createdAt'],
  },

  // Performance: Keine Versionierung für Audit-Logs
  versions: false,

  access: {
    /**
     * CREATE: Nur mit gültigem API-Key.
     * Beide Seiten (Header UND Env-Variable) müssen existieren und übereinstimmen.
     */
    create: ({ req }) => {
      const headers = req.headers as Headers | Record<string, string | string[] | undefined>
      const apiKey =
        typeof headers.get === 'function'
          ? headers.get('x-api-key')
          : (headers as Record<string, string | string[] | undefined>)['x-api-key']

      // Strikte Validierung: Header muss existieren und non-empty sein
      if (!apiKey || typeof apiKey !== 'string') {
        return false
      }

      const trimmedKey = apiKey.trim()
      if (trimmedKey === '') {
        return false
      }

      // Vergleich mit validiertem Environment-Wert
      // (env.CONSENT_LOGGING_API_KEY ist garantiert non-empty durch envValidation)
      return trimmedKey === env.CONSENT_LOGGING_API_KEY
    },

    /**
     * READ: Nur authentifizierte Admin-User
     */
    read: authenticatedOnly,

    /**
     * UPDATE: WORM - Niemals erlaubt
     */
    update: () => false,

    /**
     * DELETE: WORM - Niemals über API erlaubt
     * (Nur via Retention-Job mit direktem DB-Zugriff)
     */
    delete: () => false,
  },

  hooks: {
    beforeChange: [
      ({ data, req, operation }) => {
        // Nur bei Neuanlage
        if (operation !== 'create') {
          return data
        }

        // 1. Server-generierte Consent-ID (Trust Boundary)
        data.consentId = crypto.randomUUID()

        // 2. IP anonymisieren
        const rawIp = data.ip || extractClientIp(req)
        const tenantId = typeof data.tenant === 'object' ? String(data.tenant.id) : String(data.tenant)

        data.anonymizedIp = anonymizeIp(rawIp, tenantId)

        // Rohe IP NIEMALS speichern
        delete data.ip

        // 3. Ablaufdatum setzen (3 Jahre Retention gemäß DSGVO)
        const expiresAt = new Date()
        expiresAt.setFullYear(expiresAt.getFullYear() + 3)
        data.expiresAt = expiresAt.toISOString()

        // 4. User Agent kürzen (Datensparsamkeit)
        if (data.userAgent && typeof data.userAgent === 'string') {
          data.userAgent = data.userAgent.substring(0, 500)
        }

        return data
      },
    ],
  },

  fields: [
    {
      name: 'consentId',
      type: 'text',
      required: true,
      unique: true,
      admin: {
        readOnly: true,
        description: 'Server-generierte eindeutige ID',
      },
    },
    {
      name: 'clientRef',
      type: 'text',
      admin: {
        readOnly: true,
        description: 'Client-seitige Referenz (Cookie-UUID) für Traceability',
      },
    },
    {
      name: 'tenant',
      type: 'relationship',
      relationTo: 'tenants',
      required: true,
      admin: {
        readOnly: true,
      },
    },
    {
      name: 'categories',
      type: 'json',
      required: true,
      admin: {
        readOnly: true,
        description: 'Akzeptierte Kategorien zum Zeitpunkt der Einwilligung',
      },
    },
    {
      name: 'revision',
      type: 'number',
      required: true,
      admin: {
        readOnly: true,
        description: 'Version der Konfiguration zum Zeitpunkt der Zustimmung',
      },
    },
    {
      name: 'userAgent',
      type: 'text',
      admin: {
        readOnly: true,
        description: 'Browser/Device (für Forensik und Bot-Erkennung)',
      },
    },
    {
      name: 'anonymizedIp',
      type: 'text',
      admin: {
        readOnly: true,
        description: 'HMAC-Hash der IP (täglich rotierender, tenant-spezifischer Salt)',
      },
    },
    {
      name: 'expiresAt',
      type: 'date',
      required: true,
      admin: {
        readOnly: true,
        description: 'Automatische Löschung nach 3 Jahren',
        date: {
          pickerAppearance: 'dayOnly',
        },
      },
    },
  ],
}
