package iace

// Interlocked-enclosure model (EN ISO 14120 / EN ISO 12100).
//
// A contact or entanglement hazard from a moving part is removed during NORMAL
// operation when that part is inaccessible behind an interlocked guard. The
// hazard then remains only when the guard is open — maintenance, cleaning or
// fault clearing. Patterns flagged GuardableByEnclosure express this; a project
// emits the "interlocked_enclosure" tag (interlocked door/hood, see
// keyword_dictionary.go) to declare the guard.
//
// This is GENERIC: it applies to every enclosed machine (dishwasher spray arm,
// enclosed mixer, centrifuge ...) and is regression-safe — machines that do not
// emit interlocked_enclosure are unaffected.

const (
	phaseMaintenance   = "maintenance"
	phaseCleaning      = "cleaning"
	phaseFaultClearing = "fault_clearing"
)

// suppressedByEnclosure reports whether a guardable hazard must be dropped: the
// part is enclosed AND none of the project's lifecycle phases opens the guard.
func suppressedByEnclosure(p HazardPattern, tagSet map[string]bool, lifecycles []string) bool {
	if !p.GuardableByEnclosure || !tagSet["interlocked_enclosure"] || len(lifecycles) == 0 {
		return false
	}
	for _, lc := range lifecycles {
		if lc == phaseMaintenance || lc == phaseCleaning || lc == phaseFaultClearing {
			return false // guard is open in some phase → hazard remains there
		}
	}
	return true
}

// guardedLifecycles re-scopes a guardable hazard to the guard-open phases when
// the project declares an interlocked enclosure, so it is documented as a
// maintenance/cleaning hazard rather than a normal-operation one.
func guardedLifecycles(p HazardPattern, tagSet map[string]bool) []string {
	if p.GuardableByEnclosure && tagSet["interlocked_enclosure"] {
		return []string{phaseMaintenance, phaseCleaning}
	}
	return p.ApplicableLifecycles
}

// Domain-specific supersession.
//
// A generic pattern that fires via a broad tag (e.g. high_temperature) can
// duplicate a domain-specific pattern that describes the same hazard more
// precisely. When the domain is present, the specific pattern wins and the
// generic duplicate is dropped. Scoped to the domain tag, so machines outside
// the domain keep the generic pattern — regression-safe by construction.
//
//	HP016 (generic hot surfaces)  -> HP2201 (Boiler/Tank/Spuelkammer)
//	HP018 (actuator burn)         -> HP2201 (same contact-burn hazard)
//	HP013 (stored electrical NRG) -> HP144  (residual voltage; HP013's zone is
//	                                 framed for Batteriefaecher/USV-Anlagen a
//	                                 dishwasher does not have, HP144 is the
//	                                 Frequenzumrichter/Zwischenkreis variant)
var genericSupersededByWarewashing = map[string]bool{
	"HP016": true,
	"HP018": true,
	"HP013": true,
}

// supersededByDomainSpecific reports whether a generic pattern is replaced by a
// more precise equivalent that the project's domain already provides.
func supersededByDomainSpecific(p HazardPattern, tagSet map[string]bool) bool {
	return tagSet["dom_warewashing"] && genericSupersededByWarewashing[p.ID]
}
