package format

import (
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"testing"
)

// Boundary tests derived from the BACKEND's grading.py (the source of truth
// for tier names/thresholds — see the cross-repo contract in CLAUDE.md).
// Each threshold is asserted AT the boundary, not just around it, because the
// side each comparison is inclusive on is exactly what an off-by-one breaks:
// the top tier is strict (pct > 99), every other bound is lower-INCLUSIVE.
//
// Note: the Python frontend itself carried no band table (labels/classes
// arrive pre-computed from the content API); these are ported for the Go
// side's display parity per the same contract the backend enforces.

func TestTempBandBoundaries(t *testing.T) {
	cases := []struct {
		pct       float64
		wantLabel string
		wantClass string
	}{
		{99.01, "Near Record", "rec-hot"}, // strictly above 99 only
		{99, "Very High", "very-hot"},     // AT 99: NOT Near Record (top tier is strict)
		{90, "Very High", "very-hot"},     // lower-inclusive
		{89.99, "High", "hot"},
		{75, "High", "hot"},
		{74.99, "Above Normal", "warm"},
		{60, "Above Normal", "warm"},
		{59.99, "Normal", "normal"},
		{40, "Normal", "normal"},
		{39.99, "Below Normal", "cool"},
		{25, "Below Normal", "cool"},
		{24.99, "Low", "cold"},
		{10, "Low", "cold"},
		{9.99, "Very Low", "very-cold"},
		{1, "Very Low", "very-cold"},
		{0.99, "Near Record", "rec-cold"}, // strictly below 1
		{0, "Near Record", "rec-cold"},
		{-1, "Near Record", "rec-cold"}, // out-of-range falls to the bottom tier
		{100, "Near Record", "rec-hot"},
	}
	for _, c := range cases {
		label, class := BandFor(c.pct, TempBands)
		if label != c.wantLabel || class != c.wantClass {
			t.Errorf("BandFor(%v, TempBands) = (%q, %q), want (%q, %q)",
				c.pct, label, class, c.wantLabel, c.wantClass)
		}
	}
}

func TestRainBandBoundaries(t *testing.T) {
	cases := []struct {
		pct       float64
		wantLabel string
		wantClass string
	}{
		{99.01, "Extreme", "wet-9"}, // strictly above 99 only
		{99, "Severe", "wet-8"},     // AT 99: NOT Extreme
		{95, "Severe", "wet-8"},
		{94.99, "Very Heavy", "wet-7"},
		{90, "Very Heavy", "wet-7"},
		{89.99, "Heavy", "wet-6"},
		{60, "Heavy", "wet-6"},
		{59.99, "Typical", "wet-5"},
		{40, "Typical", "wet-5"},
		{39.99, "Brisk", "wet-4"},
		{25, "Brisk", "wet-4"},
		{24.99, "Light", "wet-3"},
		{10, "Light", "wet-3"},
		{9.99, "Trace", "wet-2"},
		{0, "Trace", "wet-2"},
		{100, "Extreme", "wet-9"},
	}
	for _, c := range cases {
		label, class := BandFor(c.pct, RainBands)
		if label != c.wantLabel || class != c.wantClass {
			t.Errorf("BandFor(%v, RainBands) = (%q, %q), want (%q, %q)",
				c.pct, label, class, c.wantLabel, c.wantClass)
		}
	}
}

// parseBackendBands extracts a band table from backend/data/grading.py by
// name, so the Go copies cannot silently drift from the source of truth.
func parseBackendBands(t *testing.T, src []byte, name string) []Band {
	t.Helper()
	block := regexp.MustCompile(`(?s)` + name + `\s*=\s*\[(.*?)\]`).FindSubmatch(src)
	if block == nil {
		t.Fatalf("could not find %s in grading.py", name)
	}
	rows := regexp.MustCompile(`\(\s*(\d+)\s*,\s*"([^"]+)"\s*,\s*"([^"]+)"\s*\)`).FindAllSubmatch(block[1], -1)
	var out []Band
	for _, r := range rows {
		thr, err := strconv.ParseFloat(string(r[1]), 64)
		if err != nil {
			t.Fatalf("bad threshold in %s: %v", name, err)
		}
		out = append(out, Band{Threshold: thr, Label: string(r[2]), Class: string(r[3])})
	}
	if len(out) == 0 {
		t.Fatalf("no bands parsed for %s", name)
	}
	return out
}

// TestBandsMatchBackend re-parses grading.py and asserts thresholds, labels,
// classes AND ordering are identical. Skips when the monorepo backend
// checkout isn't present (the tables above remain the committed contract).
func TestBandsMatchBackend(t *testing.T) {
	path := filepath.Join("..", "..", "..", "..", "backend", "data", "grading.py")
	raw, err := os.ReadFile(path)
	if err != nil {
		t.Skipf("backend checkout not present (%v) — parity asserted only against the committed copy", err)
	}
	for _, tc := range []struct {
		name string
		ours []Band
	}{
		{"TEMP_BANDS", TempBands},
		{"RAIN_BANDS", RainBands},
	} {
		theirs := parseBackendBands(t, raw, tc.name)
		if len(theirs) != len(tc.ours) {
			t.Errorf("%s: backend has %d tiers, ours has %d", tc.name, len(theirs), len(tc.ours))
			continue
		}
		for i := range theirs {
			if theirs[i] != tc.ours[i] {
				t.Errorf("%s[%d]: backend %+v != ours %+v", tc.name, i, theirs[i], tc.ours[i])
			}
		}
	}
}
