package epoch

import (
	"sort"
	"time"

	"github.com/nexuscrm/sentry/internal/config"
)

// Tracker computes epoch IDs and suppression windows based on a daily UTC
// reset schedule. An empty schedule means "no reset" — epoch stays at 0
// forever and no window is suppressed. Safe for concurrent reads.
type Tracker struct {
	schedule  []config.ResetTime // sorted ascending
	reference time.Time
	window    time.Duration
}

// New constructs a Tracker. The input schedule does not need to be sorted.
func New(schedule []config.ResetTime, reference time.Time, window time.Duration) *Tracker {
	sorted := append([]config.ResetTime(nil), schedule...)
	sort.Slice(sorted, func(i, j int) bool {
		return sorted[i].Hour*60+sorted[i].Minute < sorted[j].Hour*60+sorted[j].Minute
	})
	return &Tracker{schedule: sorted, reference: reference, window: window}
}

// EpochAt returns the epoch ID for the given instant.
// Epoch 0 is the interval before the first reset on or after reference.
// Each reset boundary increments the epoch by 1.
func (t *Tracker) EpochAt(at time.Time) int64 {
	if len(t.schedule) == 0 {
		return 0
	}
	if at.Before(t.reference) {
		return 0
	}
	day := t.reference.Truncate(24 * time.Hour)
	var count int64
	for !day.After(at) {
		for _, rt := range t.schedule {
			boundary := time.Date(day.Year(), day.Month(), day.Day(), rt.Hour, rt.Minute, 0, 0, time.UTC)
			if boundary.Before(t.reference) {
				continue
			}
			if !boundary.After(at) {
				count++
			}
		}
		day = day.Add(24 * time.Hour)
	}
	return count
}

// InSuppressionWindow reports whether `at` falls within [boundary, boundary+window)
// of any reset in the schedule. Returns false for an empty schedule.
func (t *Tracker) InSuppressionWindow(at time.Time) bool {
	if len(t.schedule) == 0 {
		return false
	}
	day := at.Truncate(24 * time.Hour)
	for _, rt := range t.schedule {
		boundary := time.Date(day.Year(), day.Month(), day.Day(), rt.Hour, rt.Minute, 0, 0, time.UTC)
		if !at.Before(boundary) && at.Before(boundary.Add(t.window)) {
			return true
		}
	}
	return false
}
