package gci

import (
	"fmt"
	"math"
	"time"
)

// Engine calculates the GCI score
type Engine struct{}

// NewEngine creates a new GCI calculation engine
func NewEngine() *Engine {
	return &Engine{}
}

// Calculate computes the full GCI result for a tenant
func (e *Engine) Calculate(tenantID string, profileID string) *GCIResult {
	now := time.Now()
	profile := GetProfile(profileID)
	auditTrail := []AuditEntry{}

	// Step 1: Get module data (mock for now)
	modules := MockModuleData(tenantID)
	certDates := MockCertificateData()

	// Step 2: Calculate Level 1 - Module Scores with validity
	for i := range modules {
		m := &modules[i]
		if m.Assigned > 0 {
			m.RawScore = float64(m.Completed) / float64(m.Assigned) * 100.0
		}
		// Apply validity factor
		if validUntil, ok := certDates[m.ModuleID]; ok {
			m.ValidityFactor = CalculateValidityFactor(validUntil, now)
		} else {
			m.ValidityFactor = 1.0 // No certificate tracking = assume valid
		}
		m.FinalScore = m.RawScore * m.ValidityFactor

		if m.ValidityFactor < 1.0 {
			auditTrail = append(auditTrail, AuditEntry{
				Timestamp:   now,
				Factor:      "validity_decay",
				Description: fmt.Sprintf("Modul '%s': Gueltigkeitsfaktor %.2f (Zertifikat laeuft ab/abgelaufen)", m.ModuleName, m.ValidityFactor),
				Value:       m.ValidityFactor,
				Impact:      "negative",
			})
		}
	}

	// Step 3: Calculate Level 2 - Risk-Weighted Scores per area
	areaModules := map[string][]ModuleScore{
		"dsgvo":    {},
		"nis2":     {},
		"iso27001": {},
		"ai_act":   {},
	}
	for _, m := range modules {
		if _, ok := areaModules[m.Category]; ok {
			areaModules[m.Category] = append(areaModules[m.Category], m)
		}
	}

	level2Areas := []RiskWeightedScore{}
	areaNames := map[string]string{
		"dsgvo":    "DSGVO",
		"nis2":     "NIS2",
		"iso27001": "ISO 27001",
		"ai_act":   "EU AI Act",
	}

	for areaID, mods := range areaModules {
		rws := RiskWeightedScore{
			AreaID:   areaID,
			AreaName: areaNames[areaID],
			Modules:  mods,
		}
		for _, m := range mods {
			rws.WeightedSum += m.FinalScore * m.RiskWeight
			rws.TotalWeight += m.RiskWeight
		}
		if rws.TotalWeight > 0 {
			rws.AreaScore = rws.WeightedSum / rws.TotalWeight
		}
		level2Areas = append(level2Areas, rws)
	}

	// Step 4: Calculate Level 3 - Regulation Area Scores
	areaScores := []RegulationAreaScore{}
	for _, rws := range level2Areas {
		weight := profile.Weights[rws.AreaID]
		completedCount := 0
		for _, m := range rws.Modules {
			if m.Completed >= m.Assigned && m.Assigned > 0 {
				completedCount++
			}
		}
		ras := RegulationAreaScore{
			RegulationID:   rws.AreaID,
			RegulationName: rws.AreaName,
			Score:          math.Round(rws.AreaScore*100) / 100,
			Weight:         weight,
			WeightedScore:  rws.AreaScore * weight,
			ModuleCount:    len(rws.Modules),
			CompletedCount: completedCount,
		}
		areaScores = append(areaScores, ras)

		auditTrail = append(auditTrail, AuditEntry{
			Timestamp:   now,
			Factor:      "area_score",
			Description: fmt.Sprintf("Bereich '%s': Score %.1f, Gewicht %.0f%%", rws.AreaName, rws.AreaScore, weight*100),
			Value:       rws.AreaScore,
			Impact:      "neutral",
		})
	}

	// Step 5: Calculate raw GCI
	rawGCI := 0.0
	totalWeight := 0.0
	for _, ras := range areaScores {
		rawGCI += ras.WeightedScore
		totalWeight += ras.Weight
	}
	if totalWeight > 0 {
		rawGCI = rawGCI / totalWeight
	}

	// Step 6: Apply Criticality Multiplier
	criticalityMult := calculateCriticalityMultiplier(modules)
	auditTrail = append(auditTrail, AuditEntry{
		Timestamp:   now,
		Factor:      "criticality_multiplier",
		Description: fmt.Sprintf("Kritikalitaetsmultiplikator: %.3f", criticalityMult),
		Value:       criticalityMult,
		Impact: func() string {
			if criticalityMult < 1.0 {
				return "negative"
			}
			return "neutral"
		}(),
	})

	// Step 7: Apply Incident Adjustment
	openInc, critInc := MockIncidentData()
	incidentAdj := calculateIncidentAdjustment(openInc, critInc)
	auditTrail = append(auditTrail, AuditEntry{
		Timestamp:   now,
		Factor:      "incident_adjustment",
		Description: fmt.Sprintf("Vorfallsanpassung: %.3f (%d offen, %d kritisch)", incidentAdj, openInc, critInc),
		Value:       incidentAdj,
		Impact:      "negative",
	})

	// Step 8: Final GCI
	finalGCI := rawGCI * criticalityMult * incidentAdj
	finalGCI = math.Max(0, math.Min(100, math.Round(finalGCI*10)/10))

	// Step 9: Determine Maturity Level
	maturity := determineMaturityLevel(finalGCI)

	auditTrail = append(auditTrail, AuditEntry{
		Timestamp:   now,
		Factor:      "final_gci",
		Description: fmt.Sprintf("GCI-Endergebnis: %.1f → Reifegrad: %s", finalGCI, MaturityLabels[maturity]),
		Value:       finalGCI,
		Impact:      "neutral",
	})

	return &GCIResult{
		TenantID:         tenantID,
		GCIScore:         finalGCI,
		MaturityLevel:    maturity,
		MaturityLabel:    MaturityLabels[maturity],
		CalculatedAt:     now,
		Profile:          profileID,
		AreaScores:       areaScores,
		CriticalityMult:  criticalityMult,
		IncidentAdj:      incidentAdj,
		AuditTrail:       auditTrail,
	}
}

// CalculateBreakdown returns the full 4-level breakdown
func (e *Engine) CalculateBreakdown(tenantID string, profileID string) *GCIBreakdown {
	result := e.Calculate(tenantID, profileID)
	modules := MockModuleData(tenantID)
	certDates := MockCertificateData()
	now := time.Now()

	// Recalculate module scores for the breakdown
	for i := range modules {
		m := &modules[i]
		if m.Assigned > 0 {
			m.RawScore = float64(m.Completed) / float64(m.Assigned) * 100.0
		}
		if validUntil, ok := certDates[m.ModuleID]; ok {
			m.ValidityFactor = CalculateValidityFactor(validUntil, now)
		} else {
			m.ValidityFactor = 1.0
		}
		m.FinalScore = m.RawScore * m.ValidityFactor
	}

	// Build Level 2 areas
	areaModules := map[string][]ModuleScore{}
	for _, m := range modules {
		areaModules[m.Category] = append(areaModules[m.Category], m)
	}

	areaNames := map[string]string{"dsgvo": "DSGVO", "nis2": "NIS2", "iso27001": "ISO 27001", "ai_act": "EU AI Act"}
	level2 := []RiskWeightedScore{}
	for areaID, mods := range areaModules {
		rws := RiskWeightedScore{AreaID: areaID, AreaName: areaNames[areaID], Modules: mods}
		for _, m := range mods {
			rws.WeightedSum += m.FinalScore * m.RiskWeight
			rws.TotalWeight += m.RiskWeight
		}
		if rws.TotalWeight > 0 {
			rws.AreaScore = rws.WeightedSum / rws.TotalWeight
		}
		level2 = append(level2, rws)
	}

	return &GCIBreakdown{
		GCIResult:     *result,
		Level1Modules: modules,
		Level2Areas:   level2,
	}
}

// GetHistory returns historical GCI snapshots
func (e *Engine) GetHistory(tenantID string) []GCISnapshot {
	// Add current score to history
	result := e.Calculate(tenantID, "default")
	history := MockGCIHistory(tenantID)
	current := GCISnapshot{
		TenantID:      tenantID,
		Score:         result.GCIScore,
		MaturityLevel: result.MaturityLevel,
		AreaScores:    make(map[string]float64),
		CalculatedAt:  result.CalculatedAt,
	}
	for _, as := range result.AreaScores {
		current.AreaScores[as.RegulationID] = as.Score
	}
	history = append(history, current)
	return history
}

// GetMatrix returns the compliance matrix (roles x regulations)
func (e *Engine) GetMatrix(tenantID string) []ComplianceMatrixEntry {
	modules := MockModuleData(tenantID)

	roles := []struct {
		ID   string
		Name string
	}{
		{"management", "Geschaeftsfuehrung"},
		{"it_security", "IT-Sicherheit / CISO"},
		{"data_protection", "Datenschutz / DSB"},
		{"hr", "Personalwesen"},
		{"general", "Allgemeine Mitarbeiter"},
	}

	// Define which modules are relevant per role
	roleModules := map[string][]string{
		"management":      {"dsgvo-grundlagen", "nis2-management", "ai-governance", "iso-isms"},
		"it_security":     {"nis2-risikomanagement", "nis2-incident-response", "iso-zugangssteuerung", "iso-kryptografie", "ai-hochrisiko"},
		"data_protection": {"dsgvo-grundlagen", "dsgvo-betroffenenrechte", "dsgvo-tom", "dsgvo-dsfa", "dsgvo-auftragsverarbeitung"},
		"hr":              {"dsgvo-grundlagen", "dsgvo-betroffenenrechte", "nis2-management"},
		"general":         {"dsgvo-grundlagen", "nis2-risikomanagement", "ai-risikokategorien", "ai-transparenz"},
	}

	moduleMap := map[string]ModuleScore{}
	for _, m := range modules {
		moduleMap[m.ModuleID] = m
	}

	entries := []ComplianceMatrixEntry{}
	for _, role := range roles {
		entry := ComplianceMatrixEntry{
			Role:        role.ID,
			RoleName:    role.Name,
			Regulations: map[string]float64{},
		}

		regScores := map[string][]float64{}
		requiredModuleIDs := roleModules[role.ID]
		entry.RequiredModules = len(requiredModuleIDs)

		for _, modID := range requiredModuleIDs {
			if m, ok := moduleMap[modID]; ok {
				score := 0.0
				if m.Assigned > 0 {
					score = float64(m.Completed) / float64(m.Assigned) * 100
				}
				regScores[m.Category] = append(regScores[m.Category], score)
				if m.Completed >= m.Assigned && m.Assigned > 0 {
					entry.CompletedModules++
				}
			}
		}

		totalScore := 0.0
		count := 0
		for reg, scores := range regScores {
			sum := 0.0
			for _, s := range scores {
				sum += s
			}
			avg := sum / float64(len(scores))
			entry.Regulations[reg] = math.Round(avg*10) / 10
			totalScore += avg
			count++
		}
		if count > 0 {
			entry.OverallScore = math.Round(totalScore/float64(count)*10) / 10
		}

		entries = append(entries, entry)
	}

	return entries
}

// Helper functions

func calculateCriticalityMultiplier(modules []ModuleScore) float64 {
	criticalModules := 0
	criticalLow := 0
	for _, m := range modules {
		if m.RiskWeight >= 2.5 {
			criticalModules++
			if m.FinalScore < 50 {
				criticalLow++
			}
		}
	}
	if criticalModules == 0 {
		return 1.0
	}
	// Reduce score if critical modules have low completion
	ratio := float64(criticalLow) / float64(criticalModules)
	return 1.0 - (ratio * 0.15) // max 15% reduction
}

func calculateIncidentAdjustment(openIncidents, criticalIncidents int) float64 {
	adj := 1.0
	// Each open incident reduces by 1%
	adj -= float64(openIncidents) * 0.01
	// Each critical incident reduces by additional 3%
	adj -= float64(criticalIncidents) * 0.03
	return math.Max(0.8, adj) // minimum 80% (max 20% reduction)
}

func determineMaturityLevel(score float64) string {
	switch {
	case score >= 90:
		return MaturityOptimized
	case score >= 75:
		return MaturityManaged
	case score >= 60:
		return MaturityDefined
	case score >= 40:
		return MaturityReactive
	default:
		return MaturityHighRisk
	}
}
