package store

import (
	"sort"
	"sync"
	"time"

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

// LRU is a bounded, time-ordered, in-memory snapshot cache.
// It is safe for concurrent reads and writes.
type LRU struct {
	mu   sync.RWMutex
	cap  int
	data []types.Snapshot // ascending by SnapshotTs
}

// NewLRU creates an LRU with the given maximum size.
func NewLRU(cap int) *LRU { return &LRU{cap: cap} }

// Add inserts a snapshot and evicts the oldest if the cap is exceeded.
func (l *LRU) Add(s types.Snapshot) {
	l.mu.Lock()
	defer l.mu.Unlock()
	l.data = append(l.data, s)
	sort.SliceStable(l.data, func(i, j int) bool {
		return l.data[i].SnapshotTs.Before(l.data[j].SnapshotTs)
	})
	if len(l.data) > l.cap {
		// Drop oldest entries so len == cap.
		l.data = append([]types.Snapshot(nil), l.data[len(l.data)-l.cap:]...)
	}
}

// Len returns the number of snapshots held.
func (l *LRU) Len() int {
	l.mu.RLock()
	defer l.mu.RUnlock()
	return len(l.data)
}

// At returns the snapshot whose SnapshotTs equals ts, if any.
func (l *LRU) At(ts time.Time) (types.Snapshot, bool) {
	l.mu.RLock()
	defer l.mu.RUnlock()
	for _, s := range l.data {
		if s.SnapshotTs.Equal(ts) {
			return s, true
		}
	}
	return types.Snapshot{}, false
}

// FindClosest returns the snapshot in the given epoch whose SnapshotTs is
// nearest to target. Only same-epoch snapshots are considered.
// Returns ok=false if no same-epoch snapshot exists.
func (l *LRU) FindClosest(target time.Time, epoch int64) (types.Snapshot, bool) {
	l.mu.RLock()
	defer l.mu.RUnlock()
	var best types.Snapshot
	var bestDelta time.Duration
	found := false
	for _, s := range l.data {
		if s.EpochID != epoch {
			continue
		}
		d := s.SnapshotTs.Sub(target)
		if d < 0 {
			d = -d
		}
		if !found || d < bestDelta {
			bestDelta = d
			best = s
			found = true
		}
	}
	return best, found
}
