/**
 * Document-scope calculation, risk flags, gap analysis, next actions,
 * and reasoning (audit trail) helpers for the ComplianceScopeEngine.
 */
import type {
  ComplianceDepthLevel,
  ComplianceScores,
  ScopeProfilingAnswer,
  TriggeredHardTrigger,
  RequiredDocument,
  RiskFlag,
  ScopeGap,
  NextAction,
  ScopeReasoning,
  ScopeDocumentType,
  HardTriggerRule,
} from './compliance-scope-types'
import {
  getDepthLevelNumeric,
  DOCUMENT_SCOPE_MATRIX_CORE,
  DOCUMENT_TYPE_LABELS,
} from './compliance-scope-types'
import { HARD_TRIGGER_RULES } from './compliance-scope-triggers'

import { DOCUMENT_SDK_STEP_MAP } from "./compliance-scope-sdk-step-map"

// ---------------------------------------------------------------------------
// Shared helpers
// ---------------------------------------------------------------------------

/** Parse employee-count bucket string to a representative number. */
export function parseEmployeeCount(value: string): number {
  if (value === '1-9') return 9
  if (value === '10-49') return 49
  if (value === '50-249') return 249
  if (value === '250-999') return 999
  if (value === '1000+') return 1000
  return 0
}

/** Derive level purely from composite score. */
export function getLevelFromScore(composite: number): ComplianceDepthLevel {
  if (composite <= 25) return 'L1'
  if (composite <= 50) return 'L2'
  if (composite <= 75) return 'L3'
  return 'L4'
}

/** Highest level among the given triggers. */
export function getMaxTriggerLevel(triggers: TriggeredHardTrigger[]): ComplianceDepthLevel {
  if (triggers.length === 0) return 'L1'
  let max: ComplianceDepthLevel = 'L1'
  for (const t of triggers) {
    if (getDepthLevelNumeric(t.minimumLevel) > getDepthLevelNumeric(max)) {
      max = t.minimumLevel
    }
  }
  return max
}

// ---------------------------------------------------------------------------
// normalizeDocType
// ---------------------------------------------------------------------------

/**
 * Maps UPPERCASE document-type identifiers from the hard-trigger rules
 * to the lowercase ScopeDocumentType keys.
 */
export function normalizeDocType(raw: string): ScopeDocumentType | null {
  const mapping: Record<string, ScopeDocumentType> = {
    VVT: 'vvt',
    TOM: 'tom',
    DSFA: 'dsfa',
    DSE: 'dsi',
    AGB: 'vertragsmanagement',
    AVV: 'av_vertrag',
    COOKIE_BANNER: 'einwilligung',
    EINWILLIGUNGEN: 'einwilligung',
    TRANSFER_DOKU: 'daten_transfer',
    AUDIT_CHECKLIST: 'audit_log',
    VENDOR_MANAGEMENT: 'vertragsmanagement',
    LOESCHKONZEPT: 'lf',
    DSR_PROZESS: 'betroffenenrechte',
    NOTFALLPLAN: 'notfallplan',
    AI_ACT_DOKU: 'ai_act_doku',
    WIDERRUFSBELEHRUNG: 'widerrufsbelehrung',
    PREISANGABEN: 'preisangaben',
    FERNABSATZ_INFO: 'fernabsatz_info',
    STREITBEILEGUNG: 'streitbeilegung',
    PRODUKTSICHERHEIT: 'produktsicherheit',
  }
  if (raw in DOCUMENT_SCOPE_MATRIX_CORE) return raw as ScopeDocumentType
  return mapping[raw] ?? null
}

// ---------------------------------------------------------------------------
// Document scope
// ---------------------------------------------------------------------------

function getDocumentPriority(
  docType: ScopeDocumentType,
  isMandatoryFromTrigger: boolean,
): 'high' | 'medium' | 'low' {
  if (isMandatoryFromTrigger) return 'high'
  if (['VVT', 'TOM', 'DSE'].includes(docType)) return 'high'
  if (['DSFA', 'AVV', 'EINWILLIGUNGEN'].includes(docType)) return 'high'
  return 'medium'
}

function estimateEffort(docType: ScopeDocumentType): number {
  const effortMap: Partial<Record<ScopeDocumentType, number>> = {
    vvt: 8,
    tom: 12,
    dsfa: 16,
    av_vertrag: 4,
    dsi: 6,
    einwilligung: 6,
    lf: 10,
    daten_transfer: 8,
    betroffenenrechte: 8,
    notfallplan: 12,
    vertragsmanagement: 10,
    audit_log: 8,
    risikoanalyse: 6,
    schulung: 4,
    datenpannen: 6,
    zertifizierung: 8,
    datenschutzmanagement: 12,
    iace_ce_assessment: 8,
    widerrufsbelehrung: 3,
    preisangaben: 2,
    fernabsatz_info: 4,
    streitbeilegung: 1,
    produktsicherheit: 8,
    ai_act_doku: 12,
  }
  return effortMap[docType] ?? 6
}

/**
 * Build the full document-scope list based on compliance level and triggers.
 */
export function buildDocumentScope(
  level: ComplianceDepthLevel,
  triggers: TriggeredHardTrigger[],
  _answers: ScopeProfilingAnswer[],
): RequiredDocument[] {
  const requiredDocs: RequiredDocument[] = []
  const mandatoryFromTriggers = new Set<ScopeDocumentType>()
  const triggerDocOrigins = new Map<ScopeDocumentType, string[]>()

  for (const trigger of triggers) {
    for (const doc of trigger.mandatoryDocuments) {
      const normalized = normalizeDocType(doc)
      if (normalized) {
        mandatoryFromTriggers.add(normalized)
        if (!triggerDocOrigins.has(normalized)) triggerDocOrigins.set(normalized, [])
        triggerDocOrigins.get(normalized)!.push(doc)
      }
    }
  }

  for (const docType of Object.keys(DOCUMENT_SCOPE_MATRIX_CORE) as ScopeDocumentType[]) {
    const depthReq = DOCUMENT_SCOPE_MATRIX_CORE[docType]?.[level]
    const isMandatoryFromTrigger = mandatoryFromTriggers.has(docType)

    if ((depthReq?.required) || isMandatoryFromTrigger) {
      const originDocs = triggerDocOrigins.get(docType) ?? []
      requiredDocs.push({
        documentType: docType,
        label: DOCUMENT_TYPE_LABELS[docType],
        requirement: 'mandatory',
        priority: getDocumentPriority(docType, isMandatoryFromTrigger),
        estimatedEffort: estimateEffort(docType),
        sdkStepUrl: DOCUMENT_SDK_STEP_MAP[docType],
        triggeredBy: isMandatoryFromTrigger
          ? triggers
              .filter((t) => t.mandatoryDocuments.some((d) => originDocs.includes(d)))
              .map((t) => t.ruleId)
          : [],
      })
    } else if (depthReq && !depthReq.required) {
      requiredDocs.push({
        documentType: docType,
        label: DOCUMENT_TYPE_LABELS[docType],
        requirement: 'recommended',
        priority: 'medium',
        estimatedEffort: estimateEffort(docType),
        sdkStepUrl: DOCUMENT_SDK_STEP_MAP[docType],
        triggeredBy: [],
      })
    }
  }

  requiredDocs.sort((a, b) => {
    if (a.requirement === 'mandatory' && b.requirement !== 'mandatory') return -1
    if (a.requirement !== 'mandatory' && b.requirement === 'mandatory') return 1
    const priorityOrder: Record<string, number> = { high: 3, medium: 2, low: 1 }
    return priorityOrder[b.priority] - priorityOrder[a.priority]
  })

  return requiredDocs
}

// ---------------------------------------------------------------------------
// Risk flags
// ---------------------------------------------------------------------------

function getMaturityRecommendation(ruleId: string): string {
  const recommendations: Record<string, string> = {
    'HT-I01': 'Prozess für Betroffenenrechte (DSAR) etablieren und dokumentieren',
    'HT-I02': 'Löschkonzept gemäß Art. 17 DSGVO entwickeln und implementieren',
    'HT-I03':
      'Incident-Response-Plan für Datenschutzverletzungen (Art. 33 DSGVO) erstellen',
    'HT-I04': 'Regelmäßige interne Audits und Reviews einführen',
    'HT-I05': 'Schulungsprogramm für Mitarbeiter zum Datenschutz etablieren',
  }
  return recommendations[ruleId] || 'Prozess etablieren und dokumentieren'
}

/**
 * Evaluate risk flags based on process-maturity gaps and other risks.
 *
 * `checkTriggerFn` is injected to avoid a circular dependency on the engine.
 */
export function evaluateRiskFlags(
  answers: ScopeProfilingAnswer[],
  level: ComplianceDepthLevel,
  checkTriggerFn: (
    rule: HardTriggerRule,
    answerMap: Map<string, any>,
    answers: ScopeProfilingAnswer[],
  ) => boolean,
): RiskFlag[] {
  const flags: RiskFlag[] = []
  const answerMap = new Map(answers.map((a) => [a.questionId, a.value]))

  const maturityRules = HARD_TRIGGER_RULES.filter((r) => r.category === 'process_maturity')
  for (const rule of maturityRules) {
    if (checkTriggerFn(rule, answerMap, answers)) {
      flags.push({
        severity: 'medium',
        category: 'process',
        message: rule.description,
        legalReference: rule.legalReference,
        recommendation: getMaturityRecommendation(rule.id),
      })
    }
  }

  if (getDepthLevelNumeric(level) >= 2) {
    const encTransit = answerMap.get('tech_encryption_transit')
    const encRest = answerMap.get('tech_encryption_rest')

    if (encTransit === false) {
      flags.push({
        severity: 'high',
        category: 'technical',
        message: 'Fehlende Verschlüsselung bei Datenübertragung',
        legalReference: 'Art. 32 DSGVO',
        recommendation: 'TLS 1.2+ für alle Datenübertragungen implementieren',
      })
    }

    if (encRest === false) {
      flags.push({
        severity: 'high',
        category: 'technical',
        message: 'Fehlende Verschlüsselung gespeicherter Daten',
        legalReference: 'Art. 32 DSGVO',
        recommendation: 'Verschlüsselung at-rest für sensitive Daten implementieren',
      })
    }
  }

  const thirdCountry = answerMap.get('tech_third_country')
  const hostingLocation = answerMap.get('tech_hosting_location')
  if (
    thirdCountry === true &&
    hostingLocation !== 'eu' &&
    hostingLocation !== 'eu_us_adequacy'
  ) {
    flags.push({
      severity: 'high',
      category: 'legal',
      message: 'Drittlandtransfer ohne angemessene Garantien',
      legalReference: 'Art. 44 ff. DSGVO',
      recommendation:
        'Standardvertragsklauseln (SCCs) oder Binding Corporate Rules (BCRs) implementieren',
    })
  }

  const hasDPO = answerMap.get('org_has_dpo')
  const employeeCount = answerMap.get('org_employee_count')
  if (hasDPO === false && parseEmployeeCount(employeeCount as string) >= 250) {
    flags.push({
      severity: 'medium',
      category: 'organizational',
      message: 'Kein Datenschutzbeauftragter bei großer Organisation',
      legalReference: 'Art. 37 DSGVO',
      recommendation: 'Bestellung eines Datenschutzbeauftragten prüfen',
    })
  }

  return flags
}

// ---------------------------------------------------------------------------
// Gap analysis
// ---------------------------------------------------------------------------

export function calculateGaps(
  answers: ScopeProfilingAnswer[],
  level: ComplianceDepthLevel,
): ScopeGap[] {
  const gaps: ScopeGap[] = []
  const answerMap = new Map(answers.map((a) => [a.questionId, a.value]))

  if (getDepthLevelNumeric(level) >= 3) {
    const hasDSFA = answerMap.get('proc_regular_audits')
    if (hasDSFA === false) {
      gaps.push({
        gapType: 'documentation',
        severity: 'high',
        description: 'Datenschutz-Folgenabschätzung (DSFA) fehlt',
        requiredFor: level,
        currentState: 'Keine DSFA durchgeführt',
        targetState: 'DSFA für Hochrisiko-Verarbeitungen durchgeführt und dokumentiert',
        effort: 16,
        priority: 'high',
      })
    }
  }

  const hasDeletion = answerMap.get('proc_deletion_concept')
  if (hasDeletion === false && getDepthLevelNumeric(level) >= 2) {
    gaps.push({
      gapType: 'process',
      severity: 'medium',
      description: 'Löschkonzept fehlt',
      requiredFor: level,
      currentState: 'Kein systematisches Löschkonzept',
      targetState: 'Dokumentiertes Löschkonzept mit definierten Fristen',
      effort: 10,
      priority: 'high',
    })
  }

  const hasDSAR = answerMap.get('proc_dsar_process')
  if (hasDSAR === false) {
    gaps.push({
      gapType: 'process',
      severity: 'high',
      description: 'Prozess für Betroffenenrechte fehlt',
      requiredFor: level,
      currentState: 'Kein etablierter DSAR-Prozess',
      targetState: 'Dokumentierter Prozess zur Bearbeitung von Betroffenenrechten',
      effort: 8,
      priority: 'high',
    })
  }

  const hasIncident = answerMap.get('proc_incident_response')
  if (hasIncident === false) {
    gaps.push({
      gapType: 'process',
      severity: 'high',
      description: 'Incident-Response-Plan fehlt',
      requiredFor: level,
      currentState: 'Kein Prozess für Datenschutzverletzungen',
      targetState: 'Dokumentierter Incident-Response-Plan gemäß Art. 33 DSGVO',
      effort: 12,
      priority: 'high',
    })
  }

  const hasTraining = answerMap.get('comp_training')
  if (hasTraining === false && getDepthLevelNumeric(level) >= 2) {
    gaps.push({
      gapType: 'organizational',
      severity: 'medium',
      description: 'Datenschutzschulungen fehlen',
      requiredFor: level,
      currentState: 'Keine regelmäßigen Schulungen',
      targetState: 'Etabliertes Schulungsprogramm für alle Mitarbeiter',
      effort: 6,
      priority: 'medium',
    })
  }

  return gaps
}

// ---------------------------------------------------------------------------
// Next actions
// ---------------------------------------------------------------------------

export function buildNextActions(
  requiredDocuments: RequiredDocument[],
  gaps: ScopeGap[],
): NextAction[] {
  const actions: NextAction[] = []

  for (const doc of requiredDocuments) {
    if (doc.requirement === 'mandatory') {
      actions.push({
        actionType: 'create_document',
        title: `${doc.label} erstellen`,
        description: `Pflichtdokument für Compliance-Level erstellen`,
        priority: doc.priority,
        estimatedEffort: doc.estimatedEffort,
        documentType: doc.documentType,
        sdkStepUrl: doc.sdkStepUrl,
        blockers: [],
      })
    }
  }

  for (const gap of gaps) {
    let actionType: NextAction['actionType'] = 'establish_process'
    if (gap.gapType === 'documentation') actionType = 'create_document'
    else if (gap.gapType === 'technical') actionType = 'implement_technical'
    else if (gap.gapType === 'organizational') actionType = 'organizational_change'

    actions.push({
      actionType,
      title: `Gap schließen: ${gap.description}`,
      description: `Von "${gap.currentState}" zu "${gap.targetState}"`,
      priority: gap.priority,
      estimatedEffort: gap.effort,
      blockers: [],
    })
  }

  const priorityOrder: Record<string, number> = { high: 3, medium: 2, low: 1 }
  actions.sort((a, b) => priorityOrder[b.priority] - priorityOrder[a.priority])

  return actions
}

// ---------------------------------------------------------------------------
// Reasoning (audit trail)
// ---------------------------------------------------------------------------

export function buildReasoning(
  scores: ComplianceScores,
  triggers: TriggeredHardTrigger[],
  level: ComplianceDepthLevel,
  docs: RequiredDocument[],
): ScopeReasoning[] {
  const reasoning: ScopeReasoning[] = []

  reasoning.push({
    step: 'score_calculation',
    description: 'Risikobasierte Score-Berechnung aus Profiling-Antworten',
    factors: [
      `Risiko-Score: ${scores.risk_score}/10`,
      `Komplexitäts-Score: ${scores.complexity_score}/10`,
      `Assurance-Score: ${scores.assurance_need}/10`,
      `Composite Score: ${scores.composite_score}/10`,
    ],
    impact: `Score-basiertes Level: ${getLevelFromScore(scores.composite_score)}`,
  })

  if (triggers.length > 0) {
    reasoning.push({
      step: 'hard_trigger_evaluation',
      description: `${triggers.length} Hard Trigger Rule(s) aktiviert`,
      factors: triggers.map(
        (t) => `${t.ruleId}: ${t.description}${t.legalReference ? ` (${t.legalReference})` : ''}`,
      ),
      impact: `Höchstes Trigger-Level: ${getMaxTriggerLevel(triggers)}`,
    })
  }

  reasoning.push({
    step: 'level_determination',
    description: 'Finales Compliance-Level durch Maximum aus Score und Triggers',
    factors: [
      `Score-Level: ${getLevelFromScore(scores.composite_score)}`,
      `Trigger-Level: ${getMaxTriggerLevel(triggers)}`,
    ],
    impact: `Finales Level: ${level}`,
  })

  const mandatoryDocs = docs.filter((d) => d.requirement === 'mandatory')
  reasoning.push({
    step: 'document_scope',
    description: `Dokumenten-Scope für ${level} bestimmt`,
    factors: [
      `${mandatoryDocs.length} Pflichtdokumente`,
      `${docs.length - mandatoryDocs.length} empfohlene Dokumente`,
    ],
    impact: `Gesamtaufwand: ~${docs.reduce((sum, d) => sum + d.estimatedEffort, 0)} Stunden`,
  })

  return reasoning
}
