package cronutil_test

import (
	"testing"
	"time"

	"galaxy/cronutil"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestParseAcceptsFiveFieldExpressions(t *testing.T) {
	t.Parallel()

	accepted := []string{
		"0 18 * * *",
		"*/15 * * * *",
		"0 */6 * * *",
		"0 */4 * * *",
		"0 0 * * *",
		"0 0 1 1 *",
		"30 9 * * MON-FRI",
	}
	for _, expr := range accepted {
		t.Run(expr, func(t *testing.T) {
			t.Parallel()

			_, err := cronutil.Parse(expr)
			require.NoError(t, err)
		})
	}
}

func TestParseRejectsInvalidExpressions(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name string
		expr string
	}{
		{name: "empty", expr: ""},
		{name: "whitespace only", expr: "   "},
		{name: "garbage", expr: "not a cron"},
		{name: "six field with seconds", expr: "*/30 * * * * *"},
		{name: "too few fields", expr: "0 18 *"},
		{name: "minute out of range", expr: "60 * * * *"},
		{name: "hour out of range", expr: "* 24 * * *"},
		{name: "month out of range", expr: "* * * 13 *"},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			_, err := cronutil.Parse(tc.expr)
			require.Error(t, err)
		})
	}
}

func TestScheduleNextComputesExpectedFire(t *testing.T) {
	t.Parallel()

	schedule, err := cronutil.Parse("0 18 * * *")
	require.NoError(t, err)

	after := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
	next := schedule.Next(after)

	assert.Equal(t, time.Date(2026, 1, 1, 18, 0, 0, 0, time.UTC), next)
	assert.Equal(t, time.UTC, next.Location())
}

func TestScheduleNextStepsToNextFifteenMinuteSlot(t *testing.T) {
	t.Parallel()

	schedule, err := cronutil.Parse("*/15 * * * *")
	require.NoError(t, err)

	after := time.Date(2026, 6, 30, 12, 7, 33, 0, time.UTC)
	next := schedule.Next(after)

	assert.Equal(t, time.Date(2026, 6, 30, 12, 15, 0, 0, time.UTC), next)
	assert.Equal(t, time.UTC, next.Location())
}

func TestScheduleNextReturnsUTCForNonUTCInput(t *testing.T) {
	t.Parallel()

	schedule, err := cronutil.Parse("0 18 * * *")
	require.NoError(t, err)

	moscow, err := time.LoadLocation("Europe/Moscow")
	require.NoError(t, err)

	after := time.Date(2026, 1, 1, 12, 0, 0, 0, moscow) // 09:00 UTC
	next := schedule.Next(after)

	assert.Equal(t, time.Date(2026, 1, 1, 18, 0, 0, 0, time.UTC), next)
	assert.Equal(t, time.UTC, next.Location())
}
