package controllogic

import (
	"testing"
	"time"
)

func mustSched(t *testing.T, spec string) *Schedule {
	t.Helper()
	s, err := ParseSchedule(spec)
	if err != nil {
		t.Fatalf("ParseSchedule(%q): %v", spec, err)
	}
	return s
}

func TestCronEveryMinute(t *testing.T) {
	s := mustSched(t, "* * * * *")
	if !s.Match(time.Date(2026, 6, 18, 12, 34, 0, 0, time.UTC)) {
		t.Error("* * * * * should match any time")
	}
}

func TestCronSpecificTime(t *testing.T) {
	s := mustSched(t, "30 9 * * *")
	if !s.Match(time.Date(2026, 6, 18, 9, 30, 0, 0, time.UTC)) {
		t.Error("should match 09:30")
	}
	if s.Match(time.Date(2026, 6, 18, 9, 31, 0, 0, time.UTC)) {
		t.Error("should not match 09:31")
	}
}

func TestCronStep(t *testing.T) {
	s := mustSched(t, "*/15 * * * *")
	for _, m := range []int{0, 15, 30, 45} {
		if !s.Match(time.Date(2026, 6, 18, 1, m, 0, 0, time.UTC)) {
			t.Errorf("*/15 should match minute %d", m)
		}
	}
	if s.Match(time.Date(2026, 6, 18, 1, 7, 0, 0, time.UTC)) {
		t.Error("*/15 should not match minute 7")
	}
}

func TestCronRangeAndList(t *testing.T) {
	s := mustSched(t, "0 9-17 * * 1,2,3,4,5")
	// 2026-06-18 is a Thursday (weekday 4).
	if !s.Match(time.Date(2026, 6, 18, 10, 0, 0, 0, time.UTC)) {
		t.Error("should match Thursday 10:00")
	}
	if s.Match(time.Date(2026, 6, 18, 18, 0, 0, 0, time.UTC)) {
		t.Error("should not match 18:00 (out of 9-17)")
	}
	// 2026-06-20 is a Saturday (weekday 6).
	if s.Match(time.Date(2026, 6, 20, 10, 0, 0, 0, time.UTC)) {
		t.Error("should not match Saturday")
	}
}

func TestCronDOWSunday7(t *testing.T) {
	s := mustSched(t, "0 0 * * 7")
	// 2026-06-21 is a Sunday.
	if !s.Match(time.Date(2026, 6, 21, 0, 0, 0, 0, time.UTC)) {
		t.Error("7 should mean Sunday")
	}
}

func TestCronDomOrDow(t *testing.T) {
	// When both day fields restricted, match either (Vixie semantics).
	s := mustSched(t, "0 0 1 * 5")
	// 2026-06-01 is a Monday — matches via day-of-month=1.
	if !s.Match(time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC)) {
		t.Error("should match day-of-month 1")
	}
	// 2026-06-19 is a Friday (weekday 5) — matches via day-of-week.
	if !s.Match(time.Date(2026, 6, 19, 0, 0, 0, 0, time.UTC)) {
		t.Error("should match Friday")
	}
	// 2026-06-18 Thursday, not the 1st — no match.
	if s.Match(time.Date(2026, 6, 18, 0, 0, 0, 0, time.UTC)) {
		t.Error("should not match Thursday the 18th")
	}
}

func TestCronInvalid(t *testing.T) {
	for _, bad := range []string{"* * * *", "60 * * * *", "* 24 * * *", "* * 0 * *", "a * * * *", "*/0 * * * *"} {
		if _, err := ParseSchedule(bad); err == nil {
			t.Errorf("ParseSchedule(%q) should error", bad)
		}
	}
}
