package iace

import "sort"

// GetProjectComplianceTriggers analyses a project's hazards and the full
// pattern library to determine which DSGVO/AI Act/CRA/NIS2/Data Act
// obligations are triggered. It returns a deduplicated, severity-sorted
// summary with boolean flags for each regulation family.
func GetProjectComplianceTriggers(hazards []Hazard, patterns []HazardPattern) *ComplianceTriggerSummary {
	triggerMap := GetComplianceTriggerMap()

	// Build set of pattern IDs present in the pattern library for quick lookup
	patternByID := make(map[string]HazardPattern, len(patterns))
	for _, p := range patterns {
		patternByID[p.ID] = p
	}

	// Collect all fired pattern IDs from the project's hazards.
	// Hazards created from pattern matching store the source pattern ID
	// in their Description ("Pattern HPXXX") or Name field.
	firedPatterns := make(map[string]bool)
	for _, h := range hazards {
		extractPatternIDs(h.Description, firedPatterns)
		extractPatternIDs(h.Name, firedPatterns)
		extractPatternIDs(h.Scenario, firedPatterns)
	}

	// Also check each pattern against the hazard categories present
	// in the project — if a pattern generates a category that exists
	// among the hazards, consider the pattern relevant.
	hazardCats := make(map[string]bool)
	for _, h := range hazards {
		if h.Category != "" {
			hazardCats[h.Category] = true
		}
	}
	for _, p := range patterns {
		for _, cat := range p.GeneratedHazardCats {
			if hazardCats[cat] {
				firedPatterns[p.ID] = true
				break
			}
		}
	}

	// Collect tag-level information from hazard metadata
	tags := collectHazardTags(hazards)
	tagTriggers := GetTagBasedTriggers(tags)

	// Gather all triggers from fired patterns
	var results []TriggerResult
	seenRegulation := make(map[string]bool)

	for pid := range firedPatterns {
		triggers, ok := triggerMap[pid]
		if !ok {
			continue
		}
		for _, t := range triggers {
			key := t.Regulation + "|" + t.Module
			if seenRegulation[key] {
				continue
			}
			seenRegulation[key] = true

			// Try to find a hazard name for context
			hName := findHazardNameForPattern(pid, hazards)
			results = append(results, TriggerResult{
				HazardID:   "",
				HazardName: hName,
				PatternID:  pid,
				Trigger:    t,
			})
		}
	}

	// Append tag-based triggers (deduplicated against pattern triggers)
	for _, t := range tagTriggers {
		key := t.Regulation + "|" + t.Module
		if seenRegulation[key] {
			continue
		}
		seenRegulation[key] = true
		results = append(results, TriggerResult{
			HazardID:   "",
			HazardName: "Tag-basiert",
			PatternID:  "",
			Trigger:    t,
		})
	}

	// Sort by severity: high > medium > low
	sort.Slice(results, func(i, j int) bool {
		return severityRank(results[i].Trigger.Severity) > severityRank(results[j].Trigger.Severity)
	})

	// Build boolean summary flags
	summary := buildSummaryFlags(results)

	return &ComplianceTriggerSummary{
		Triggers: results,
		Total:    len(results),
		Summary:  summary,
	}
}

// AllPatterns returns every registered hazard pattern. Delegates to
// collectAllPatterns() in pattern_registry.go so new pattern sources only
// need to be added in one place. Pre-2026-05-21 this function maintained
// a duplicate enumeration which silently drifted from the registry —
// CRA, ISO12100-gap, robot-cell, CNC, VDMA, textile-agri, GT-bremse and
// secondary-harm patterns were invisible to AllPatterns callers.
func AllPatterns() []HazardPattern {
	return collectAllPatterns()
}

// extractPatternIDs scans a text for "HP" followed by digits and adds
// any found pattern IDs to the set.
func extractPatternIDs(text string, set map[string]bool) {
	for i := 0; i < len(text)-2; i++ {
		if text[i] == 'H' && text[i+1] == 'P' && i+2 < len(text) && text[i+2] >= '0' && text[i+2] <= '9' {
			end := i + 2
			for end < len(text) && text[end] >= '0' && text[end] <= '9' {
				end++
			}
			set[text[i:end]] = true
		}
	}
}

// findHazardNameForPattern returns the name of the first hazard whose
// description/name/scenario mentions the given pattern ID.
func findHazardNameForPattern(pid string, hazards []Hazard) string {
	for _, h := range hazards {
		if containsPatternID(h.Description, pid) || containsPatternID(h.Name, pid) || containsPatternID(h.Scenario, pid) {
			return h.Name
		}
	}
	if len(hazards) > 0 {
		return hazards[0].Name
	}
	return ""
}

// containsPatternID checks whether text contains the exact pattern ID token.
func containsPatternID(text, pid string) bool {
	idx := 0
	for idx <= len(text)-len(pid) {
		if text[idx:idx+len(pid)] == pid {
			// Ensure it is not a substring of a longer ID
			after := idx + len(pid)
			if after >= len(text) || text[after] < '0' || text[after] > '9' {
				return true
			}
		}
		idx++
	}
	return false
}

// collectHazardTags extracts tag-like signals from hazard fields.
func collectHazardTags(hazards []Hazard) []string {
	tagSet := make(map[string]bool)
	for _, h := range hazards {
		// Infer tags from hazard category names
		switch h.Category {
		case "software", "steuerung", "steuerungsfehler":
			tagSet["has_software"] = true
			tagSet["programmable"] = true
		case "cyber", "cybersicherheit", "netzwerk":
			tagSet["is_networked"] = true
			tagSet["has_software"] = true
		case "ki", "kuenstliche_intelligenz", "ai_ml":
			tagSet["has_ai"] = true
			tagSet["has_software"] = true
		case "sensorik", "sensor":
			tagSet["sensor_part"] = true
		}
	}
	tags := make([]string, 0, len(tagSet))
	for t := range tagSet {
		tags = append(tags, t)
	}
	return tags
}

// severityRank maps severity strings to sort-order integers.
func severityRank(s string) int {
	switch s {
	case "high":
		return 3
	case "medium":
		return 2
	case "low":
		return 1
	default:
		return 0
	}
}

// buildSummaryFlags derives boolean flags from the collected trigger results.
func buildSummaryFlags(results []TriggerResult) map[string]bool {
	summary := map[string]bool{
		"dsfa_required":      false,
		"ai_act_relevant":    false,
		"cra_relevant":       false,
		"nis2_relevant":      false,
		"data_act_relevant":  false,
	}
	for _, r := range results {
		reg := r.Trigger.Regulation
		if len(reg) >= 4 && reg[:4] == "DSGV" {
			summary["dsfa_required"] = true
		}
		if len(reg) >= 6 && reg[:6] == "AI Act" {
			summary["ai_act_relevant"] = true
		}
		if len(reg) >= 3 && reg[:3] == "CRA" {
			summary["cra_relevant"] = true
		}
		if len(reg) >= 4 && reg[:4] == "NIS2" {
			summary["nis2_relevant"] = true
		}
		if len(reg) >= 11 && reg[:11] == "EU Data Act" {
			summary["data_act_relevant"] = true
		}
	}
	return summary
}
