package admin

import (
	"context"
	"testing"
	"time"

	"github.com/alicebob/miniredis/v2"
	"github.com/redis/go-redis/v9"
)

func newTestRedis(t *testing.T) (*redis.Client, *miniredis.Miniredis) {
	t.Helper()
	mr := miniredis.RunT(t)
	rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
	t.Cleanup(func() { rdb.Close() })
	return rdb, mr
}

func TestSessionStore_Lifecycle(t *testing.T) {
	rdb, _ := newTestRedis(t)
	store := NewSessionStore(rdb, 30*time.Minute)
	ctx := context.Background()

	sid, sess, err := store.Create(ctx, 42, "alice")
	if err != nil {
		t.Fatal(err)
	}
	if sess.AdminID != 42 || sess.Username != "alice" || sess.CSRFToken == "" {
		t.Fatalf("unexpected session %+v", sess)
	}

	got, err := store.Get(ctx, sid)
	if err != nil {
		t.Fatal(err)
	}
	if got == nil || got.AdminID != 42 {
		t.Fatalf("Get returned %+v", got)
	}

	// CSRF check.
	if !got.ValidCSRF(sess.CSRFToken) {
		t.Error("valid CSRF token rejected")
	}
	if got.ValidCSRF("bogus") {
		t.Error("bogus CSRF token accepted")
	}

	// Delete.
	if err := store.Delete(ctx, sid); err != nil {
		t.Fatal(err)
	}
	got, _ = store.Get(ctx, sid)
	if got != nil {
		t.Error("session still present after delete")
	}
}

func TestSessionStore_SlidingExpiry(t *testing.T) {
	rdb, mr := newTestRedis(t)
	store := NewSessionStore(rdb, 30*time.Minute)
	ctx := context.Background()

	sid, _, err := store.Create(ctx, 1, "bob")
	if err != nil {
		t.Fatal(err)
	}

	// Advance 20 min, access (slides TTL forward).
	mr.FastForward(20 * time.Minute)
	if got, _ := store.Get(ctx, sid); got == nil {
		t.Fatal("session expired prematurely")
	}
	// Another 20 min: still alive because the previous Get slid the TTL.
	mr.FastForward(20 * time.Minute)
	if got, _ := store.Get(ctx, sid); got == nil {
		t.Fatal("sliding TTL did not extend session")
	}
	// Idle past the full TTL: gone.
	mr.FastForward(31 * time.Minute)
	if got, _ := store.Get(ctx, sid); got != nil {
		t.Error("session survived past idle timeout")
	}
}
