package gap

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

// Engine orchestrates the gap analysis pipeline.
type Engine struct {
	classifier *Classifier
	store      *Store
}

// NewEngine creates a new gap analysis engine.
func NewEngine(store *Store) *Engine {
	return &Engine{
		classifier: NewClassifier(),
		store:      store,
	}
}

// Analyze runs the full gap analysis for a product profile.
func (e *Engine) Analyze(profile *ProductProfile) (*GapReport, error) {
	// Step 1: Extract scope signals
	signals := e.classifier.ExtractScopeSignals(profile)

	// Step 2: Classify regulations
	regulations := e.classifier.ClassifyAll(profile)

	// Step 3: Fetch applicable MCs from DB
	mcGroups, err := e.store.FetchApplicableMCs(signals, regulations)
	if err != nil {
		return nil, fmt.Errorf("fetch MCs: %w", err)
	}

	// Step 4: Assess gaps
	gaps := make([]GapItem, 0, len(mcGroups))
	for _, mc := range mcGroups {
		status := e.assessGapStatus(mc, profile)
		item := GapItem{
			MCID:          mc.MasterControlID,
			MCName:        mc.CanonicalName,
			Regulation:    mc.Regulation,
			Status:        status,
			Title:         mc.Title,
			Description:   mc.Description,
			Severity:      mc.Severity,
			ControlCount:  mc.ControlCount,
			Recommendation: e.generateRecommendation(mc, status),
		}
		item.Priority = e.calculatePriority(item, regulations)
		gaps = append(gaps, item)
	}

	// Step 5: Sort by priority (highest first)
	sort.Slice(gaps, func(i, j int) bool {
		return gaps[i].Priority.Score > gaps[j].Priority.Score
	})

	// Assign ranks
	for i := range gaps {
		gaps[i].Priority.Rank = i + 1
	}

	// Step 6: Build report
	report := &GapReport{
		ProfileID:   profile.ID,
		ProfileName: profile.Name,
		Regulations: regulations,
		Summary:     e.buildSummary(gaps, regulations),
		Gaps:        gaps,
		CreatedAt:   time.Now(),
	}

	return report, nil
}

// assessGapStatus determines if a MC is fulfilled based on IST-Zustand:
// IACE project data, applied norms, certifications, and existing processes.
func (e *Engine) assessGapStatus(mc MCGroup, profile *ProductProfile) GapStatus {
	name := mc.CanonicalName

	// A) IACE-Projekt vorhanden → aus verified Mitigations ableiten
	if profile.IACEProjectID != nil {
		status := e.store.CheckIACECoverage(*profile.IACEProjectID, name)
		if status == "verified" {
			return GapFulfilled
		}
		if status == "implemented" {
			return GapPartial
		}
	}

	// B) Bestehende Zertifizierungen
	for _, cert := range profile.ExistingCertifications {
		switch cert {
		case "CE":
			if isMachineryTopic(name) {
				return GapFulfilled
			}
		case "ISO27001":
			if isSecurityTopic(name) {
				return GapPartial
			}
		case "SOC2":
			if isSecurityTopic(name) {
				return GapPartial
			}
		case "ISO13485":
			if contains(name, "risk_management") || contains(name, "documentation") {
				return GapPartial
			}
		}
	}

	// C) Angewandte Normen → Controls als fulfilled erkennen
	if normCoversControl(profile.AppliedNorms, name) {
		return GapFulfilled
	}

	// D) IST-Felder direkt matchen
	if profile.HasSBOM && contains(name, "asset_management_inventory") {
		return GapFulfilled
	}
	if profile.HasVulnManagement && contains(name, "vulnerability") {
		return GapFulfilled
	}
	if profile.HasUpdateMechanism && contains(name, "patch_management") {
		return GapFulfilled
	}
	if profile.HasIncidentResponse && contains(name, "incident") {
		return GapFulfilled
	}
	if profile.HasRiskAssessment && contains(name, "risk_management") {
		return GapFulfilled
	}
	if profile.HasTechnicalFile && contains(name, "documentation") {
		return GapFulfilled
	}
	if profile.HasOperatingManual && contains(name, "operating_instructions") {
		return GapFulfilled
	}
	if profile.HasSupplyChainMgmt && contains(name, "third_party_management") {
		return GapFulfilled
	}

	// E) CE-Kennzeichnung vorhanden → Produktsicherheit fulfilled
	if profile.CEMarkingSince != nil && isMachineryTopic(name) {
		return GapFulfilled
	}

	return GapMissing
}

// calculatePriority computes the priority score for a gap.
func (e *Engine) calculatePriority(item GapItem, regs []ApplicableRegulation) Priority {
	p := Priority{
		SeverityFactor:   severityToFactor(item.Severity),
		DeadlineFactor:   1.0,
		DependencyFactor: 1.0,
	}

	// Find deadline for this regulation
	for _, reg := range regs {
		if reg.ID == item.Regulation && reg.Deadline != nil {
			monthsUntil := time.Until(*reg.Deadline).Hours() / (24 * 30)
			if monthsUntil < 6 {
				p.DeadlineFactor = 3.0
			} else if monthsUntil < 12 {
				p.DeadlineFactor = 2.0
			}
		}
	}

	// Dependency: foundational controls get higher priority
	if isFoundational(item.MCName) {
		p.DependencyFactor = 2.0
	}

	p.Score = p.SeverityFactor * p.DeadlineFactor * p.DependencyFactor

	// Only gaps count — fulfilled items get score 0
	if item.Status == GapFulfilled {
		p.Score = 0
	} else if item.Status == GapPartial {
		p.Score *= 0.5
	}

	return p
}

// generateRecommendation creates an actionable recommendation for a gap.
func (e *Engine) generateRecommendation(mc MCGroup, status GapStatus) string {
	if status == GapFulfilled {
		return "Bereits erfüllt — keine Maßnahme erforderlich."
	}

	// Build recommendation from MC name + regulation
	name := mc.CanonicalName
	switch {
	case contains(name, "encryption"):
		return "Verschlüsselungsimplementierung prüfen und dokumentieren."
	case contains(name, "access_control"):
		return "Zugriffskontrollkonzept erstellen und Berechtigungen überprüfen."
	case contains(name, "incident"):
		return "Incident-Response-Plan erstellen und Meldeprozesse etablieren."
	case contains(name, "vulnerability"):
		return "Schwachstellenmanagement einführen (Scanning, CVE-Tracking, Patching)."
	case contains(name, "audit_logging"):
		return "Protokollierung implementieren und Audit-Trail sicherstellen."
	case contains(name, "data_retention"):
		return "Löschkonzept erstellen mit konkreten Fristen pro Datenkategorie."
	case contains(name, "consent"):
		return "Einwilligungsmanagement implementieren (Opt-In, Widerruf, Dokumentation)."
	case contains(name, "dpia"):
		return "Datenschutz-Folgenabschätzung durchführen und dokumentieren."
	case contains(name, "training"):
		return "Schulungsprogramm für Mitarbeiter etablieren."
	case contains(name, "risk_management"):
		return "Risikobewertung durchführen und Maßnahmenplan erstellen."
	default:
		return fmt.Sprintf("Anforderung '%s' prüfen und Umsetzung planen.", mc.Title)
	}
}

// buildSummary aggregates gap statistics.
func (e *Engine) buildSummary(gaps []GapItem, regs []ApplicableRegulation) GapSummary {
	s := GapSummary{
		TotalApplicableRegulations: len(regs),
		TotalGaps:                  len(gaps),
		GapsByStatus:               map[string]int{},
		GapsBySeverity:             map[string]int{},
		GapsByRegulation:           map[string]int{},
	}

	fulfilled := 0
	for _, g := range gaps {
		s.GapsByStatus[string(g.Status)]++
		s.GapsBySeverity[g.Severity]++
		s.GapsByRegulation[string(g.Regulation)]++
		if g.Status == GapFulfilled {
			fulfilled++
		}

		// Rough effort estimate per gap
		switch g.Severity {
		case "CRITICAL":
			s.EstimatedEffortWeeks += 2
		case "HIGH":
			s.EstimatedEffortWeeks += 1
		case "MEDIUM":
			s.EstimatedEffortWeeks += 0.5
		case "LOW":
			s.EstimatedEffortWeeks += 0.25
		}
	}

	if len(gaps) > 0 {
		s.OverallCompliancePercent = math.Round(float64(fulfilled)/float64(len(gaps))*1000) / 10
	}

	// Only count effort for non-fulfilled gaps
	s.EstimatedEffortWeeks = math.Round(s.EstimatedEffortWeeks*10) / 10

	return s
}

// ── Helpers ──────────────────────────────────────────────────────────

func severityToFactor(sev string) float64 {
	switch sev {
	case "CRITICAL":
		return 4.0
	case "HIGH":
		return 3.0
	case "MEDIUM":
		return 2.0
	case "LOW":
		return 1.0
	default:
		return 2.0
	}
}

func isFoundational(name string) bool {
	foundational := []string{
		"risk_management", "policy", "asset_management",
		"access_control_rbac", "encryption_key",
	}
	for _, f := range foundational {
		if contains(name, f) {
			return true
		}
	}
	return false
}

func isSecurityTopic(name string) bool {
	topics := []string{
		"encryption", "access_control", "vulnerability", "patch_management",
		"audit_logging", "monitoring", "firewall", "network_security",
		"session_management", "multi_factor_auth", "key_management",
		"backup", "disaster_recovery", "incident",
	}
	for _, t := range topics {
		if contains(name, t) {
			return true
		}
	}
	return false
}

func isMachineryTopic(name string) bool {
	return contains(name, "product_safety") || contains(name, "certification")
}

func contains(s, substr string) bool {
	return len(s) >= len(substr) && (s == substr || len(s) > len(substr) &&
		(s[:len(substr)] == substr || s[len(s)-len(substr):] == substr ||
			findSubstring(s, substr)))
}

func findSubstring(s, sub string) bool {
	for i := 0; i <= len(s)-len(sub); i++ {
		if s[i:i+len(sub)] == sub {
			return true
		}
	}
	return false
}

// MCGroup represents a Master Control with aggregated info for gap analysis.
type MCGroup struct {
	MasterControlID string
	CanonicalName   string
	Title           string
	Description     string
	Regulation      RegulationID
	Severity        string
	ControlCount    int
}
