package iace

import (
	"fmt"
	"strings"
)

// Fine-Kinney risk model — BreakPilot's deterministic, citable risk backbone.
//
// Method: W.T. Fine, "Mathematical Evaluations for Controlling Hazards"
// (1971, US Naval Ordnance Laboratory report) and G.F. Kinney & A.D. Wiruth,
// "Practical Risk Analysis for Safety Management" (1976). Risk = P x E x C.
// It is a PUBLISHED, freely-usable method — not a copyrighted DIN/Beuth/ISO
// standard — and is widely used in industry (incl. CE-marking risk analysis).
//
// We derive SUGGESTED P/E/C values deterministically from PUBLIC, permissively
// licensed sources (Eurostat ESAW frequencies, NIOSH/OSHA/MIL-STD-882 injury
// outcomes — see DATA_SOURCES.md), each with a plain-language justification.
// The professional then ADJUSTS them (e.g. from his own licensed DIN/Beuth
// data) — the tool only supplies the formula and computes. We never reproduce
// norm tables.

// FKParam is a suggested Fine-Kinney parameter value plus why we chose it.
type FKParam struct {
	Value         float64 `json:"value"`
	Justification string  `json:"justification"`
}

// FKAssessment is a full suggested Fine-Kinney assessment for one hazard.
type FKAssessment struct {
	Probability FKParam `json:"probability"` // P: 0.1 .. 10
	Exposure    FKParam `json:"exposure"`    // E: 0.5 .. 10
	Consequence FKParam `json:"consequence"` // C: 1 .. 100
	Score       float64 `json:"score"`       // R = P * E * C
	Band        string  `json:"band"`        // Fine-Kinney risk band label
	Action      string  `json:"action"`      // suggested urgency of action
}

// fkProbabilityByMode maps a contact mode to a Fine-Kinney probability value,
// anchored to the ESAW relative frequency of that injury mechanism.
var fkProbabilityByMode = map[string]float64{
	"impact_stationary": 6, "crushing": 6, "struck_by": 6, "ergonomic": 6,
	"cutting": 3, "entanglement": 3, "shearing": 3, "electrical": 3,
	"thermal": 3, "fall": 3,
	"chemical": 1, "pressure_burst": 1, "radiation": 0.5,
}

// fkConsequenceFromSeverity maps our pattern-specific, de-biased severity (1-5)
// onto the published Fine-Kinney consequence scale. Using the per-hazard
// severity (not a coarse mode constant) preserves the ranking signal.
// 1=first aid, 3=disability, 7=serious injury, 15=a fatality, 40=multiple.
func fkConsequenceFromSeverity(s int) float64 {
	switch {
	case s >= 5:
		return 40
	case s == 4:
		return 15
	case s == 3:
		return 7
	case s == 2:
		return 3
	default:
		return 1
	}
}

// SuggestFineKinney builds a justified Fine-Kinney assessment from public-data
// anchors. Inputs are the hazard's categories, scenario text and the project's
// lifecycle phases. Values are SUGGESTIONS the professional adjusts.
func SuggestFineKinney(cats []string, scenario string, lifecyclePhases []string, defaultSeverity int) FKAssessment {
	mode := DetectContactMode(cats, scenario)

	p := 3.0
	if v, ok := fkProbabilityByMode[mode]; ok {
		p = v
	}
	s := EstimateSeverity(cats, scenario, defaultSeverity)
	c := fkConsequenceFromSeverity(s)
	e := fkExposure(lifecyclePhases)

	modeLabel := mode
	if modeLabel == "" {
		modeLabel = "unbestimmt"
	}
	a := FKAssessment{
		Probability: FKParam{p, "Eintrittswahrscheinlichkeit aus ESAW-Haeufigkeit der Kontaktart '" + modeLabel + "'"},
		Exposure:    FKParam{e.value, e.reason},
		Consequence: FKParam{c, fmt.Sprintf("Konsequenz aus Schwere-Einstufung S%d (NIOSH/OSHA/MIL-STD-882-Verletzungsbild)", s)},
	}
	a.Score, a.Band, a.Action = ComputeFineKinney(p, e.value, c)
	return a
}

type fkExp struct {
	value  float64
	reason string
}

// fkExposure maps the active lifecycle phases to a Fine-Kinney exposure value
// (how often a person is exposed to the task).
func fkExposure(phases []string) fkExp {
	has := func(needle string) bool {
		for _, p := range phases {
			if strings.Contains(p, needle) {
				return true
			}
		}
		return false
	}
	switch {
	case has("normal_operation") || has("auto_operation") || has("manual_operation"):
		return fkExp{6, "Exposition: Normalbetrieb (taeglich/dauernd)"}
	case has("setup") || has("maintenance") || has("cleaning") || has("changeover"):
		return fkExp{3, "Exposition: Einricht-/Wartungs-/Reinigungstaetigkeit (gelegentlich)"}
	case len(phases) > 0:
		return fkExp{1, "Exposition: seltene Lebensphase (wenige Male pro Jahr)"}
	default:
		return fkExp{3, "Exposition: angenommen gelegentlich (keine Lebensphase angegeben)"}
	}
}

// ComputeFineKinney returns the Fine-Kinney risk score (P*E*C) and the
// published risk band + suggested action urgency. The professional may pass
// his own adjusted P/E/C here (e.g. derived from his licensed DIN/Beuth data) —
// the tool only computes; it stores no norm table.
func ComputeFineKinney(p, e, c float64) (score float64, band, action string) {
	score = p * e * c
	switch {
	case score > 400:
		return score, "sehr hoch", "Taetigkeit einstellen / sofortige Massnahmen"
	case score > 200:
		return score, "hoch", "sofortige Sanierung erforderlich"
	case score > 70:
		return score, "wesentlich", "Sanierung erforderlich"
	case score > 20:
		return score, "moeglich", "Aufmerksamkeit, Massnahmen planen"
	default:
		return score, "gering", "ggf. akzeptabel, beobachten"
	}
}
