package scheduler

import (
	"testing"
	"time"
)

func TestParseCron(t *testing.T) {
	tests := []struct {
		expr    string
		wantErr bool
	}{
		{"0 8 * * *", false},       // 每天8点
		{"*/10 * * * *", false},    // 每10分钟
		{"0 9 * * 1", false},       // 每周一9点
		{"30 14 1 * *", false},     // 每月1日14:30
		{"0,30 * * * *", false},    // 每小时0分和30分
		{"0 9-17 * * 1-5", false},  // 工作日9-17点每小时
		{"bad", true},              // 无效
		{"* * *", true},            // 字段不足
		{"60 * * * *", true},       // 分钟超范围
		{"* 25 * * *", 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 TestCronMatch(t *testing.T) {
	// 2026-03-07 是周六（weekday=6）
	base := time.Date(2026, 3, 7, 8, 0, 0, 0, time.Local)

	tests := []struct {
		expr string
		time time.Time
		want bool
	}{
		// 每天8:00
		{"0 8 * * *", base, true},
		{"0 8 * * *", base.Add(1 * time.Minute), false}, // 8:01 不匹配
		{"0 8 * * *", base.Add(-1 * time.Hour), false},  // 7:00 不匹配

		// 每10分钟
		{"*/10 * * * *", time.Date(2026, 3, 7, 8, 0, 0, 0, time.Local), true},
		{"*/10 * * * *", time.Date(2026, 3, 7, 8, 10, 0, 0, time.Local), true},
		{"*/10 * * * *", time.Date(2026, 3, 7, 8, 20, 0, 0, time.Local), true},
		{"*/10 * * * *", time.Date(2026, 3, 7, 8, 5, 0, 0, time.Local), false},

		// 每周一9:00（周六不匹配）
		{"0 9 * * 1", base, false},
		{"0 9 * * 1", time.Date(2026, 3, 9, 9, 0, 0, 0, time.Local), true}, // 周一

		// 每周六8:00
		{"0 8 * * 6", base, true},

		// 多值
		{"0,30 * * * *", time.Date(2026, 3, 7, 8, 0, 0, 0, time.Local), true},
		{"0,30 * * * *", time.Date(2026, 3, 7, 8, 30, 0, 0, time.Local), true},
		{"0,30 * * * *", time.Date(2026, 3, 7, 8, 15, 0, 0, time.Local), false},

		// 范围
		{"0 9-17 * * *", time.Date(2026, 3, 7, 9, 0, 0, 0, time.Local), true},
		{"0 9-17 * * *", time.Date(2026, 3, 7, 17, 0, 0, 0, time.Local), true},
		{"0 9-17 * * *", time.Date(2026, 3, 7, 8, 0, 0, 0, time.Local), false},
		{"0 9-17 * * *", time.Date(2026, 3, 7, 18, 0, 0, 0, time.Local), false},

		// 步进范围
		{"0 9-17/2 * * *", time.Date(2026, 3, 7, 9, 0, 0, 0, time.Local), true},
		{"0 9-17/2 * * *", time.Date(2026, 3, 7, 11, 0, 0, 0, time.Local), true},
		{"0 9-17/2 * * *", time.Date(2026, 3, 7, 10, 0, 0, 0, time.Local), false},
	}

	for _, tt := range tests {
		expr, err := ParseCron(tt.expr)
		if err != nil {
			t.Fatalf("ParseCron(%q) 解析失败: %v", tt.expr, err)
		}
		got := expr.Match(tt.time)
		if got != tt.want {
			t.Errorf("CronExpr(%q).Match(%s) = %v, want %v",
				tt.expr, tt.time.Format("2006-01-02 15:04 Mon"), got, tt.want)
		}
	}
}

func TestMatchOnce(t *testing.T) {
	now := time.Date(2026, 3, 7, 14, 30, 0, 0, time.Local)

	tests := []struct {
		expr string
		want bool
	}{
		{"2026-03-07 14:30", true},
		{"2026-03-07 14:31", false},
		{"2026-03-08 14:30", false},
		{"bad-format", false},
	}

	for _, tt := range tests {
		got := matchOnce(tt.expr, now)
		if got != tt.want {
			t.Errorf("matchOnce(%q, %s) = %v, want %v", tt.expr, now.Format("2006-01-02 15:04"), got, tt.want)
		}
	}
}

func TestParseField(t *testing.T) {
	tests := []struct {
		field   string
		min     int
		max     int
		want    []int
		wantNil bool
	}{
		{"*", 0, 59, nil, true},
		{"5", 0, 59, []int{5}, false},
		{"1,3,5", 0, 59, []int{1, 3, 5}, false},
		{"1-5", 0, 59, []int{1, 2, 3, 4, 5}, false},
		{"*/15", 0, 59, []int{0, 15, 30, 45}, false},
		{"10-20/3", 0, 59, []int{10, 13, 16, 19}, false},
	}

	for _, tt := range tests {
		got, err := parseField(tt.field, tt.min, tt.max)
		if err != nil {
			t.Errorf("parseField(%q) 失败: %v", tt.field, err)
			continue
		}
		if tt.wantNil {
			if got != nil {
				t.Errorf("parseField(%q) = %v, want nil", tt.field, got)
			}
			continue
		}
		if len(got) != len(tt.want) {
			t.Errorf("parseField(%q) = %v, want %v", tt.field, got, tt.want)
			continue
		}
		for i := range got {
			if got[i] != tt.want[i] {
				t.Errorf("parseField(%q)[%d] = %d, want %d", tt.field, i, got[i], tt.want[i])
			}
		}
	}
}
