package infra

import (
	"context"
	"testing"
	"time"
)

func allMinutes() []int {
	s := make([]int, 60)
	for i := range s {
		s[i] = i
	}
	return s
}

func allHours() []int {
	s := make([]int, 24)
	for i := range s {
		s[i] = i
	}
	return s
}

func allDays() []int {
	s := make([]int, 31)
	for i := range s {
		s[i] = i + 1
	}
	return s
}

func allMonths() []int {
	s := make([]int, 12)
	for i := range s {
		s[i] = i + 1
	}
	return s
}

func allDOW() []int {
	s := make([]int, 7)
	for i := range s {
		s[i] = i
	}
	return s
}

func TestCronTicker(t *testing.T) {
	t.Run("context cancel cierra el channel", func(t *testing.T) {
		sched := CronTickerSchedule{
			Minute:     allMinutes(),
			Hour:       allHours(),
			DayOfMonth: allDays(),
			Month:      allMonths(),
			DayOfWeek:  allDOW(),
		}
		ctx, cancel := context.WithCancel(context.Background())
		ch := CronTicker(sched, ctx)

		// Cancel immediately.
		cancel()

		// Channel should close without blocking.
		timeout := time.After(2 * time.Second)
		select {
		case _, ok := <-ch:
			if ok {
				// Might receive one tick before cancel propagates — acceptable.
			}
			// Drain remaining.
			for range ch {
			}
		case <-timeout:
			t.Error("channel did not close within 2s after context cancel")
		}
	})

	t.Run("ticker emite al llegar el momento del schedule", func(t *testing.T) {
		// Use a schedule that fires every minute (all minutes).
		// The next tick is at most 60s away. We use a short-lived context
		// to avoid waiting: instead we verify the channel is not nil and
		// that cancellation closes it cleanly.
		sched := CronTickerSchedule{
			Minute:     allMinutes(),
			Hour:       allHours(),
			DayOfMonth: allDays(),
			Month:      allMonths(),
			DayOfWeek:  allDOW(),
		}
		ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
		defer cancel()

		ch := CronTicker(sched, ctx)
		if ch == nil {
			t.Fatal("CronTicker returned nil channel")
		}

		// Wait for context to expire, then confirm channel closes.
		<-ctx.Done()
		timeout := time.After(2 * time.Second)
		for {
			select {
			case _, ok := <-ch:
				if !ok {
					return // channel closed, test passes
				}
			case <-timeout:
				t.Error("channel did not close within 2s after context timeout")
				return
			}
		}
	})
}
