package domain

import (
	"errors"
	"testing"
	"time"
)

func TestBandFromScore_LockedThresholds(t *testing.T) {
	cases := []struct {
		score int
		want  string
	}{
		{80, BandHigh},
		{100, BandHigh},
		{79, BandMid},
		{50, BandMid},
		{49, BandLow},
		{0, BandLow},
		{-1, BandLow},
	}
	for _, tc := range cases {
		if got := BandFromScore(tc.score); got != tc.want {
			t.Fatalf("BandFromScore(%d)=%q want %q", tc.score, got, tc.want)
		}
	}
}

func TestUnknownPublishedTimeIsNotStale(t *testing.T) {
	if IsStaleHardReject(0, NowNano()) {
		t.Fatal("an unknown source timestamp must not be rejected as stale")
	}
	if FreshnessScore(FreshnessHoursSince(0, NowNano())) != 0 {
		t.Fatal("an unknown source timestamp must receive no freshness credit")
	}
}

func TestTwentyDayOldPostIsNotHardRejected(t *testing.T) {
	now := NowNano()
	posted := now - 20*24*int64(time.Hour)
	if IsStaleHardReject(posted, now) {
		t.Fatal("a 20-day-old demand post must still be judgeable, not hard-rejected")
	}
	old := now - 40*24*int64(time.Hour)
	if !IsStaleHardReject(old, now) {
		t.Fatal("a 40-day-old post should still be hard-rejected")
	}
}

func TestValidateReasons_RequiresAllFive(t *testing.T) {
	full := fiveReasons()
	if err := ValidateReasons(full); err != nil {
		t.Fatalf("full reasons should pass: %v", err)
	}

	// Drop fit — OP-06.
	missing := full[:4]
	err := ValidateReasons(missing)
	if err == nil {
		t.Fatal("expected error when one dimension is missing")
	}
	if !errors.Is(err, ErrValidation) {
		t.Fatalf("want ErrValidation, got %v", err)
	}

	// Blank reason text.
	blank := fiveReasons()
	blank[0].Reason = "   "
	if err := ValidateReasons(blank); err == nil {
		t.Fatal("expected error for blank reason text")
	}

	if err := ValidateReasons(nil); err == nil {
		t.Fatal("expected error for nil reasons")
	}
}

func TestCanTransitionOpportunity(t *testing.T) {
	allow := [][2]string{
		{OppJudging, OppQualified},
		{OppJudging, OppRejected},
		{OppQualified, OppAccepted},
		{OppQualified, OppDismissed},
		{OppRejected, OppQualified},
	}
	for _, p := range allow {
		if !CanTransitionOpportunity(p[0], p[1]) {
			t.Fatalf("expected allow %s → %s", p[0], p[1])
		}
	}
	deny := [][2]string{
		{OppAccepted, OppQualified},
		{OppDismissed, OppQualified},
		{OppQualified, OppJudging},
		{OppRejected, OppAccepted},
	}
	for _, p := range deny {
		if CanTransitionOpportunity(p[0], p[1]) {
			t.Fatalf("expected deny %s → %s", p[0], p[1])
		}
	}
}

func fiveReasons() []OpportunityReason {
	return []OpportunityReason{
		{Dimension: DimAuthenticity, Score: 25, Reason: "真的在求推薦"},
		{Dimension: DimIntent, Score: 28, Reason: "有明確購買意圖"},
		{Dimension: DimRegion, Score: 15, Reason: "地區相符"},
		{Dimension: DimFreshness, Score: 12, Reason: "24 小時內"},
		{Dimension: DimFit, Score: 8, Reason: "對上婚攝服務"},
	}
}
