/**
 * Risk Score Calculator
 *
 * Calculate inherent and residual risk scores for vendors and processing activities
 */

import {
  Vendor,
  ProcessingActivity,
  RiskScore,
  RiskLevel,
  RiskFactor,
  ControlInstance,
  DataAccessLevel,
  VendorRole,
  ServiceCategory,
  PersonalDataCategory,
  getRiskLevelFromScore,
  isSpecialCategory,
  hasAdequacyDecision,
  LocalizedText,
} from '../types'

// ==========================================
// RISK FACTOR DEFINITIONS
// ==========================================

export interface RiskFactorDefinition {
  id: string
  name: LocalizedText
  category: 'DATA' | 'ACCESS' | 'LOCATION' | 'VENDOR' | 'PROCESSING'
  description: LocalizedText
  weight: number // 0-1
  evaluator: (context: RiskContext) => number // Returns 1-5
}

export interface RiskContext {
  vendor?: Vendor
  processingActivity?: ProcessingActivity
  controlInstances?: ControlInstance[]
}

export const RISK_FACTOR_DEFINITIONS: RiskFactorDefinition[] = [
  // DATA FACTORS
  {
    id: 'data_volume',
    name: { de: 'Datenvolumen', en: 'Data Volume' },
    category: 'DATA',
    description: { de: 'Menge der verarbeiteten Daten', en: 'Volume of data processed' },
    weight: 0.15,
    evaluator: (ctx) => {
      // Based on vendor service category or processing activity scope
      if (ctx.vendor) {
        const highVolume: ServiceCategory[] = ['CLOUD_INFRASTRUCTURE', 'ERP', 'CRM', 'HR_SOFTWARE']
        if (highVolume.includes(ctx.vendor.serviceCategory)) return 5
        const medVolume: ServiceCategory[] = ['HOSTING', 'EMAIL', 'ANALYTICS', 'BACKUP']
        if (medVolume.includes(ctx.vendor.serviceCategory)) return 3
        return 2
      }
      return 3 // Default medium
    },
  },
  {
    id: 'data_sensitivity',
    name: { de: 'Datensensibilität', en: 'Data Sensitivity' },
    category: 'DATA',
    description: { de: 'Sensibilität der verarbeiteten Daten', en: 'Sensitivity of data processed' },
    weight: 0.2,
    evaluator: (ctx) => {
      if (ctx.processingActivity) {
        const hasSpecial = ctx.processingActivity.personalDataCategories.some(isSpecialCategory)
        if (hasSpecial) return 5
        const hasFinancial = ctx.processingActivity.personalDataCategories.some(
          (c) => ['BANK_ACCOUNT', 'PAYMENT_DATA', 'SALARY_DATA', 'TAX_ID'].includes(c)
        )
        if (hasFinancial) return 4
        const hasIdentifiers = ctx.processingActivity.personalDataCategories.some(
          (c) => ['ID_NUMBER', 'SOCIAL_SECURITY'].includes(c)
        )
        if (hasIdentifiers) return 4
        return 2
      }
      return 3
    },
  },
  {
    id: 'special_categories',
    name: { de: 'Besondere Kategorien', en: 'Special Categories' },
    category: 'DATA',
    description: { de: 'Verarbeitung besonderer Datenkategorien (Art. 9)', en: 'Processing of special data categories (Art. 9)' },
    weight: 0.15,
    evaluator: (ctx) => {
      if (ctx.processingActivity) {
        const specialCount = ctx.processingActivity.personalDataCategories.filter(isSpecialCategory).length
        if (specialCount >= 3) return 5
        if (specialCount >= 1) return 4
        return 1
      }
      return 1
    },
  },

  // ACCESS FACTORS
  {
    id: 'data_access_level',
    name: { de: 'Datenzugriffsebene', en: 'Data Access Level' },
    category: 'ACCESS',
    description: { de: 'Art des Zugriffs auf personenbezogene Daten', en: 'Type of access to personal data' },
    weight: 0.15,
    evaluator: (ctx) => {
      if (ctx.vendor) {
        const accessLevelScores: Record<DataAccessLevel, number> = {
          NONE: 1,
          POTENTIAL: 2,
          ADMINISTRATIVE: 4,
          CONTENT: 5,
        }
        return accessLevelScores[ctx.vendor.dataAccessLevel]
      }
      return 3
    },
  },
  {
    id: 'vendor_role',
    name: { de: 'Vendor-Rolle', en: 'Vendor Role' },
    category: 'ACCESS',
    description: { de: 'Rolle des Vendors im Datenschutzkontext', en: 'Role of vendor in data protection context' },
    weight: 0.1,
    evaluator: (ctx) => {
      if (ctx.vendor) {
        const roleScores: Record<VendorRole, number> = {
          THIRD_PARTY: 1,
          CONTROLLER: 3,
          JOINT_CONTROLLER: 4,
          PROCESSOR: 4,
          SUB_PROCESSOR: 5,
        }
        return roleScores[ctx.vendor.role]
      }
      return 3
    },
  },

  // LOCATION FACTORS
  {
    id: 'third_country_transfer',
    name: { de: 'Drittlandtransfer', en: 'Third Country Transfer' },
    category: 'LOCATION',
    description: { de: 'Datenübermittlung in Drittländer', en: 'Data transfer to third countries' },
    weight: 0.15,
    evaluator: (ctx) => {
      if (ctx.vendor) {
        const locations = ctx.vendor.processingLocations
        const hasNonAdequate = locations.some((l) => !l.isEU && !l.isAdequate)
        if (hasNonAdequate) return 5
        const hasAdequate = locations.some((l) => !l.isEU && l.isAdequate)
        if (hasAdequate) return 3
        return 1 // EU only
      }
      if (ctx.processingActivity && ctx.processingActivity.thirdCountryTransfers.length > 0) {
        const hasNonAdequate = ctx.processingActivity.thirdCountryTransfers.some(
          (t) => !hasAdequacyDecision(t.country)
        )
        if (hasNonAdequate) return 5
        return 3
      }
      return 1
    },
  },

  // VENDOR FACTORS
  {
    id: 'certification_status',
    name: { de: 'Zertifizierungsstatus', en: 'Certification Status' },
    category: 'VENDOR',
    description: { de: 'Vorhandensein relevanter Zertifizierungen', en: 'Presence of relevant certifications' },
    weight: 0.1,
    evaluator: (ctx) => {
      if (ctx.vendor) {
        const certs = ctx.vendor.certifications
        const relevantCerts = ['ISO 27001', 'SOC 2', 'SOC2', 'TISAX', 'C5', 'PCI DSS']
        const hasCert = certs.some((c) => relevantCerts.some((rc) => c.type.includes(rc)))
        const hasExpired = certs.some((c) => c.expirationDate && new Date(c.expirationDate) < new Date())
        if (hasCert && !hasExpired) return 1
        if (hasCert && hasExpired) return 3
        return 4
      }
      return 3
    },
  },

  // PROCESSING FACTORS
  {
    id: 'processing_scope',
    name: { de: 'Verarbeitungsumfang', en: 'Processing Scope' },
    category: 'PROCESSING',
    description: { de: 'Umfang und Art der Verarbeitung', en: 'Scope and nature of processing' },
    weight: 0.1,
    evaluator: (ctx) => {
      if (ctx.processingActivity) {
        const subjectCount = ctx.processingActivity.dataSubjectCategories.length
        const categoryCount = ctx.processingActivity.personalDataCategories.length
        const totalScope = subjectCount + categoryCount
        if (totalScope > 15) return 5
        if (totalScope > 10) return 4
        if (totalScope > 5) return 3
        return 2
      }
      return 3
    },
  },
]

// ==========================================
// RISK CALCULATION FUNCTIONS
// ==========================================

/**
 * Calculate inherent risk score for a vendor
 */
export function calculateVendorInherentRisk(vendor: Vendor): {
  score: RiskScore
  factors: RiskFactor[]
} {
  const context: RiskContext = { vendor }
  return calculateInherentRisk(context)
}

/**
 * Calculate inherent risk score for a processing activity
 */
export function calculateProcessingActivityInherentRisk(activity: ProcessingActivity): {
  score: RiskScore
  factors: RiskFactor[]
} {
  const context: RiskContext = { processingActivity: activity }
  return calculateInherentRisk(context)
}

/**
 * Generic inherent risk calculation
 */
function calculateInherentRisk(context: RiskContext): {
  score: RiskScore
  factors: RiskFactor[]
} {
  const factors: RiskFactor[] = []
  let totalWeight = 0
  let weightedSum = 0

  for (const definition of RISK_FACTOR_DEFINITIONS) {
    const value = definition.evaluator(context)
    const factor: RiskFactor = {
      id: definition.id,
      name: definition.name,
      category: definition.category,
      weight: definition.weight,
      value,
    }
    factors.push(factor)

    totalWeight += definition.weight
    weightedSum += value * definition.weight
  }

  // Calculate average (1-5 scale)
  const averageScore = weightedSum / totalWeight

  // Map to likelihood and impact
  const likelihood = Math.round(averageScore) as 1 | 2 | 3 | 4 | 5
  const impact = calculateImpact(context)
  const score = likelihood * impact
  const level = getRiskLevelFromScore(score)

  return {
    score: {
      likelihood,
      impact,
      score,
      level,
      rationale: generateRationale(factors, likelihood, impact),
    },
    factors,
  }
}

/**
 * Calculate impact based on context
 */
function calculateImpact(context: RiskContext): 1 | 2 | 3 | 4 | 5 {
  let impactScore = 3 // Default medium

  if (context.vendor) {
    // Higher impact for critical services
    const criticalServices: ServiceCategory[] = ['CLOUD_INFRASTRUCTURE', 'ERP', 'PAYMENT', 'SECURITY']
    if (criticalServices.includes(context.vendor.serviceCategory)) {
      impactScore += 1
    }

    // Higher impact for content access
    if (context.vendor.dataAccessLevel === 'CONTENT') {
      impactScore += 1
    }
  }

  if (context.processingActivity) {
    // Higher impact for special categories
    if (context.processingActivity.personalDataCategories.some(isSpecialCategory)) {
      impactScore += 1
    }

    // Higher impact for high protection level
    if (context.processingActivity.protectionLevel === 'HIGH') {
      impactScore += 1
    }
  }

  return Math.min(5, Math.max(1, impactScore)) as 1 | 2 | 3 | 4 | 5
}

/**
 * Generate rationale text
 */
function generateRationale(
  factors: RiskFactor[],
  likelihood: number,
  impact: number
): string {
  const highFactors = factors.filter((f) => f.value >= 4).map((f) => f.name.de)
  const lowFactors = factors.filter((f) => f.value <= 2).map((f) => f.name.de)

  let rationale = `Bewertung: Eintrittswahrscheinlichkeit ${likelihood}/5, Auswirkung ${impact}/5.`

  if (highFactors.length > 0) {
    rationale += ` Erhöhte Risikofaktoren: ${highFactors.join(', ')}.`
  }

  if (lowFactors.length > 0) {
    rationale += ` Risikomindernde Faktoren: ${lowFactors.join(', ')}.`
  }

  return rationale
}

// ==========================================
// RESIDUAL RISK CALCULATION
// ==========================================

/**
 * Calculate residual risk based on control effectiveness
 */
export function calculateResidualRisk(
  inherentRisk: RiskScore,
  controlInstances: ControlInstance[]
): RiskScore {
  // Calculate control effectiveness (0-1)
  const effectiveness = calculateControlEffectiveness(controlInstances)

  // Reduce likelihood based on control effectiveness
  const reducedLikelihood = Math.max(1, Math.round(inherentRisk.likelihood * (1 - effectiveness * 0.6)))

  // Impact reduction is smaller (controls primarily reduce likelihood)
  const reducedImpact = Math.max(1, Math.round(inherentRisk.impact * (1 - effectiveness * 0.3)))

  const residualScore = reducedLikelihood * reducedImpact
  const level = getRiskLevelFromScore(residualScore)

  return {
    likelihood: reducedLikelihood as 1 | 2 | 3 | 4 | 5,
    impact: reducedImpact as 1 | 2 | 3 | 4 | 5,
    score: residualScore,
    level,
    rationale: `Restrisiko nach Berücksichtigung von ${controlInstances.length} Kontrollen (Effektivität: ${Math.round(effectiveness * 100)}%).`,
  }
}

/**
 * Calculate overall control effectiveness
 */
function calculateControlEffectiveness(controlInstances: ControlInstance[]): number {
  if (controlInstances.length === 0) return 0

  const statusScores: Record<string, number> = {
    PASS: 1,
    PARTIAL: 0.5,
    FAIL: 0,
    NOT_APPLICABLE: 0,
    PLANNED: 0.2,
  }

  const totalScore = controlInstances.reduce(
    (sum, ci) => sum + (statusScores[ci.status] || 0),
    0
  )

  return totalScore / controlInstances.length
}

// ==========================================
// RISK MATRIX
// ==========================================

export interface RiskMatrixCell {
  likelihood: number
  impact: number
  level: RiskLevel
  score: number
}

/**
 * Generate full risk matrix
 */
export function generateRiskMatrix(): RiskMatrixCell[][] {
  const matrix: RiskMatrixCell[][] = []

  for (let likelihood = 1; likelihood <= 5; likelihood++) {
    const row: RiskMatrixCell[] = []
    for (let impact = 1; impact <= 5; impact++) {
      const score = likelihood * impact
      row.push({
        likelihood,
        impact,
        score,
        level: getRiskLevelFromScore(score),
      })
    }
    matrix.push(row)
  }

  return matrix
}

/**
 * Get risk matrix colors
 */
export function getRiskLevelColor(level: RiskLevel): {
  bg: string
  text: string
  border: string
} {
  switch (level) {
    case 'LOW':
      return { bg: 'bg-green-100', text: 'text-green-800', border: 'border-green-300' }
    case 'MEDIUM':
      return { bg: 'bg-yellow-100', text: 'text-yellow-800', border: 'border-yellow-300' }
    case 'HIGH':
      return { bg: 'bg-orange-100', text: 'text-orange-800', border: 'border-orange-300' }
    case 'CRITICAL':
      return { bg: 'bg-red-100', text: 'text-red-800', border: 'border-red-300' }
  }
}

// ==========================================
// RISK TREND ANALYSIS
// ==========================================

export interface RiskTrend {
  currentScore: number
  previousScore: number
  change: number
  trend: 'IMPROVING' | 'STABLE' | 'DETERIORATING'
}

/**
 * Calculate risk trend
 */
export function calculateRiskTrend(
  currentScore: number,
  previousScore: number
): RiskTrend {
  const change = currentScore - previousScore
  let trend: 'IMPROVING' | 'STABLE' | 'DETERIORATING'

  if (Math.abs(change) <= 2) {
    trend = 'STABLE'
  } else if (change < 0) {
    trend = 'IMPROVING'
  } else {
    trend = 'DETERIORATING'
  }

  return {
    currentScore,
    previousScore,
    change,
    trend,
  }
}
