package scheduler

import (
	"testing"
	"time"
)

func TestParseCron(t *testing.T) {
	tests := []struct {
		expr    string
		wantErr bool
	}{
		{"* * * * *", false},
		{"0 * * * *", false},
		{"*/5 * * * *", false},
		{"0,30 * * * *", false},
		{"0-30 * * * *", false},
		{"*/15 9-17 * * *", false},
		{"0 0 1 * *", false},
		{"0 0 * * 0", false},
		{"0 0 1,15 * *", false},
		{"invalid", true},
		{"* * * *", true},
		{"60 * * * *", true},
		{"* 24 * * *", true},
	}

	for _, tt := range tests {
		_, err := ParseCron(tt.expr)
		if (err != nil) != tt.wantErr {
			t.Errorf("ParseCron(%q) error = %v, wantErr %v", tt.expr, err, tt.wantErr)
		}
	}
}

func TestCronMatches(t *testing.T) {
	expr, err := ParseCron("*/5 * * * *")
	if err != nil {
		t.Fatal(err)
	}

	tests := []struct {
		minute int
		want   bool
	}{
		{0, true},
		{5, true},
		{10, true},
		{15, true},
		{1, false},
		{2, false},
		{7, false},
	}

	now := time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC)
	for _, tt := range tests {
		tm := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), tt.minute, 0, 0, time.UTC)
		if got := expr.Matches(tm); got != tt.want {
			t.Errorf("Matches(minute=%d) = %v, want %v", tt.minute, got, tt.want)
		}
	}
}

func TestCronMatchesSpecific(t *testing.T) {
	expr, err := ParseCron("30 9 15 * *")
	if err != nil {
		t.Fatal(err)
	}

	matches := time.Date(2024, 6, 15, 9, 30, 0, 0, time.UTC)
	if !expr.Matches(matches) {
		t.Error("should match 9:30 on 15th of month")
	}

	notMatch := time.Date(2024, 6, 16, 9, 30, 0, 0, time.UTC)
	if expr.Matches(notMatch) {
		t.Error("should not match 9:30 on 16th of month")
	}
}

func TestNextRun(t *testing.T) {
	expr, err := ParseCron("*/5 * * * *")
	if err != nil {
		t.Fatal(err)
	}

	from := time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC)
	next := NextRun(expr, from)

	if next.Minute() != 5 || next.Hour() != 12 {
		t.Errorf("NextRun = %v, want 12:05", next)
	}

	if !next.After(from) {
		t.Error("NextRun should be after from time")
	}
}
