package store

import (
	"testing"
	"time"

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

func TestLRU_AddAndBound(t *testing.T) {
	lru := NewLRU(3)
	for i := 0; i < 5; i++ {
		lru.Add(types.Snapshot{
			SnapshotTs: time.Unix(int64(i), 0),
			EpochID:    0,
		})
	}
	if lru.Len() != 3 {
		t.Errorf("len=%d want 3", lru.Len())
	}
	// Oldest two should be evicted
	if _, ok := lru.At(time.Unix(0, 0)); ok {
		t.Error("ts=0 should be evicted")
	}
	if _, ok := lru.At(time.Unix(4, 0)); !ok {
		t.Error("ts=4 should remain")
	}
}

func TestLRU_FindClosest_SameEpoch(t *testing.T) {
	lru := NewLRU(10)
	base := time.Date(2026, 4, 12, 9, 0, 0, 0, time.UTC)
	for i := 0; i < 10; i++ {
		lru.Add(types.Snapshot{
			SnapshotTs: base.Add(time.Duration(i) * 10 * time.Minute),
			EpochID:    1,
		})
	}
	// Looking for 1 hour before the latest entry (at base + 90m) → base + 30m
	target := base.Add(90 * time.Minute).Add(-1 * time.Hour)
	snap, ok := lru.FindClosest(target, 1)
	if !ok {
		t.Fatal("not found")
	}
	if !snap.SnapshotTs.Equal(base.Add(30 * time.Minute)) {
		t.Errorf("got %v want %v", snap.SnapshotTs, base.Add(30*time.Minute))
	}
}

func TestLRU_FindClosest_WrongEpoch(t *testing.T) {
	lru := NewLRU(10)
	base := time.Date(2026, 4, 12, 9, 0, 0, 0, time.UTC)
	lru.Add(types.Snapshot{SnapshotTs: base, EpochID: 5})
	if _, ok := lru.FindClosest(base, 6); ok {
		t.Error("different epoch must not match")
	}
}

func TestLRU_AddUnsortedInput(t *testing.T) {
	lru := NewLRU(10)
	// Add out of order
	lru.Add(types.Snapshot{SnapshotTs: time.Unix(20, 0), EpochID: 0})
	lru.Add(types.Snapshot{SnapshotTs: time.Unix(10, 0), EpochID: 0})
	lru.Add(types.Snapshot{SnapshotTs: time.Unix(30, 0), EpochID: 0})
	if lru.Len() != 3 {
		t.Errorf("len=%d", lru.Len())
	}
	// FindClosest should still find 10 when target is 9
	snap, ok := lru.FindClosest(time.Unix(9, 0), 0)
	if !ok || !snap.SnapshotTs.Equal(time.Unix(10, 0)) {
		t.Errorf("got %v ok=%v", snap.SnapshotTs, ok)
	}
}

func TestLRU_EmptyFindClosest(t *testing.T) {
	lru := NewLRU(10)
	if _, ok := lru.FindClosest(time.Now(), 0); ok {
		t.Error("empty LRU must return ok=false")
	}
}
