package iace

import (
	"sort"
	"strings"
	"testing"
)

// producibleTagUniverse returns every component/energy tag that some machine
// could ever carry: tags from the component library, the energy library, the
// keyword dictionary's ExtraTags, and the domain-gate tags. A pattern whose
// RequiredComponentTags/RequiredEnergyTags include a tag outside this set can
// never match — no machine can produce that tag.
func producibleTagUniverse() map[string]bool {
	u := make(map[string]bool)
	for _, c := range GetComponentLibrary() {
		for _, t := range c.Tags {
			u[t] = true
		}
	}
	for _, e := range GetEnergySources() {
		for _, t := range e.Tags {
			u[t] = true
		}
	}
	for _, k := range GetKeywordDictionary() {
		for _, t := range k.ExtraTags {
			u[t] = true
		}
	}
	// Domain-gate tags are produced from narrative domain terms.
	for _, t := range []string{
		"dom_agri", "dom_cnc", "dom_escalator", "dom_glass", "dom_grinding",
		"dom_plastics", "dom_press", "dom_rolling", "dom_solar", "dom_textile",
		"dom_welding", "dom_wind",
	} {
		u[t] = true
	}
	return u
}

// TestPatternReachability reports patterns that can never fire because they
// require a component/energy tag that nothing in the libraries produces. Every
// pattern should be usable in SOME CE risk assessment. Currently informational
// (t.Log) so we can review the list before deciding to prune.
func TestPatternReachability(t *testing.T) {
	universe := producibleTagUniverse()
	patterns := AllPatterns()

	type dead struct {
		id, name string
		missing  []string
	}
	var deads []dead
	missingTagCount := make(map[string]int)

	for _, p := range patterns {
		var missing []string
		for _, tag := range append(append([]string{}, p.RequiredComponentTags...), p.RequiredEnergyTags...) {
			if !universe[tag] {
				missing = append(missing, tag)
				missingTagCount[tag]++
			}
		}
		if len(missing) > 0 {
			deads = append(deads, dead{p.ID, p.NameDE, missing})
		}
	}

	t.Logf("Patterns gesamt: %d", len(patterns))
	t.Logf("Unerreichbare (tote) Patterns: %d", len(deads))

	// Most common unsatisfiable tags first — these point at the systemic gaps.
	type kv struct {
		tag string
		n   int
	}
	var ranked []kv
	for tag, n := range missingTagCount {
		ranked = append(ranked, kv{tag, n})
	}
	sort.Slice(ranked, func(i, j int) bool { return ranked[i].n > ranked[j].n })
	t.Log("--- Unerfuellbare Required-Tags (Haeufigkeit) ---")
	for _, r := range ranked {
		t.Logf("  %3d  %s", r.n, r.tag)
	}

	t.Log("--- Tote Patterns (erste 60) ---")
	for i, d := range deads {
		if i >= 60 {
			break
		}
		t.Logf("  %s  %q  fehlend: %s", d.id, d.name, strings.Join(d.missing, ","))
	}

	// Guard: every pattern must be reachable by some CE risk assessment. A
	// pattern requiring a tag no component/energy/keyword can ever produce is
	// dead weight (and often a tag-naming typo). Keep this at zero.
	if len(deads) > 0 {
		t.Errorf("%d unreachable pattern(s) — required tags that nothing produces: %v",
			len(deads), missingTagCount)
	}
}
