package iace

// SILPLResult contains the deterministic SIL/PL recommendation
// derived from the risk assessment S×E×P values, plus expert override fields.
type SILPLResult struct {
	RecommendedPL  string       `json:"recommended_pl"`  // "a", "b", "c", "d", "e"
	RecommendedSIL string       `json:"recommended_sil"` // "none", "SIL 1", "SIL 2", "SIL 3"
	RiskGraphInput RiskGraphIn  `json:"risk_graph_input"`
	// Expert override fields (filled by Fachmann)
	OverriddenPL    string   `json:"overridden_pl,omitempty"`
	OverriddenSIL   string   `json:"overridden_sil,omitempty"`
	OverrideReason  string   `json:"override_reason,omitempty"`
	NormReferences  []string `json:"norm_references,omitempty"`
	ExpertValidated bool     `json:"expert_validated"`
	ValidatedBy     string   `json:"validated_by,omitempty"`
	ValidatedAt     string   `json:"validated_at,omitempty"`
}

// RiskGraphIn shows the mapped ISO risk graph factors.
type RiskGraphIn struct {
	S string `json:"s"` // "S1" or "S2"
	F string `json:"f"` // "F1" or "F2"
	P string `json:"p"` // "P1" or "P2"
}

// CalculateSILPL derives Performance Level (PL) and Safety Integrity Level (SIL)
// from the IACE risk assessment factors (Severity 1-5, Exposure 1-5, Probability 1-5).
//
// This is a deterministic mapping based on the general risk graph principle:
//   - Severity ≥ 4 → S2 (serious/fatal), else S1 (minor/reversible)
//   - Exposure ≥ 3 → F2 (frequent/continuous), else F1 (rare/short)
//   - Probability ≥ 4 → P2 (hardly avoidable), else P1 (avoidable)
//
// NO normative text is reproduced. This is our own implementation.
func CalculateSILPL(severity, exposure, probability int) SILPLResult {
	s, f, p := mapToRiskGraph(severity, exposure, probability)
	pl := calculatePL(s, f, p)
	sil := plToSIL(pl)

	return SILPLResult{
		RecommendedPL:  pl,
		RecommendedSIL: sil,
		RiskGraphInput: RiskGraphIn{S: s, F: f, P: p},
	}
}

// mapToRiskGraph converts 1-5 scale factors to binary S1/S2, F1/F2, P1/P2.
func mapToRiskGraph(severity, exposure, probability int) (string, string, string) {
	s := "S1"
	if severity >= 4 {
		s = "S2"
	}
	f := "F1"
	if exposure >= 3 {
		f = "F2"
	}
	p := "P1"
	if probability >= 4 {
		p = "P2"
	}
	return s, f, p
}

// calculatePL determines Performance Level from the risk graph.
//
// Risk graph (own formulation, no norm text):
//
//	S1+F1+P1 ��� a (lowest)
//	S1+F1+P2 → b
//	S1+F2+P1 → b
//	S1+F2+P2 → c
//	S2+F1+P1 → c
//	S2+F1+P2 → d
//	S2+F2+P1 → d
//	S2+F2+P2 → e (highest)
func calculatePL(s, f, p string) string {
	if s == "S1" {
		if f == "F1" {
			if p == "P1" {
				return "a"
			}
			return "b"
		}
		// F2
		if p == "P1" {
			return "b"
		}
		return "c"
	}
	// S2
	if f == "F1" {
		if p == "P1" {
			return "c"
		}
		return "d"
	}
	// S2+F2
	if p == "P1" {
		return "d"
	}
	return "e"
}

// plToSIL maps Performance Level to Safety Integrity Level.
//
//	PL a, b → no SIL requirement
//	PL c    → SIL 1
//	PL d    → SIL 2
//	PL e    → SIL 3
func plToSIL(pl string) string {
	switch pl {
	case "a", "b":
		return "none"
	case "c":
		return "SIL 1"
	case "d":
		return "SIL 2"
	case "e":
		return "SIL 3"
	default:
		return "none"
	}
}

// GetEffectivePL returns the expert-overridden PL if set, otherwise the recommendation.
func (r *SILPLResult) GetEffectivePL() string {
	if r.OverriddenPL != "" {
		return r.OverriddenPL
	}
	return r.RecommendedPL
}

// GetEffectiveSIL returns the expert-overridden SIL if set, otherwise the recommendation.
func (r *SILPLResult) GetEffectiveSIL() string {
	if r.OverriddenSIL != "" {
		return r.OverriddenSIL
	}
	return r.RecommendedSIL
}
