package notification

import (
	"testing"
	"time"
)

// TestRouteBackoffMonotonic locks the documented schedule:
// attempt 1 == ~backoffBase, each subsequent attempt grows by
// backoffFactor up to backoffMax. The check uses the lower bound of
// the jitter window so the assertion is robust under random output.
func TestRouteBackoffMonotonic(t *testing.T) {
	t.Parallel()
	lower := func(d time.Duration) time.Duration {
		return time.Duration(float64(d) * (1 - backoffJitter))
	}
	upper := func(d time.Duration) time.Duration {
		return time.Duration(float64(d) * (1 + backoffJitter))
	}

	cases := []struct {
		attempt int32
		want    time.Duration
	}{
		{attempt: 1, want: backoffBase},
		{attempt: 2, want: time.Duration(float64(backoffBase) * backoffFactor)},
		{attempt: 3, want: time.Duration(float64(backoffBase) * backoffFactor * backoffFactor)},
	}
	for _, tc := range cases {
		got := routeBackoff(tc.attempt)
		if got < lower(tc.want) || got > upper(tc.want) {
			t.Fatalf("attempt=%d got=%s want ~%s (±%.0f%%)", tc.attempt, got, tc.want, backoffJitter*100)
		}
	}
}

// TestRouteBackoffCap asserts the schedule clamps at backoffMax.
func TestRouteBackoffCap(t *testing.T) {
	t.Parallel()
	upper := time.Duration(float64(backoffMax) * (1 + backoffJitter))
	got := routeBackoff(50)
	if got > upper {
		t.Fatalf("attempt=50 got=%s exceeds cap (max=%s)", got, backoffMax)
	}
}
