/**
 * Compliance Module
 *
 * General compliance functionality including controls, evidence,
 * AI Act, NIS2, and regulatory obligations
 */

import type {
  Control,
  Evidence,
  Requirement,
  Obligation,
  AIActResult,
  AIActRiskCategory,
  RegulationCode,
  ComplianceScore,
  SDKState,
  RiskSeverity,
} from '@breakpilot/compliance-sdk-types'
import { ComplianceClient } from '../client'

export class ComplianceModule {
  private client: ComplianceClient
  private getState: () => SDKState

  constructor(client: ComplianceClient, getState: () => SDKState) {
    this.client = client
    this.getState = getState
  }

  // ---------------------------------------------------------------------------
  // Controls
  // ---------------------------------------------------------------------------

  getControls(): Control[] {
    return this.getState().controls
  }

  getControlById(id: string): Control | undefined {
    return this.getState().controls.find(c => c.id === id)
  }

  getControlsByDomain(domain: string): Control[] {
    return this.getState().controls.filter(c => c.domain === domain)
  }

  getControlsByStatus(status: string): Control[] {
    return this.getState().controls.filter(c => c.implementationStatus === status)
  }

  getControlComplianceRate(): number {
    const controls = this.getControls()
    if (controls.length === 0) return 0

    const implemented = controls.filter(c => c.implementationStatus === 'IMPLEMENTED').length
    return Math.round((implemented / controls.length) * 100)
  }

  // ---------------------------------------------------------------------------
  // Evidence
  // ---------------------------------------------------------------------------

  getEvidence(): Evidence[] {
    return this.getState().evidence
  }

  getEvidenceById(id: string): Evidence | undefined {
    return this.getState().evidence.find(e => e.id === id)
  }

  getEvidenceByControlId(controlId: string): Evidence[] {
    return this.getState().evidence.filter(e => e.controlId === controlId)
  }

  getExpiringEvidence(days: number = 30): Evidence[] {
    const cutoff = new Date()
    cutoff.setDate(cutoff.getDate() + days)

    return this.getState().evidence.filter(e => {
      if (!e.validUntil) return false
      const validUntil = new Date(e.validUntil)
      return validUntil <= cutoff && e.status === 'ACTIVE'
    })
  }

  // ---------------------------------------------------------------------------
  // Requirements
  // ---------------------------------------------------------------------------

  getRequirements(): Requirement[] {
    return this.getState().requirements
  }

  getRequirementsByRegulation(regulation: RegulationCode): Requirement[] {
    return this.getState().requirements.filter(r => r.regulationCode === regulation)
  }

  getRequirementComplianceRate(regulation?: RegulationCode): number {
    let requirements = this.getRequirements()
    if (regulation) {
      requirements = requirements.filter(r => r.regulationCode === regulation)
    }
    if (requirements.length === 0) return 0

    const implemented = requirements.filter(
      r => r.status === 'IMPLEMENTED' || r.status === 'VERIFIED'
    ).length
    return Math.round((implemented / requirements.length) * 100)
  }

  // ---------------------------------------------------------------------------
  // Obligations
  // ---------------------------------------------------------------------------

  getObligations(): Obligation[] {
    return this.getState().obligations
  }

  getUpcomingObligations(days: number = 30): Obligation[] {
    const cutoff = new Date()
    cutoff.setDate(cutoff.getDate() + days)

    return this.getState().obligations.filter(o => {
      if (!o.deadline || o.status === 'COMPLETED') return false
      const deadline = new Date(o.deadline)
      return deadline <= cutoff
    })
  }

  getOverdueObligations(): Obligation[] {
    const now = new Date()

    return this.getState().obligations.filter(o => {
      if (!o.deadline || o.status === 'COMPLETED') return false
      const deadline = new Date(o.deadline)
      return deadline < now
    })
  }

  // ---------------------------------------------------------------------------
  // AI Act
  // ---------------------------------------------------------------------------

  getAIActClassification(): AIActResult | null {
    return this.getState().aiActClassification
  }

  getAIActRiskCategory(): AIActRiskCategory | null {
    return this.getState().aiActClassification?.riskCategory ?? null
  }

  isHighRiskAI(): boolean {
    const category = this.getAIActRiskCategory()
    return category === 'HIGH' || category === 'UNACCEPTABLE'
  }

  // ---------------------------------------------------------------------------
  // Compliance Score
  // ---------------------------------------------------------------------------

  calculateComplianceScore(): ComplianceScore {
    const state = this.getState()

    // Calculate overall score based on controls, requirements, and evidence
    const controlScore = this.getControlComplianceRate()
    const requirementScore = this.getRequirementComplianceRate()
    const evidenceCoverage = this.calculateEvidenceCoverage()

    const overall = Math.round((controlScore + requirementScore + evidenceCoverage) / 3)

    // Calculate scores by regulation
    const byRegulation: Record<string, number> = {}
    const regulations = new Set(state.requirements.map(r => r.regulationCode))
    regulations.forEach(reg => {
      byRegulation[reg] = this.getRequirementComplianceRate(reg as RegulationCode)
    })

    // Calculate scores by domain
    const byDomain: Record<string, number> = {}
    const domains = new Set(state.controls.map(c => c.domain))
    domains.forEach(domain => {
      const domainControls = state.controls.filter(c => c.domain === domain)
      const implemented = domainControls.filter(c => c.implementationStatus === 'IMPLEMENTED').length
      byDomain[domain] = domainControls.length > 0
        ? Math.round((implemented / domainControls.length) * 100)
        : 0
    })

    return {
      overall,
      byRegulation: byRegulation as Record<RegulationCode, number>,
      byDomain: byDomain as Record<string, number>,
      trend: 'STABLE', // Would need historical data to calculate
      lastCalculated: new Date(),
    }
  }

  private calculateEvidenceCoverage(): number {
    const controls = this.getControls()
    const implementedControls = controls.filter(c => c.implementationStatus === 'IMPLEMENTED')

    if (implementedControls.length === 0) return 0

    const controlsWithEvidence = implementedControls.filter(c => {
      const evidence = this.getEvidenceByControlId(c.id)
      return evidence.some(e => e.status === 'ACTIVE')
    })

    return Math.round((controlsWithEvidence.length / implementedControls.length) * 100)
  }

  // ---------------------------------------------------------------------------
  // Risks
  // ---------------------------------------------------------------------------

  getRisks() {
    return this.getState().risks
  }

  getRisksByStatus(status: string) {
    return this.getRisks().filter(r => r.status === status)
  }

  getRisksBySeverity(severity: RiskSeverity) {
    return this.getRisks().filter(r => r.severity === severity)
  }

  getCriticalRisks() {
    return this.getRisks().filter(r => r.severity === 'CRITICAL' || r.severity === 'HIGH')
  }

  getAverageRiskScore(): number {
    const risks = this.getRisks()
    if (risks.length === 0) return 0

    const totalScore = risks.reduce((sum, r) => sum + r.residualRiskScore, 0)
    return Math.round(totalScore / risks.length)
  }
}

export function createComplianceModule(
  client: ComplianceClient,
  getState: () => SDKState
): ComplianceModule {
  return new ComplianceModule(client, getState)
}
