package iace

import "strconv"

// DeltaResult contains the impact of a proposed change to a project.
// It compares Match(current) vs Match(current+proposed) and returns
// the difference in matched patterns, hazards, measures, and evidence.
type DeltaResult struct {
	// Patterns that would fire with the proposed change but not currently
	AddedPatterns []PatternMatch `json:"added_patterns"`
	// Patterns that currently fire but would stop with the proposed change
	RemovedPatterns []PatternMatch `json:"removed_patterns"`
	// Net change counts
	AddedHazardCount   int `json:"added_hazard_count"`
	RemovedHazardCount int `json:"removed_hazard_count"`
	AddedMeasureCount  int `json:"added_measure_count"`
	RemovedMeasureCount int `json:"removed_measure_count"`
	AddedEvidenceCount  int `json:"added_evidence_count"`
	RemovedEvidenceCount int `json:"removed_evidence_count"`
	// Added/removed hazard categories
	AddedHazardCats   []string `json:"added_hazard_categories,omitempty"`
	RemovedHazardCats []string `json:"removed_hazard_categories,omitempty"`
	// Added/removed measure IDs
	AddedMeasureIDs   []string `json:"added_measure_ids,omitempty"`
	RemovedMeasureIDs []string `json:"removed_measure_ids,omitempty"`
	// Summary text (German)
	SummaryDE string `json:"summary_de"`
}

// DeltaMatch computes the impact of a proposed change by comparing
// the current match output with the proposed match output.
func (e *PatternEngine) DeltaMatch(current, proposed MatchInput) *DeltaResult {
	currentOutput := e.Match(current)
	proposedOutput := e.Match(proposed)

	// Build pattern ID sets
	currentPatterns := make(map[string]PatternMatch)
	for _, p := range currentOutput.MatchedPatterns {
		currentPatterns[p.PatternID] = p
	}
	proposedPatterns := make(map[string]PatternMatch)
	for _, p := range proposedOutput.MatchedPatterns {
		proposedPatterns[p.PatternID] = p
	}

	// Find added/removed patterns
	var added, removed []PatternMatch
	for id, p := range proposedPatterns {
		if _, exists := currentPatterns[id]; !exists {
			added = append(added, p)
		}
	}
	for id, p := range currentPatterns {
		if _, exists := proposedPatterns[id]; !exists {
			removed = append(removed, p)
		}
	}

	// Compute hazard category diff
	currentCats := collectCats(currentOutput.SuggestedHazards)
	proposedCats := collectCats(proposedOutput.SuggestedHazards)
	addedCats := diffStrings(proposedCats, currentCats)
	removedCats := diffStrings(currentCats, proposedCats)

	// Compute measure ID diff
	currentMIDs := collectMIDs(currentOutput.SuggestedMeasures)
	proposedMIDs := collectMIDs(proposedOutput.SuggestedMeasures)
	addedMIDs := diffStrings(proposedMIDs, currentMIDs)
	removedMIDs := diffStrings(currentMIDs, proposedMIDs)

	// Compute evidence ID diff
	currentEIDs := collectEIDs(currentOutput.SuggestedEvidence)
	proposedEIDs := collectEIDs(proposedOutput.SuggestedEvidence)

	result := &DeltaResult{
		AddedPatterns:        added,
		RemovedPatterns:      removed,
		AddedHazardCount:     len(addedCats),
		RemovedHazardCount:   len(removedCats),
		AddedMeasureCount:    len(addedMIDs),
		RemovedMeasureCount:  len(removedMIDs),
		AddedEvidenceCount:   len(diffStrings(proposedEIDs, currentEIDs)),
		RemovedEvidenceCount: len(diffStrings(currentEIDs, proposedEIDs)),
		AddedHazardCats:      addedCats,
		RemovedHazardCats:    removedCats,
		AddedMeasureIDs:      addedMIDs,
		RemovedMeasureIDs:    removedMIDs,
	}

	result.SummaryDE = buildDeltaSummary(result)
	return result
}

func collectCats(hazards []HazardSuggestion) []string {
	var cats []string
	for _, h := range hazards {
		cats = append(cats, h.Category)
	}
	return cats
}

func collectMIDs(measures []MeasureSuggestion) []string {
	var ids []string
	for _, m := range measures {
		ids = append(ids, m.MeasureID)
	}
	return ids
}

func collectEIDs(evidence []EvidenceSuggestion) []string {
	var ids []string
	for _, e := range evidence {
		ids = append(ids, e.EvidenceID)
	}
	return ids
}

func diffStrings(a, b []string) []string {
	bSet := make(map[string]bool)
	for _, s := range b {
		bSet[s] = true
	}
	var diff []string
	seen := make(map[string]bool)
	for _, s := range a {
		if !bSet[s] && !seen[s] {
			diff = append(diff, s)
			seen[s] = true
		}
	}
	return diff
}

func buildDeltaSummary(r *DeltaResult) string {
	if len(r.AddedPatterns) == 0 && len(r.RemovedPatterns) == 0 {
		return "Keine Auswirkung — die vorgeschlagene Aenderung veraendert die Risikobeurteilung nicht."
	}
	s := ""
	if len(r.AddedPatterns) > 0 {
		s += "+" + deltaItoa(len(r.AddedPatterns)) + " neue Gefaehrdungsmuster erkannt"
	}
	if len(r.RemovedPatterns) > 0 {
		if s != "" {
			s += ", "
		}
		s += "-" + deltaItoa(len(r.RemovedPatterns)) + " Gefaehrdungsmuster entfallen"
	}
	if r.AddedHazardCount > 0 {
		s += ". +" + deltaItoa(r.AddedHazardCount) + " neue Gefaehrdungskategorien"
	}
	if r.AddedMeasureCount > 0 {
		s += ". +" + deltaItoa(r.AddedMeasureCount) + " zusaetzliche Massnahmen empfohlen"
	}
	s += "."
	return s
}

func deltaItoa(i int) string {
	return strconv.Itoa(i)
}
