package iace

import "testing"

func TestFailureModeLibrary_Count(t *testing.T) {
	fms := GetFailureModeLibrary()
	if len(fms) < 140 {
		t.Errorf("expected at least 140 failure modes, got %d", len(fms))
	}
	t.Logf("Total failure modes: %d", len(fms))
}

func TestFailureModeLibrary_UniqueIDs(t *testing.T) {
	seen := make(map[string]bool)
	for _, fm := range GetFailureModeLibrary() {
		if seen[fm.ID] {
			t.Errorf("duplicate failure mode ID: %s", fm.ID)
		}
		seen[fm.ID] = true
	}
}

func TestFailureModeLibrary_ValidComponentTypes(t *testing.T) {
	validTypes := map[string]bool{
		"sensor": true, "controller": true, "actuator": true,
		"mechanical": true, "electrical": true, "software": true,
		"hydraulic": true, "pneumatic": true, "safety_device": true,
		"network": true, "ai_model": true,
	}
	for _, fm := range GetFailureModeLibrary() {
		if !validTypes[fm.ComponentType] {
			t.Errorf("FM %s has invalid ComponentType: %s", fm.ID, fm.ComponentType)
		}
	}
}

func TestFailureModeLibrary_NonEmptyFields(t *testing.T) {
	for _, fm := range GetFailureModeLibrary() {
		if fm.NameDE == "" {
			t.Errorf("FM %s has empty NameDE", fm.ID)
		}
		if fm.Effect == "" {
			t.Errorf("FM %s has empty Effect", fm.ID)
		}
		if fm.DefaultSeverity < 1 || fm.DefaultSeverity > 10 {
			t.Errorf("FM %s has invalid Severity: %d", fm.ID, fm.DefaultSeverity)
		}
		if fm.DefaultOccurrence < 1 || fm.DefaultOccurrence > 10 {
			t.Errorf("FM %s has invalid Occurrence: %d", fm.ID, fm.DefaultOccurrence)
		}
		if fm.DefaultDetection < 1 || fm.DefaultDetection > 10 {
			t.Errorf("FM %s has invalid Detection: %d", fm.ID, fm.DefaultDetection)
		}
	}
}

func TestFailureModeLibrary_ComponentTypeDistribution(t *testing.T) {
	counts := make(map[string]int)
	for _, fm := range GetFailureModeLibrary() {
		counts[fm.ComponentType]++
	}
	for ct, n := range counts {
		t.Logf("  %s: %d failure modes", ct, n)
	}
	if len(counts) < 8 {
		t.Errorf("expected at least 8 component types, got %d", len(counts))
	}
}

func TestRPZ_Calculation(t *testing.T) {
	fm := FailureModeEntry{DefaultSeverity: 8, DefaultOccurrence: 3, DefaultDetection: 4}
	rpz := fm.CalculateRPZ()
	if rpz != 96 {
		t.Errorf("expected RPZ 96 (8*3*4), got %d", rpz)
	}
}

func TestRPZ_Maximum(t *testing.T) {
	fm := FailureModeEntry{DefaultSeverity: 10, DefaultOccurrence: 10, DefaultDetection: 10}
	rpz := fm.CalculateRPZ()
	if rpz != 1000 {
		t.Errorf("expected RPZ 1000 (10*10*10), got %d", rpz)
	}
}

func TestRPZ_Threshold(t *testing.T) {
	below := FailureModeEntry{DefaultSeverity: 5, DefaultOccurrence: 2, DefaultDetection: 3}
	above := FailureModeEntry{DefaultSeverity: 8, DefaultOccurrence: 4, DefaultDetection: 4}

	if below.CalculateRPZ() >= RPZThresholdAction {
		t.Error("RPZ 30 should be below threshold 100")
	}
	if above.CalculateRPZ() < RPZThresholdAction {
		t.Errorf("RPZ %d should be above threshold 100", above.CalculateRPZ())
	}
}

func TestPatternEngine_FailureMode_NilFiresAlways(t *testing.T) {
	engine := NewPatternEngine()
	// Without FailureModes in input — all patterns should fire normally
	result := engine.Match(MatchInput{
		ComponentLibraryIDs: []string{"C001"},
		EnergySourceIDs:     []string{"EN01"},
	})
	if len(result.MatchedPatterns) == 0 {
		t.Fatal("expected patterns without failure mode filter")
	}
}

func TestFailureModeLibrary_RPZDistribution(t *testing.T) {
	actionRequired := 0
	critical := 0
	for _, fm := range GetFailureModeLibrary() {
		rpz := fm.CalculateRPZ()
		if rpz >= RPZThresholdAction {
			actionRequired++
		}
		if rpz >= 200 {
			critical++
		}
	}
	t.Logf("RPZ distribution: %d action required (>=100), %d critical (>=200), of %d total",
		actionRequired, critical, len(GetFailureModeLibrary()))
}
