/**
 * Cleanup Service
 *
 * Führt die eigentliche Datenbereinigung durch.
 * Wird vom Retention Worker aufgerufen.
 */

import type { Payload } from 'payload'
import type { Config } from '@/payload-types'
import { retentionPolicies, getCutoffDate, mediaOrphanConfig } from './retention-config'

// Type für dynamische Collection-Zugriffe
type CollectionSlug = keyof Config['collections']

export interface CleanupResult {
  collection: string
  deletedCount: number
  errorCount: number
  errors: string[]
  duration: number
}

export interface MediaOrphanResult {
  deletedCount: number
  deletedFiles: string[]
  errorCount: number
  errors: string[]
  duration: number
}

/**
 * Löscht alte Einträge aus einer Collection basierend auf dem Datum
 */
export async function cleanupCollection(
  payload: Payload,
  collection: string,
  cutoffDate: Date,
  options?: {
    dateField?: string
    batchSize?: number
  }
): Promise<CleanupResult> {
  const startTime = Date.now()
  const dateField = options?.dateField || 'createdAt'
  const batchSize = options?.batchSize || 100

  const result: CleanupResult = {
    collection,
    deletedCount: 0,
    errorCount: 0,
    errors: [],
    duration: 0,
  }

  console.log(`[CleanupService] Starting cleanup for ${collection}`)
  console.log(`[CleanupService] Cutoff date: ${cutoffDate.toISOString()}`)
  console.log(`[CleanupService] Date field: ${dateField}, Batch size: ${batchSize}`)

  try {
    let hasMore = true

    while (hasMore) {
      // Finde alte Einträge
      const oldEntries = await payload.find({
        collection: collection as CollectionSlug,
        where: {
          [dateField]: {
            less_than: cutoffDate.toISOString(),
          },
        },
        limit: batchSize,
        depth: 0, // Keine Relation-Auflösung für Performance
      })

      if (oldEntries.docs.length === 0) {
        hasMore = false
        break
      }

      console.log(`[CleanupService] Found ${oldEntries.docs.length} entries to delete`)

      // Lösche in Batches
      for (const doc of oldEntries.docs) {
        try {
          await payload.delete({
            collection: collection as CollectionSlug,
            id: doc.id,
            overrideAccess: true, // System-Löschung
          })
          result.deletedCount++
        } catch (error) {
          result.errorCount++
          const errorMsg = error instanceof Error ? error.message : 'Unknown error'
          result.errors.push(`Failed to delete ${collection}/${doc.id}: ${errorMsg}`)
          console.error(`[CleanupService] Error deleting ${collection}/${doc.id}:`, errorMsg)
        }
      }

      // Prüfe ob es mehr Einträge gibt
      hasMore = oldEntries.docs.length === batchSize
    }
  } catch (error) {
    const errorMsg = error instanceof Error ? error.message : 'Unknown error'
    result.errors.push(`Query failed: ${errorMsg}`)
    result.errorCount++
    console.error(`[CleanupService] Query error for ${collection}:`, errorMsg)
  }

  result.duration = Date.now() - startTime
  console.log(
    `[CleanupService] Cleanup for ${collection} completed: ${result.deletedCount} deleted, ${result.errorCount} errors, ${result.duration}ms`
  )

  return result
}

/**
 * Löscht ConsentLogs basierend auf expiresAt (bereits abgelaufene)
 * Spezielle Behandlung für WORM-Collection
 */
export async function cleanupExpiredConsentLogs(
  payload: Payload,
  batchSize = 50
): Promise<CleanupResult> {
  const startTime = Date.now()
  const now = new Date()

  const result: CleanupResult = {
    collection: 'consent-logs',
    deletedCount: 0,
    errorCount: 0,
    errors: [],
    duration: 0,
  }

  console.log(`[CleanupService] Starting consent-logs cleanup (expired before ${now.toISOString()})`)

  try {
    let hasMore = true

    while (hasMore) {
      // Finde abgelaufene Consent-Logs
      const expiredLogs = await payload.find({
        collection: 'consent-logs',
        where: {
          expiresAt: {
            less_than: now.toISOString(),
          },
        },
        limit: batchSize,
        depth: 0,
      })

      if (expiredLogs.docs.length === 0) {
        hasMore = false
        break
      }

      console.log(`[CleanupService] Found ${expiredLogs.docs.length} expired consent logs`)

      // Lösche via Direct Access (WORM Collection hat delete: false)
      // Verwende overrideAccess für System-Löschung
      for (const doc of expiredLogs.docs) {
        try {
          await payload.delete({
            collection: 'consent-logs',
            id: doc.id,
            overrideAccess: true, // Bypass WORM protection für Retention
          })
          result.deletedCount++
        } catch (error) {
          result.errorCount++
          const errorMsg = error instanceof Error ? error.message : 'Unknown error'
          result.errors.push(`Failed to delete consent-logs/${doc.id}: ${errorMsg}`)
          console.error(`[CleanupService] Error deleting consent-logs/${doc.id}:`, errorMsg)
        }
      }

      hasMore = expiredLogs.docs.length === batchSize
    }
  } catch (error) {
    const errorMsg = error instanceof Error ? error.message : 'Unknown error'
    result.errors.push(`Query failed: ${errorMsg}`)
    result.errorCount++
    console.error(`[CleanupService] Query error for consent-logs:`, errorMsg)
  }

  result.duration = Date.now() - startTime
  console.log(
    `[CleanupService] Consent-logs cleanup completed: ${result.deletedCount} deleted, ${result.errorCount} errors, ${result.duration}ms`
  )

  return result
}

/**
 * Findet und löscht verwaiste Media-Dateien
 * (Dateien, die von keinem Dokument mehr referenziert werden)
 */
export async function cleanupOrphanedMedia(
  payload: Payload,
  options?: {
    minAgeDays?: number
    batchSize?: number
  }
): Promise<MediaOrphanResult> {
  const startTime = Date.now()
  const minAgeDays = options?.minAgeDays || mediaOrphanConfig.minAgeDays
  const batchSize = options?.batchSize || mediaOrphanConfig.batchSize

  const result: MediaOrphanResult = {
    deletedCount: 0,
    deletedFiles: [],
    errorCount: 0,
    errors: [],
    duration: 0,
  }

  // Cutoff für Mindestalter
  const cutoff = getCutoffDate(minAgeDays)

  console.log(`[CleanupService] Starting media orphan cleanup`)
  console.log(`[CleanupService] Min age: ${minAgeDays} days (cutoff: ${cutoff.toISOString()})`)

  try {
    // Hole alle Media älter als Cutoff
    let offset = 0
    let hasMore = true

    while (hasMore) {
      const mediaItems = await payload.find({
        collection: 'media',
        where: {
          createdAt: {
            less_than: cutoff.toISOString(),
          },
        },
        limit: batchSize,
        page: Math.floor(offset / batchSize) + 1,
        depth: 0,
      })

      if (mediaItems.docs.length === 0) {
        hasMore = false
        break
      }

      console.log(`[CleanupService] Checking ${mediaItems.docs.length} media items for orphans`)

      // Prüfe jedes Media-Item auf Referenzen
      for (const media of mediaItems.docs) {
        const isOrphan = await checkIfMediaIsOrphan(payload, media.id)

        if (isOrphan) {
          try {
            // Lösche das Media-Item (Payload löscht auch die Dateien)
            await payload.delete({
              collection: 'media',
              id: media.id,
              overrideAccess: true,
            })
            result.deletedCount++
            result.deletedFiles.push(
              typeof media.filename === 'string' ? media.filename : String(media.id)
            )
            console.log(`[CleanupService] Deleted orphan media: ${media.id}`)
          } catch (error) {
            result.errorCount++
            const errorMsg = error instanceof Error ? error.message : 'Unknown error'
            result.errors.push(`Failed to delete media/${media.id}: ${errorMsg}`)
            console.error(`[CleanupService] Error deleting media/${media.id}:`, errorMsg)
          }
        }
      }

      offset += mediaItems.docs.length
      hasMore = mediaItems.docs.length === batchSize
    }
  } catch (error) {
    const errorMsg = error instanceof Error ? error.message : 'Unknown error'
    result.errors.push(`Query failed: ${errorMsg}`)
    result.errorCount++
    console.error(`[CleanupService] Query error for media orphans:`, errorMsg)
  }

  result.duration = Date.now() - startTime
  console.log(
    `[CleanupService] Media orphan cleanup completed: ${result.deletedCount} deleted, ${result.errorCount} errors, ${result.duration}ms`
  )

  return result
}

/**
 * Prüft ob ein Media-Item von keinem Dokument referenziert wird
 */
async function checkIfMediaIsOrphan(
  payload: Payload,
  mediaId: number | string
): Promise<boolean> {
  const collections = mediaOrphanConfig.referencingCollections

  for (const collection of collections) {
    try {
      // Suche nach Referenzen in verschiedenen Feldtypen
      // Media kann als relationship, in Blocks, oder in Rich-Text referenziert werden
      const references = await payload.find({
        collection: collection as CollectionSlug,
        where: {
          or: [
            // Direct relationship fields (common patterns)
            { image: { equals: mediaId } },
            { featuredImage: { equals: mediaId } },
            { thumbnail: { equals: mediaId } },
            { logo: { equals: mediaId } },
            { avatar: { equals: mediaId } },
            { photo: { equals: mediaId } },
            { cover: { equals: mediaId } },
            { icon: { equals: mediaId } },
            { backgroundImage: { equals: mediaId } },
            { heroImage: { equals: mediaId } },
            { ogImage: { equals: mediaId } },
            // Gallery/Array fields (check if contains)
            { 'gallery.image': { equals: mediaId } },
            { 'images.image': { equals: mediaId } },
            { 'slides.image': { equals: mediaId } },
            { 'slides.backgroundImage': { equals: mediaId } },
            { 'slides.mobileBackgroundImage': { equals: mediaId } },
          ],
        },
        limit: 1,
        depth: 0,
      })

      if (references.totalDocs > 0) {
        return false // Hat Referenzen, ist kein Orphan
      }
    } catch {
      // Collection existiert möglicherweise nicht oder Feld nicht vorhanden
      // Ignorieren und mit nächster Collection fortfahren
    }
  }

  return true // Keine Referenzen gefunden
}

/**
 * Führt alle konfigurierten Retention Policies aus
 */
export async function runFullRetention(payload: Payload): Promise<{
  results: CleanupResult[]
  mediaOrphanResult?: MediaOrphanResult
  totalDeleted: number
  totalErrors: number
  duration: number
}> {
  const startTime = Date.now()
  const results: CleanupResult[] = []
  let totalDeleted = 0
  let totalErrors = 0

  console.log('[CleanupService] Starting full retention run')
  console.log(`[CleanupService] Policies: ${retentionPolicies.map((p) => p.name).join(', ')}`)

  // Führe Collection Cleanups durch
  for (const policy of retentionPolicies) {
    // ConsentLogs haben spezielle Behandlung
    if (policy.collection === 'consent-logs') {
      const consentResult = await cleanupExpiredConsentLogs(payload, policy.batchSize)
      results.push(consentResult)
      totalDeleted += consentResult.deletedCount
      totalErrors += consentResult.errorCount
    } else {
      const cutoff = getCutoffDate(policy.retentionDays)
      const result = await cleanupCollection(payload, policy.collection, cutoff, {
        dateField: policy.dateField,
        batchSize: policy.batchSize,
      })
      results.push(result)
      totalDeleted += result.deletedCount
      totalErrors += result.errorCount
    }
  }

  // Media Orphan Cleanup
  const mediaOrphanResult = await cleanupOrphanedMedia(payload)
  totalDeleted += mediaOrphanResult.deletedCount
  totalErrors += mediaOrphanResult.errorCount

  const duration = Date.now() - startTime

  console.log(
    `[CleanupService] Full retention completed: ${totalDeleted} total deleted, ${totalErrors} total errors, ${duration}ms`
  )

  return {
    results,
    mediaOrphanResult,
    totalDeleted,
    totalErrors,
    duration,
  }
}
