// =============================================================================
// Loeschfristen Module - Compliance Check Engine
// Prueft Policies auf Vollstaendigkeit, Konsistenz und DSGVO-Konformitaet
// =============================================================================

import {
  LoeschfristPolicy,
  PolicyStatus,
  RetentionDriverType,
  isPolicyOverdue,
  getActiveLegalHolds,
  RETENTION_DRIVER_META,
} from './loeschfristen-types'

// =============================================================================
// TYPES
// =============================================================================

export type ComplianceIssueType =
  | 'MISSING_TRIGGER'
  | 'MISSING_LEGAL_BASIS'
  | 'OVERDUE_REVIEW'
  | 'NO_RESPONSIBLE'
  | 'LEGAL_HOLD_CONFLICT'
  | 'STALE_DRAFT'
  | 'UNCOVERED_VVT_CATEGORY'
  | 'MISSING_DELETION_METHOD'
  | 'MISSING_STORAGE_LOCATIONS'
  | 'EXCESSIVE_RETENTION'
  | 'MISSING_DATA_CATEGORIES'

export type ComplianceIssueSeverity = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'

export interface ComplianceIssue {
  id: string
  policyId: string
  policyName: string
  type: ComplianceIssueType
  severity: ComplianceIssueSeverity
  title: string
  description: string
  recommendation: string
}

export interface ComplianceCheckResult {
  issues: ComplianceIssue[]
  score: number // 0-100
  stats: {
    total: number
    passed: number
    failed: number
    bySeverity: Record<ComplianceIssueSeverity, number>
  }
}

// =============================================================================
// HELPERS
// =============================================================================

let issueCounter = 0

function createIssueId(): string {
  issueCounter++
  return `CI-${Date.now()}-${String(issueCounter).padStart(4, '0')}`
}

function createIssue(
  policy: LoeschfristPolicy,
  type: ComplianceIssueType,
  severity: ComplianceIssueSeverity,
  title: string,
  description: string,
  recommendation: string
): ComplianceIssue {
  return {
    id: createIssueId(),
    policyId: policy.policyId,
    policyName: policy.dataObjectName || policy.policyId,
    type,
    severity,
    title,
    description,
    recommendation,
  }
}

function daysBetween(dateStr: string, now: Date): number {
  const date = new Date(dateStr)
  const diffMs = now.getTime() - date.getTime()
  return Math.floor(diffMs / (1000 * 60 * 60 * 24))
}

// =============================================================================
// INDIVIDUAL CHECKS
// =============================================================================

/**
 * Check 1: MISSING_TRIGGER (HIGH)
 * Policy has no deletionTrigger set, or trigger is PURPOSE_END but no startEvent defined.
 */
function checkMissingTrigger(policy: LoeschfristPolicy): ComplianceIssue | null {
  if (!policy.deletionTrigger) {
    return createIssue(
      policy,
      'MISSING_TRIGGER',
      'HIGH',
      'Kein Loeschtrigger definiert',
      `Die Policy "${policy.dataObjectName}" hat keinen Loeschtrigger gesetzt. Ohne Trigger ist unklar, wann die Daten geloescht werden.`,
      'Definieren Sie einen Loeschtrigger (Zweckende, Aufbewahrungspflicht oder Legal Hold) fuer diese Policy.'
    )
  }

  if (policy.deletionTrigger === 'PURPOSE_END' && !policy.startEvent.trim()) {
    return createIssue(
      policy,
      'MISSING_TRIGGER',
      'HIGH',
      'Zweckende ohne Startereignis',
      `Die Policy "${policy.dataObjectName}" nutzt "Zweckende" als Trigger, hat aber kein Startereignis definiert. Ohne Startereignis laesst sich der Loeschzeitpunkt nicht berechnen.`,
      'Definieren Sie ein konkretes Startereignis (z.B. "Vertragsende", "Abmeldung", "Projektabschluss").'
    )
  }

  return null
}

/**
 * Check 2: MISSING_LEGAL_BASIS (HIGH)
 * Policy with RETENTION_DRIVER trigger but no retentionDriver set.
 */
function checkMissingLegalBasis(policy: LoeschfristPolicy): ComplianceIssue | null {
  if (policy.deletionTrigger === 'RETENTION_DRIVER' && !policy.retentionDriver) {
    return createIssue(
      policy,
      'MISSING_LEGAL_BASIS',
      'HIGH',
      'Aufbewahrungspflicht ohne Rechtsgrundlage',
      `Die Policy "${policy.dataObjectName}" hat "Aufbewahrungspflicht" als Trigger, aber keinen konkreten Aufbewahrungstreiber (z.B. AO 147, HGB 257) zugeordnet.`,
      'Waehlen Sie den passenden gesetzlichen Aufbewahrungstreiber aus oder wechseln Sie den Trigger-Typ.'
    )
  }

  return null
}

/**
 * Check 3: OVERDUE_REVIEW (MEDIUM)
 * Policy where nextReviewDate is in the past.
 */
function checkOverdueReview(policy: LoeschfristPolicy): ComplianceIssue | null {
  if (isPolicyOverdue(policy)) {
    const overdueDays = daysBetween(policy.nextReviewDate, new Date())
    return createIssue(
      policy,
      'OVERDUE_REVIEW',
      'MEDIUM',
      'Ueberfaellige Pruefung',
      `Die Policy "${policy.dataObjectName}" haette am ${new Date(policy.nextReviewDate).toLocaleDateString('de-DE')} geprueft werden muessen. Die Pruefung ist ${overdueDays} Tag(e) ueberfaellig.`,
      'Fuehren Sie umgehend eine Pruefung dieser Policy durch und aktualisieren Sie das naechste Pruefungsdatum.'
    )
  }

  return null
}

/**
 * Check 4: NO_RESPONSIBLE (MEDIUM)
 * Policy with no responsiblePerson AND no responsibleRole.
 */
function checkNoResponsible(policy: LoeschfristPolicy): ComplianceIssue | null {
  if (!policy.responsiblePerson.trim() && !policy.responsibleRole.trim()) {
    return createIssue(
      policy,
      'NO_RESPONSIBLE',
      'MEDIUM',
      'Keine verantwortliche Person/Rolle',
      `Die Policy "${policy.dataObjectName}" hat weder eine verantwortliche Person noch eine verantwortliche Rolle zugewiesen. Ohne Verantwortlichkeit kann die Loeschung nicht zuverlaessig durchgefuehrt werden.`,
      'Weisen Sie eine verantwortliche Person oder zumindest eine verantwortliche Rolle (z.B. "Datenschutzbeauftragter", "IT-Leitung") zu.'
    )
  }

  return null
}

/**
 * Check 5: LEGAL_HOLD_CONFLICT (CRITICAL)
 * Policy has active legal hold but deletionMethod is AUTO_DELETE.
 */
function checkLegalHoldConflict(policy: LoeschfristPolicy): ComplianceIssue | null {
  const activeHolds = getActiveLegalHolds(policy)
  if (activeHolds.length > 0 && policy.deletionMethod === 'AUTO_DELETE') {
    const holdReasons = activeHolds.map((h) => h.reason).join(', ')
    return createIssue(
      policy,
      'LEGAL_HOLD_CONFLICT',
      'CRITICAL',
      'Legal Hold mit automatischer Loeschung',
      `Die Policy "${policy.dataObjectName}" hat ${activeHolds.length} aktive(n) Legal Hold(s) (${holdReasons}), aber die Loeschmethode ist auf "Automatische Loeschung" gesetzt. Dies kann zu unbeabsichtigter Vernichtung von Beweismitteln fuehren.`,
      'Aendern Sie die Loeschmethode auf "Manuelle Pruefung & Loeschung" oder deaktivieren Sie die automatische Loeschung, solange der Legal Hold aktiv ist.'
    )
  }

  return null
}

/**
 * Check 6: STALE_DRAFT (LOW)
 * Policy in DRAFT status older than 90 days.
 */
function checkStaleDraft(policy: LoeschfristPolicy): ComplianceIssue | null {
  if (policy.status === 'DRAFT') {
    const ageInDays = daysBetween(policy.createdAt, new Date())
    if (ageInDays > 90) {
      return createIssue(
        policy,
        'STALE_DRAFT',
        'LOW',
        'Veralteter Entwurf',
        `Die Policy "${policy.dataObjectName}" ist seit ${ageInDays} Tagen im Entwurfsstatus. Entwuerfe, die laenger als 90 Tage nicht finalisiert werden, deuten auf unvollstaendige Dokumentation hin.`,
        'Finalisieren Sie den Entwurf und setzen Sie den Status auf "Aktiv", oder archivieren Sie die Policy, falls sie nicht mehr benoetigt wird.'
      )
    }
  }

  return null
}

/**
 * Check 8: MISSING_DELETION_METHOD (MEDIUM)
 * Active policy without a deletion method detail description.
 */
function checkMissingDeletionMethod(policy: LoeschfristPolicy): ComplianceIssue | null {
  if (policy.status === 'ACTIVE' && !policy.deletionMethodDetail.trim()) {
    return createIssue(
      policy,
      'MISSING_DELETION_METHOD',
      'MEDIUM',
      'Keine Loeschmethode beschrieben',
      `Die aktive Policy "${policy.dataObjectName}" hat keine detaillierte Beschreibung der Loeschmethode. Fuer ein auditfaehiges Loeschkonzept muss dokumentiert sein, wie die Loeschung technisch durchgefuehrt wird.`,
      'Ergaenzen Sie eine detaillierte Beschreibung der Loeschmethode (z.B. automatisches Loeschen durch Datenbank-Job, manuelle Pruefung durch Fachabteilung, kryptographische Loeschung).'
    )
  }

  return null
}

/**
 * Check 9: MISSING_STORAGE_LOCATIONS (MEDIUM)
 * Active policy without any documented storage locations.
 */
function checkMissingStorageLocations(policy: LoeschfristPolicy): ComplianceIssue | null {
  if (policy.status === 'ACTIVE' && policy.storageLocations.length === 0) {
    return createIssue(
      policy,
      'MISSING_STORAGE_LOCATIONS',
      'MEDIUM',
      'Keine Speicherorte dokumentiert',
      `Die aktive Policy "${policy.dataObjectName}" hat keine Speicherorte hinterlegt. Ohne Speicherort-Dokumentation ist unklar, wo die Daten gespeichert sind und wo die Loeschung durchgefuehrt werden muss.`,
      'Dokumentieren Sie mindestens einen Speicherort (z.B. Datenbank, Cloud-Speicher, E-Mail-System, Papierarchiv).'
    )
  }

  return null
}

/**
 * Check 10: EXCESSIVE_RETENTION (HIGH)
 * Retention duration exceeds 2x the legal default for the driver.
 */
function checkExcessiveRetention(policy: LoeschfristPolicy): ComplianceIssue | null {
  if (
    policy.retentionDriver &&
    policy.retentionDriver !== 'CUSTOM' &&
    policy.retentionDuration !== null &&
    policy.retentionUnit !== null
  ) {
    const meta = RETENTION_DRIVER_META[policy.retentionDriver]
    if (meta.defaultDuration !== null && meta.defaultUnit !== null) {
      // Normalize both to days for comparison
      const policyDays = toDays(policy.retentionDuration, policy.retentionUnit)
      const legalDays = toDays(meta.defaultDuration, meta.defaultUnit)

      if (legalDays > 0 && policyDays > legalDays * 2) {
        return createIssue(
          policy,
          'EXCESSIVE_RETENTION',
          'HIGH',
          'Ueberschreitung der gesetzlichen Aufbewahrungsfrist',
          `Die Policy "${policy.dataObjectName}" hat eine Aufbewahrungsdauer von ${policy.retentionDuration} ${policy.retentionUnit === 'YEARS' ? 'Jahren' : policy.retentionUnit === 'MONTHS' ? 'Monaten' : 'Tagen'}, die mehr als das Doppelte der gesetzlichen Frist (${meta.defaultDuration} ${meta.defaultUnit === 'YEARS' ? 'Jahre' : meta.defaultUnit === 'MONTHS' ? 'Monate' : 'Tage'} nach ${meta.statute}) betraegt. Ueberlange Speicherung widerspricht dem Grundsatz der Speicherbegrenzung (Art. 5 Abs. 1 lit. e DSGVO).`,
          'Pruefen Sie, ob die verlaengerte Aufbewahrungsdauer gerechtfertigt ist. Falls nicht, reduzieren Sie sie auf die gesetzliche Mindestfrist.'
        )
      }
    }
  }

  return null
}

/**
 * Check 11: MISSING_DATA_CATEGORIES (LOW)
 * Non-draft policy without any data categories assigned.
 */
function checkMissingDataCategories(policy: LoeschfristPolicy): ComplianceIssue | null {
  if (policy.status !== 'DRAFT' && policy.dataCategories.length === 0) {
    return createIssue(
      policy,
      'MISSING_DATA_CATEGORIES',
      'LOW',
      'Keine Datenkategorien zugeordnet',
      `Die Policy "${policy.dataObjectName}" (Status: ${policy.status}) hat keine Datenkategorien zugeordnet. Ohne Datenkategorien ist unklar, welche personenbezogenen Daten von dieser Loeschregel betroffen sind.`,
      'Ordnen Sie mindestens eine Datenkategorie zu (z.B. Stammdaten, Kontaktdaten, Finanzdaten, Gesundheitsdaten).'
    )
  }

  return null
}

/**
 * Helper: convert retention duration to days for comparison.
 */
function toDays(duration: number, unit: string): number {
  switch (unit) {
    case 'DAYS': return duration
    case 'MONTHS': return duration * 30
    case 'YEARS': return duration * 365
    default: return duration
  }
}

// =============================================================================
// MAIN COMPLIANCE CHECK
// =============================================================================

/**
 * Fuehrt einen vollstaendigen Compliance-Check ueber alle Policies durch.
 *
 * @param policies - Alle Loeschfrist-Policies
 * @param vvtDataCategories - Optionale Datenkategorien aus dem VVT (localStorage)
 * @returns ComplianceCheckResult mit Issues, Score und Statistiken
 */
export function runComplianceCheck(
  policies: LoeschfristPolicy[],
  vvtDataCategories?: string[]
): ComplianceCheckResult {
  // Reset counter for deterministic IDs within a single check run
  issueCounter = 0

  const issues: ComplianceIssue[] = []

  // Run checks 1-6 for each policy
  for (const policy of policies) {
    const checks = [
      checkMissingTrigger(policy),
      checkMissingLegalBasis(policy),
      checkOverdueReview(policy),
      checkNoResponsible(policy),
      checkLegalHoldConflict(policy),
      checkStaleDraft(policy),
      checkMissingDeletionMethod(policy),
      checkMissingStorageLocations(policy),
      checkExcessiveRetention(policy),
      checkMissingDataCategories(policy),
    ]

    for (const issue of checks) {
      if (issue !== null) {
        issues.push(issue)
      }
    }
  }

  // Check 7: UNCOVERED_VVT_CATEGORY (MEDIUM)
  if (vvtDataCategories && vvtDataCategories.length > 0) {
    const coveredCategories = new Set<string>()
    for (const policy of policies) {
      for (const category of policy.dataCategories) {
        coveredCategories.add(category.toLowerCase().trim())
      }
    }

    for (const vvtCategory of vvtDataCategories) {
      const normalized = vvtCategory.toLowerCase().trim()
      if (!coveredCategories.has(normalized)) {
        issues.push({
          id: createIssueId(),
          policyId: '-',
          policyName: '-',
          type: 'UNCOVERED_VVT_CATEGORY',
          severity: 'MEDIUM',
          title: `Datenkategorie ohne Loeschfrist: "${vvtCategory}"`,
          description: `Die Datenkategorie "${vvtCategory}" ist im Verzeichnis der Verarbeitungstaetigkeiten (VVT) erfasst, hat aber keine zugehoerige Loeschfrist-Policy. Gemaess DSGVO Art. 5 Abs. 1 lit. e muss fuer jede Datenkategorie eine Speicherbegrenzung definiert sein.`,
          recommendation: `Erstellen Sie eine neue Loeschfrist-Policy fuer die Datenkategorie "${vvtCategory}" oder ordnen Sie sie einer bestehenden Policy zu.`,
        })
      }
    }
  }

  // Calculate score
  const bySeverity: Record<ComplianceIssueSeverity, number> = {
    LOW: 0,
    MEDIUM: 0,
    HIGH: 0,
    CRITICAL: 0,
  }

  for (const issue of issues) {
    bySeverity[issue.severity]++
  }

  const rawScore =
    100 -
    (bySeverity.CRITICAL * 15 +
      bySeverity.HIGH * 10 +
      bySeverity.MEDIUM * 5 +
      bySeverity.LOW * 2)

  const score = Math.max(0, rawScore)

  // Calculate pass/fail per policy
  const failedPolicyIds = new Set(
    issues.filter((i) => i.policyId !== '-').map((i) => i.policyId)
  )
  const totalPolicies = policies.length
  const failedCount = failedPolicyIds.size
  const passedCount = totalPolicies - failedCount

  return {
    issues,
    score,
    stats: {
      total: totalPolicies,
      passed: passedCount,
      failed: failedCount,
      bySeverity,
    },
  }
}
