package usecase

import (
	"fmt"
	"strings"
)

// GapDetector identifies missing regulations for a use-case template.
type GapDetector struct {
	store *Store
}

// NewGapDetector creates a GapDetector.
func NewGapDetector(store *Store) *GapDetector {
	return &GapDetector{store: store}
}

// DetectMissingRegulations finds MCs with insufficient source citations.
func (d *GapDetector) DetectMissingRegulations(tmpl *Template) ([]MissingSource, error) {
	mcs, err := d.store.FetchMCsByFilters(tmpl.MCFilters)
	if err != nil {
		return nil, fmt.Errorf("fetch MCs: %w", err)
	}
	if len(mcs) == 0 {
		return nil, nil
	}

	mcIDs := make([]string, len(mcs))
	for i, mc := range mcs {
		mcIDs[i] = mc.MasterControlID
	}

	citations, err := d.store.CountMCSourceCitations(mcIDs)
	if err != nil {
		return nil, fmt.Errorf("count citations: %w", err)
	}

	var gaps []MissingSource

	for _, mc := range mcs {
		citCount := citations[mc.MasterControlID]

		// MC with many controls but few citations → gap
		if mc.TotalControls > 20 && citCount < 3 {
			missing := identifyMissingRegulation(mc, tmpl.Regulations)
			if missing != nil {
				gaps = append(gaps, *missing)
			}
		}

		// MC topic implies a regulation that's not in source citations
		expectedRegs := expectedRegulations(mc.CanonicalName)
		for _, expected := range expectedRegs {
			if !containsRegulation(tmpl.Regulations, expected.regID) {
				continue
			}
			if mc.RegSource == "" || !strings.Contains(mc.RegSource, expected.keyword) {
				gaps = append(gaps, MissingSource{
					Regulation:   expected.name,
					AffectsMCs:   []string{mc.CanonicalName},
					EstimatedGap: mc.TotalControls / 3,
					SourceURL:    expected.url,
					Priority:     expected.priority,
				})
			}
		}
	}

	return deduplicateGaps(gaps), nil
}

// DetectAuditGaps checks an audit's answers for regulation-specific gaps.
func (d *GapDetector) DetectAuditGaps(audit *Audit, answers []Answer) []MissingSource {
	answerMap := make(map[string]Answer)
	for _, a := range answers {
		answerMap[a.QuestionID] = a
	}

	// Find regulations with many failures
	failsByReg := make(map[string]int)
	totalByReg := make(map[string]int)

	for _, q := range audit.Questions {
		if q.Regulation == "" {
			continue
		}
		totalByReg[q.Regulation]++
		a, ok := answerMap[q.ID]
		if ok && !isPassed(a) {
			failsByReg[q.Regulation]++
		}
	}

	var gaps []MissingSource
	for reg, fails := range failsByReg {
		total := totalByReg[reg]
		if total > 0 && float64(fails)/float64(total) > 0.5 {
			gaps = append(gaps, MissingSource{
				Regulation:   reg,
				AffectsMCs:   []string{audit.TemplateID},
				EstimatedGap: fails,
				Priority:     "high",
			})
		}
	}

	return gaps
}

type expectedReg struct {
	regID    string
	name     string
	keyword  string
	url      string
	priority string
}

func expectedRegulations(mcName string) []expectedReg {
	mappings := []struct {
		prefix string
		regs   []expectedReg
	}{
		{"data_processing_agreement", []expectedReg{
			{regID: "dsgvo", name: "DSGVO (EU) 2016/679", keyword: "DSGVO", url: "https://eur-lex.europa.eu/legal-content/DE/TXT/?uri=CELEX:32016R0679", priority: "high"},
		}},
		{"incident_", []expectedReg{
			{regID: "nis2", name: "NIS2-Richtlinie (EU) 2022/2555", keyword: "NIS2", url: "https://eur-lex.europa.eu/legal-content/DE/TXT/?uri=CELEX:32022L2555", priority: "high"},
		}},
		{"vulnerability_", []expectedReg{
			{regID: "cra", name: "Cyber Resilience Act (CRA)", keyword: "CRA", url: "https://eur-lex.europa.eu/legal-content/DE/TXT/?uri=CELEX:32024R2847", priority: "high"},
		}},
		{"aml_", []expectedReg{
			{regID: "aml", name: "5. Geldwaescherichtlinie (EU) 2024/1624", keyword: "Geldwaesche", url: "https://eur-lex.europa.eu/legal-content/DE/TXT/?uri=CELEX:32024L1624", priority: "high"},
		}},
	}

	var result []expectedReg
	for _, m := range mappings {
		if strings.HasPrefix(mcName, m.prefix) {
			result = append(result, m.regs...)
		}
	}
	return result
}

func identifyMissingRegulation(mc MCInfo, templateRegs []string) *MissingSource {
	if mc.RegSource != "" {
		return nil
	}
	return &MissingSource{
		Regulation:   fmt.Sprintf("Unbekannte Quelle fuer '%s'", mc.CanonicalName),
		AffectsMCs:   []string{mc.CanonicalName},
		EstimatedGap: mc.TotalControls,
		Priority:     "medium",
	}
}

func containsRegulation(regs []string, id string) bool {
	for _, r := range regs {
		if strings.EqualFold(r, id) {
			return true
		}
	}
	return true // if template doesn't restrict, always check
}

func deduplicateGaps(gaps []MissingSource) []MissingSource {
	seen := make(map[string]*MissingSource)
	for i := range gaps {
		key := gaps[i].Regulation
		if existing, ok := seen[key]; ok {
			existing.AffectsMCs = append(existing.AffectsMCs, gaps[i].AffectsMCs...)
			existing.EstimatedGap += gaps[i].EstimatedGap
		} else {
			copy := gaps[i]
			seen[key] = &copy
		}
	}

	result := make([]MissingSource, 0, len(seen))
	for _, g := range seen {
		result = append(result, *g)
	}
	return result
}
