import type {
  ComplianceDepthLevel,
  ComplianceScores,
  ScopeProfilingAnswer,
  ScopeDecision,
  HardTriggerRule,
  TriggeredHardTrigger,
  RequiredDocument,
  RiskFlag,
  ScopeGap,
  NextAction,
  ScopeReasoning,
} from './compliance-scope-types'
import type { CompanyProfile, MachineBuilderProfile } from './types'
import {
  getDepthLevelNumeric,
  maxDepthLevel,
  createEmptyScopeDecision,
} from './compliance-scope-types'

// Re-export data constants so existing barrel imports keep working
export { QUESTION_SCORE_WEIGHTS, ANSWER_MULTIPLIERS } from './compliance-scope-data'
export { HARD_TRIGGER_RULES } from './compliance-scope-triggers'

import { QUESTION_SCORE_WEIGHTS, ANSWER_MULTIPLIERS } from './compliance-scope-data'
import { HARD_TRIGGER_RULES } from './compliance-scope-triggers'
import {
  parseEmployeeCount,
  getLevelFromScore,
  getMaxTriggerLevel,
  buildDocumentScope,
  evaluateRiskFlags,
  calculateGaps,
  buildNextActions,
  buildReasoning,
} from './compliance-scope-documents'

// ============================================================================
// COMPLIANCE SCOPE ENGINE
// ============================================================================

export class ComplianceScopeEngine {
  /**
   * Haupteinstiegspunkt: Evaluiert alle Profiling-Antworten und produziert eine ScopeDecision
   * Optional: companyProfile fuer machineBuilder-basierte IACE Triggers
   */
  evaluate(answers: ScopeProfilingAnswer[], companyProfile?: CompanyProfile | null): ScopeDecision {
    const decision = createEmptyScopeDecision()

    // 1. Scores berechnen
    decision.scores = this.calculateScores(answers)

    // 2. Hard Triggers prüfen (inkl. IACE machineBuilder Triggers)
    decision.triggeredHardTriggers = this.evaluateHardTriggers(answers, companyProfile)

    // 3. Finales Level bestimmen
    decision.determinedLevel = this.determineLevel(
      decision.scores,
      decision.triggeredHardTriggers
    )

    // 4. Dokumenten-Scope aufbauen
    decision.requiredDocuments = this.buildDocumentScope(
      decision.determinedLevel,
      decision.triggeredHardTriggers,
      answers
    )

    // 5. Risk Flags ermitteln
    decision.riskFlags = this.evaluateRiskFlags(answers, decision.determinedLevel)

    // 6. Gaps berechnen
    decision.gaps = this.calculateGaps(answers, decision.determinedLevel)

    // 7. Next Actions ableiten
    decision.nextActions = this.buildNextActions(
      decision.requiredDocuments,
      decision.gaps
    )

    // 8. Reasoning (Audit Trail) aufbauen
    decision.reasoning = this.buildReasoning(
      decision.scores,
      decision.triggeredHardTriggers,
      decision.determinedLevel,
      decision.requiredDocuments
    )

    decision.evaluatedAt = new Date().toISOString()

    return decision
  }

  /**
   * Berechnet Risk-, Complexity- und Assurance-Scores aus den Profiling-Antworten
   */
  calculateScores(answers: ScopeProfilingAnswer[]): ComplianceScores {
    let riskSum = 0
    let complexitySum = 0
    let assuranceSum = 0
    let riskWeightSum = 0
    let complexityWeightSum = 0
    let assuranceWeightSum = 0

    for (const answer of answers) {
      const weights = QUESTION_SCORE_WEIGHTS[answer.questionId]
      if (!weights) continue

      const multiplier = this.getAnswerMultiplier(answer)

      riskSum += weights.risk * multiplier
      complexitySum += weights.complexity * multiplier
      assuranceSum += weights.assurance * multiplier

      riskWeightSum += weights.risk
      complexityWeightSum += weights.complexity
      assuranceWeightSum += weights.assurance
    }

    const riskScore =
      riskWeightSum > 0 ? (riskSum / riskWeightSum) * 10 : 0
    const complexityScore =
      complexityWeightSum > 0 ? (complexitySum / complexityWeightSum) * 10 : 0
    const assuranceScore =
      assuranceWeightSum > 0 ? (assuranceSum / assuranceWeightSum) * 10 : 0

    const composite = riskScore * 0.4 + complexityScore * 0.3 + assuranceScore * 0.3

    return {
      risk_score: Math.round(riskScore * 10) / 10,
      complexity_score: Math.round(complexityScore * 10) / 10,
      assurance_need: Math.round(assuranceScore * 10) / 10,
      composite_score: Math.round(composite * 10) / 10,
    }
  }

  /**
   * Bestimmt den Multiplikator für eine Antwort (0.0 - 1.0)
   */
  private getAnswerMultiplier(answer: ScopeProfilingAnswer): number {
    const { questionId, value } = answer

    // Boolean
    if (typeof value === 'boolean') {
      return value ? 1.0 : 0.0
    }

    // Number
    if (typeof value === 'number') {
      return this.normalizeNumericAnswer(questionId, value)
    }

    // Single choice
    if (typeof value === 'string') {
      const multipliers = ANSWER_MULTIPLIERS[questionId]
      if (multipliers && multipliers[value] !== undefined) {
        return multipliers[value]
      }
      return 0.5 // Fallback
    }

    // Multi choice
    if (Array.isArray(value)) {
      if (value.length === 0) return 0.0
      return Math.min(value.length / 5, 1.0)
    }

    return 0.0
  }

  /**
   * Normalisiert numerische Antworten
   */
  private normalizeNumericAnswer(_questionId: string, value: number): number {
    if (value <= 0) return 0.0
    if (value >= 1000) return 1.0
    return Math.log10(value + 1) / 3
  }

  /**
   * Evaluiert Hard Trigger Rules
   * Optional: companyProfile fuer machineBuilder-basierte IACE Triggers
   */
  evaluateHardTriggers(answers: ScopeProfilingAnswer[], companyProfile?: CompanyProfile | null): TriggeredHardTrigger[] {
    const triggered: TriggeredHardTrigger[] = []
    const answerMap = new Map(answers.map((a) => [a.questionId, a.value]))

    for (const rule of HARD_TRIGGER_RULES) {
      const isTriggered = this.checkTriggerCondition(rule, answerMap, answers, companyProfile)

      if (isTriggered) {
        triggered.push({
          ruleId: rule.id,
          category: rule.category,
          description: rule.description,
          legalReference: rule.legalReference,
          minimumLevel: rule.minimumLevel,
          requiresDSFA: rule.requiresDSFA,
          mandatoryDocuments: rule.mandatoryDocuments,
        })
      }
    }

    return triggered
  }

  /**
   * Liest einen Wert aus dem MachineBuilderProfile anhand eines Feldnamens
   */
  private getMachineBuilderValue(mb: MachineBuilderProfile, field: string): unknown {
    return (mb as unknown as Record<string, unknown>)[field]
  }

  /**
   * Prüft, ob eine Trigger-Regel erfüllt ist
   */
  checkTriggerCondition(
    rule: HardTriggerRule,
    answerMap: Map<string, any>,
    answers: ScopeProfilingAnswer[],
    companyProfile?: CompanyProfile | null,
  ): boolean {
    // IACE machineBuilder-basierte Triggers
    if (rule.questionId.startsWith('machineBuilder.')) {
      const mb = companyProfile?.machineBuilder
      if (!mb) return false

      const fieldName = rule.questionId.replace('machineBuilder.', '')
      const fieldValue = this.getMachineBuilderValue(mb, fieldName)
      if (fieldValue === undefined) return false

      let baseCondition = false
      switch (rule.condition) {
        case 'EQUALS':
          baseCondition = fieldValue === rule.conditionValue
          break
        case 'CONTAINS':
          if (Array.isArray(fieldValue)) {
            baseCondition = fieldValue.includes(rule.conditionValue)
          }
          break
        default:
          baseCondition = fieldValue === rule.conditionValue
      }

      if (!baseCondition) return false

      const combine = (rule as any).combineWithMachineBuilder
      if (combine) {
        const combineVal = this.getMachineBuilderValue(mb, combine.field)
        if (combine.value !== undefined && combineVal !== combine.value) return false
        if (combine.includes !== undefined) {
          if (!Array.isArray(combineVal) || !combineVal.includes(combine.includes)) return false
        }
      }

      return true
    }

    // Standard answer-based triggers
    const value = answerMap.get(rule.questionId)
    if (value === undefined) return false

    let baseCondition = false

    switch (rule.condition) {
      case 'EQUALS':
        baseCondition = value === rule.conditionValue
        break
      case 'CONTAINS':
        if (Array.isArray(value)) {
          baseCondition = value.includes(rule.conditionValue as string)
        } else if (typeof value === 'string') {
          baseCondition = value.includes(rule.conditionValue as string)
        }
        break
      case 'IN':
        if (Array.isArray(rule.conditionValue)) {
          baseCondition = rule.conditionValue.includes(value)
        }
        break
      case 'GREATER_THAN':
        if (typeof value === 'number' && typeof rule.conditionValue === 'number') {
          baseCondition = value > rule.conditionValue
        } else if (typeof value === 'string') {
          const parsed = parseEmployeeCount(value)
          baseCondition = parsed > (rule.conditionValue as number)
        }
        break
      case 'NOT_EQUALS':
        baseCondition = value !== rule.conditionValue
        break
    }

    if (!baseCondition) return false

    // Exclude-Bedingung
    if (rule.excludeWhen) {
      const exVal = answerMap.get(rule.excludeWhen.questionId)
      if (Array.isArray(rule.excludeWhen.value)
        ? rule.excludeWhen.value.includes(exVal)
        : exVal === rule.excludeWhen.value) {
        return false
      }
    }

    // Require-Bedingung
    if (rule.requireWhen) {
      const reqVal = answerMap.get(rule.requireWhen.questionId)
      if (Array.isArray(rule.requireWhen.value)
        ? !rule.requireWhen.value.includes(reqVal)
        : reqVal !== rule.requireWhen.value) {
        return false
      }
    }

    // Combined checks
    if (rule.combineWithArt9) {
      const art9 = answerMap.get('data_art9')
      if (!art9 || (Array.isArray(art9) && art9.length === 0)) return false
    }

    if (rule.combineWithMinors) {
      const minors = answerMap.get('data_minors')
      if (minors !== true) return false
    }

    if (rule.combineWithAI) {
      const ai = answerMap.get('proc_ai_usage')
      if (!ai || (Array.isArray(ai) && (ai.length === 0 || ai.includes('keine')))) {
        return false
      }
    }

    if (rule.combineWithEmployeeMonitoring) {
      const empMon = answerMap.get('proc_employee_monitoring')
      if (empMon !== true) return false
    }

    if (rule.combineWithADM) {
      const adm = answerMap.get('proc_adm_scoring')
      if (adm !== true) return false
    }

    return true
  }

  /**
   * Bestimmt das finale Compliance-Level basierend auf Scores und Triggers
   */
  determineLevel(
    scores: ComplianceScores,
    triggers: TriggeredHardTrigger[]
  ): ComplianceDepthLevel {
    const levelFromScore = getLevelFromScore(scores.composite_score)
    const maxTriggerLevel = getMaxTriggerLevel(triggers)
    return maxDepthLevel(levelFromScore, maxTriggerLevel)
  }

  // Delegate to extracted helpers (keep public API surface identical)

  buildDocumentScope(
    level: ComplianceDepthLevel,
    triggers: TriggeredHardTrigger[],
    answers: ScopeProfilingAnswer[]
  ): RequiredDocument[] {
    return buildDocumentScope(level, triggers, answers)
  }

  evaluateRiskFlags(
    answers: ScopeProfilingAnswer[],
    level: ComplianceDepthLevel
  ): RiskFlag[] {
    return evaluateRiskFlags(answers, level, (rule, answerMap, ans) =>
      this.checkTriggerCondition(rule, answerMap, ans))
  }

  calculateGaps(
    answers: ScopeProfilingAnswer[],
    level: ComplianceDepthLevel
  ): ScopeGap[] {
    return calculateGaps(answers, level)
  }

  buildNextActions(
    requiredDocuments: RequiredDocument[],
    gaps: ScopeGap[]
  ): NextAction[] {
    return buildNextActions(requiredDocuments, gaps)
  }

  buildReasoning(
    scores: ComplianceScores,
    triggers: TriggeredHardTrigger[],
    level: ComplianceDepthLevel,
    docs: RequiredDocument[]
  ): ScopeReasoning[] {
    return buildReasoning(scores, triggers, level, docs)
  }
}

// ============================================================================
// SINGLETON EXPORT
// ============================================================================

export const complianceScopeEngine = new ComplianceScopeEngine()
