package iace

import "testing"

func TestRiskTrajectory_NoMeasures(t *testing.T) {
	engine := NewRiskEngine()
	steps := engine.CalculateRiskTrajectory(4, 4, 3, nil)
	if len(steps) != 1 {
		t.Fatalf("expected 1 step (inherent only), got %d", len(steps))
	}
	if steps[0].Stage != "inherent" {
		t.Errorf("expected stage 'inherent', got %q", steps[0].Stage)
	}
	if steps[0].RiskScore != 48 {
		t.Errorf("expected risk score 48 (4×4×3), got %.1f", steps[0].RiskScore)
	}
}

func TestRiskTrajectory_DesignMeasuresReduce(t *testing.T) {
	engine := NewRiskEngine()
	measures := []ProtectiveMeasureEntry{
		{ID: "M001", ReductionType: "design", RiskReduction: &RiskReduction{SeverityDelta: -1, ExposureDelta: -1}},
		{ID: "M012", ReductionType: "design", RiskReduction: &RiskReduction{SeverityDelta: -2}},
	}
	steps := engine.CalculateRiskTrajectory(4, 4, 3, measures)

	if len(steps) != 2 {
		t.Fatalf("expected 2 steps (inherent + after_design), got %d", len(steps))
	}

	// After design: S=4+(-1)+(-2)=1, E=4+(-1)=3, P=3 → 1×3×3 = 9
	after := steps[1]
	if after.Stage != "after_design" {
		t.Errorf("expected stage 'after_design', got %q", after.Stage)
	}
	if after.Severity != 1 {
		t.Errorf("expected severity 1, got %d", after.Severity)
	}
	if after.Exposure != 3 {
		t.Errorf("expected exposure 3, got %d", after.Exposure)
	}
	if after.RiskScore != 9 {
		t.Errorf("expected risk score 9, got %.1f", after.RiskScore)
	}
}

func TestRiskTrajectory_FullHierarchy(t *testing.T) {
	engine := NewRiskEngine()
	measures := []ProtectiveMeasureEntry{
		{ID: "M001", ReductionType: "design", RiskReduction: &RiskReduction{ExposureDelta: -1}},
		{ID: "M067", ReductionType: "protection", RiskReduction: &RiskReduction{ExposureDelta: -2, ProbabilityDelta: -1}},
		{ID: "M161", ReductionType: "information", RiskReduction: &RiskReduction{ProbabilityDelta: -1}},
	}
	// Start: S=4, E=4, P=3 → 48
	steps := engine.CalculateRiskTrajectory(4, 4, 3, measures)

	if len(steps) != 4 {
		t.Fatalf("expected 4 steps, got %d", len(steps))
	}

	// Inherent: 4×4×3 = 48
	if steps[0].RiskScore != 48 {
		t.Errorf("inherent: expected 48, got %.1f", steps[0].RiskScore)
	}
	// After design: S=4, E=3, P=3 → 36
	if steps[1].RiskScore != 36 {
		t.Errorf("after_design: expected 36, got %.1f", steps[1].RiskScore)
	}
	// After protection: S=4, E=1, P=2 → 8
	if steps[2].RiskScore != 8 {
		t.Errorf("after_protection: expected 8, got %.1f", steps[2].RiskScore)
	}
	// After information: S=4, E=1, P=1 → 4
	if steps[3].RiskScore != 4 {
		t.Errorf("after_information: expected 4, got %.1f", steps[3].RiskScore)
	}
	if !steps[3].IsAcceptable {
		t.Error("final risk 4 should be acceptable")
	}
}

func TestRiskTrajectory_ClampMinimum1(t *testing.T) {
	engine := NewRiskEngine()
	// Very aggressive reduction that would push below 1
	measures := []ProtectiveMeasureEntry{
		{ID: "M001", ReductionType: "design", RiskReduction: &RiskReduction{SeverityDelta: -5, ExposureDelta: -5, ProbabilityDelta: -5}},
	}
	steps := engine.CalculateRiskTrajectory(3, 3, 3, measures)

	after := steps[1]
	if after.Severity != 1 || after.Exposure != 1 || after.Probability != 1 {
		t.Errorf("expected all clamped to 1, got S=%d E=%d P=%d", after.Severity, after.Exposure, after.Probability)
	}
	if after.RiskScore != 1 {
		t.Errorf("expected minimum risk score 1, got %.1f", after.RiskScore)
	}
}

func TestRiskTrajectory_OnlyProtectionMeasures(t *testing.T) {
	engine := NewRiskEngine()
	// No design measures, only protection
	measures := []ProtectiveMeasureEntry{
		{ID: "M067", ReductionType: "protection", RiskReduction: &RiskReduction{ExposureDelta: -2}},
	}
	steps := engine.CalculateRiskTrajectory(4, 4, 3, measures)

	// Should have 2 steps: inherent + after_protection (no after_design)
	if len(steps) != 2 {
		t.Fatalf("expected 2 steps, got %d", len(steps))
	}
	if steps[1].Stage != "after_protection" {
		t.Errorf("expected stage after_protection, got %q", steps[1].Stage)
	}
	// S=4, E=2, P=3 → 24
	if steps[1].RiskScore != 24 {
		t.Errorf("expected 24, got %.1f", steps[1].RiskScore)
	}
}

func TestRiskTrajectory_MeasuresWithoutRiskReduction(t *testing.T) {
	engine := NewRiskEngine()
	// Measures without RiskReduction should be skipped (no delta)
	measures := []ProtectiveMeasureEntry{
		{ID: "M151", ReductionType: "information", Name: "Betriebsanleitung"},
	}
	steps := engine.CalculateRiskTrajectory(3, 3, 3, measures)

	if len(steps) != 1 {
		t.Fatalf("expected 1 step (measures without RiskReduction have no effect), got %d", len(steps))
	}
}

func TestRiskTrajectory_MandatoryMeasuresAsProtective(t *testing.T) {
	engine := NewRiskEngine()
	// MN measures have ReductionType "protective" — should be grouped with "protection"
	measures := []ProtectiveMeasureEntry{
		{ID: "MN001", ReductionType: "protective", RiskReduction: &RiskReduction{ProbabilityDelta: -1}},
	}
	steps := engine.CalculateRiskTrajectory(4, 4, 3, measures)

	if len(steps) != 2 {
		t.Fatalf("expected 2 steps, got %d", len(steps))
	}
	if steps[1].Stage != "after_protection" {
		t.Errorf("expected after_protection for 'protective' type, got %q", steps[1].Stage)
	}
}

func TestRiskReduction_OnMeasureLibrary(t *testing.T) {
	// Verify that the library has measures with RiskReduction profiles
	measures := GetProtectiveMeasureLibrary()
	withReduction := 0
	for _, m := range measures {
		if m.RiskReduction != nil {
			withReduction++
		}
	}
	if withReduction < 80 {
		t.Errorf("expected at least 80 measures with RiskReduction profiles, got %d", withReduction)
	}
}
