package iace

import "testing"

func mkPM(id, cat, zone, scenario string, prio int, measures, opstates []string) PatternMatch {
	return PatternMatch{
		PatternID: id, PatternName: id, Priority: prio,
		HazardCats: []string{cat}, ZoneDE: zone, ScenarioDE: scenario,
		SuggestedMeasureIDs: measures, OperationalStates: opstates,
	}
}

func TestFindDedupCandidates_FindsOverlappingPair(t *testing.T) {
	fired := []PatternMatch{
		mkPM("HPa", "update_failure", "Steuerung, SPS", "Software-Update der Steuerung scheitert nach Abbruch", 80,
			[]string{"M138", "M146"}, nil),
		mkPM("HPb", "update_failure", "Steuerung, Antriebsregler", "Software-Update der Steuerung schlaegt fehl", 75,
			[]string{"M138", "M146", "M141"}, nil),
		mkPM("HPc", "mechanical_hazard", "Tuer", "Quetschen der Finger an der Tuer", 70,
			[]string{"M003"}, nil),
	}
	got := FindDedupCandidates(fired, 0.4)
	if len(got) != 1 {
		t.Fatalf("want 1 candidate, got %d: %+v", len(got), got)
	}
	// Higher-priority pattern survives, lower one is the drop target.
	if got[0].KeepPattern != "HPa" || got[0].DropPattern != "HPb" {
		t.Errorf("want keep HPa / drop HPb, got keep %s / drop %s", got[0].KeepPattern, got[0].DropPattern)
	}
	if got[0].DropName != "Software-Update der Steuerung schlaegt fehl" {
		t.Errorf("DropName must equal drop pattern ScenarioDE, got %q", got[0].DropName)
	}
}

func TestFindDedupCandidates_LifecycleGuard(t *testing.T) {
	// Same category, zone and measures — but normal-operation vs maintenance.
	// These are legitimate variants (HP011 vs HP077) and must NOT be proposed.
	fired := []PatternMatch{
		mkPM("HP011", "electrical_hazard", "Schaltschrank, Klemmenkasten", "Person beruehrt spannungsfuehrende Teile", 95,
			[]string{"M481", "M482"}, nil),
		mkPM("HP077", "electrical_hazard", "Schaltschrank, Klemmenkasten", "Person beruehrt spannungsfuehrende Teile", 80,
			[]string{"M481", "M482"}, []string{"maintenance"}),
	}
	if got := FindDedupCandidates(fired, 0.4); len(got) != 0 {
		t.Fatalf("lifecycle guard failed: want 0 candidates, got %d: %+v", len(got), got)
	}
}

func TestFindDedupCandidates_DifferentCategoryIgnored(t *testing.T) {
	fired := []PatternMatch{
		mkPM("HPa", "thermal_hazard", "Boiler", "Heisse Oberflaeche am Boiler", 80, []string{"M071"}, nil),
		mkPM("HPb", "mechanical_hazard", "Boiler", "Heisse Oberflaeche am Boiler", 80, []string{"M071"}, nil),
	}
	if got := FindDedupCandidates(fired, 0.3); len(got) != 0 {
		t.Fatalf("cross-category pair must not be proposed, got %d", len(got))
	}
}

func TestFindDedupCandidates_BelowThresholdDropped(t *testing.T) {
	fired := []PatternMatch{
		mkPM("HPa", "mechanical_hazard", "Tuer", "Quetschen an der Tuer", 80, []string{"M003"}, nil),
		mkPM("HPb", "mechanical_hazard", "Foerderband", "Einzug am Foerderband", 80, []string{"M540"}, nil),
	}
	if got := FindDedupCandidates(fired, 0.4); len(got) != 0 {
		t.Fatalf("disjoint pair must be below threshold, got %d: %+v", len(got), got)
	}
}
