package lobby

import (
	"testing"
	"time"

	"github.com/google/uuid"
)

func TestCachePutGetRemoveGame(t *testing.T) {
	c := NewCache()
	g := GameRecord{
		GameID:    uuid.New(),
		Status:    GameStatusEnrollmentOpen,
		GameName:  "Test Game",
		CreatedAt: time.Now(),
	}
	if _, ok := c.GetGame(g.GameID); ok {
		t.Fatalf("GetGame on empty cache returned ok=true")
	}
	c.PutGame(g)
	got, ok := c.GetGame(g.GameID)
	if !ok || got.GameID != g.GameID {
		t.Fatalf("GetGame after PutGame: ok=%v, got=%v", ok, got)
	}
	c.RemoveGame(g.GameID)
	if _, ok := c.GetGame(g.GameID); ok {
		t.Fatalf("GetGame after RemoveGame: ok=true")
	}
}

func TestCachePutGameEvictsOnTerminalStatus(t *testing.T) {
	c := NewCache()
	g := GameRecord{
		GameID:   uuid.New(),
		Status:   GameStatusEnrollmentOpen,
		GameName: "Test Game",
	}
	c.PutGame(g)
	if _, ok := c.GetGame(g.GameID); !ok {
		t.Fatalf("PutGame did not insert")
	}
	g.Status = GameStatusFinished
	c.PutGame(g)
	if _, ok := c.GetGame(g.GameID); ok {
		t.Fatalf("PutGame with finished did not evict")
	}
}

func TestCachePutMembershipEvictsOnNonActive(t *testing.T) {
	c := NewCache()
	gameID := uuid.New()
	c.PutGame(GameRecord{GameID: gameID, Status: GameStatusEnrollmentOpen})
	m := Membership{
		MembershipID: uuid.New(),
		GameID:       gameID,
		UserID:       uuid.New(),
		Status:       MembershipStatusActive,
	}
	c.PutMembership(m)
	if got := c.MembershipsForGame(gameID); len(got) != 1 {
		t.Fatalf("MembershipsForGame after add = %d, want 1", len(got))
	}
	m.Status = MembershipStatusRemoved
	c.PutMembership(m)
	if got := c.MembershipsForGame(gameID); len(got) != 0 {
		t.Fatalf("MembershipsForGame after remove = %d, want 0", len(got))
	}
}

func TestCachePutRaceNameAndEvict(t *testing.T) {
	c := NewCache()
	owner := uuid.New()
	entry := RaceNameEntry{
		Name:        "Andromeda",
		Canonical:   CanonicalKey("andromeda"),
		Status:      RaceNameStatusReservation,
		OwnerUserID: owner,
		GameID:      uuid.New(),
	}
	c.PutRaceName(entry)
	got, ok := c.GetRaceName(entry.Canonical)
	if !ok || got.Canonical != entry.Canonical {
		t.Fatalf("GetRaceName: ok=%v, got=%v", ok, got)
	}
	c.EvictUserRaceNames(owner)
	if _, ok := c.GetRaceName(entry.Canonical); ok {
		t.Fatalf("EvictUserRaceNames did not evict")
	}
}

func TestCacheReadyDefaultsFalse(t *testing.T) {
	c := NewCache()
	if c.Ready() {
		t.Fatalf("Ready() before Warm = true, want false")
	}
}

func TestCacheSizesZero(t *testing.T) {
	c := NewCache()
	games, members, raceNames := c.Sizes()
	if games != 0 || members != 0 || raceNames != 0 {
		t.Fatalf("Sizes() on empty = (%d,%d,%d), want (0,0,0)", games, members, raceNames)
	}
}

func TestCacheEvictOwnerGames(t *testing.T) {
	c := NewCache()
	owner := uuid.New()
	otherOwner := uuid.New()
	owned := GameRecord{GameID: uuid.New(), Status: GameStatusEnrollmentOpen, OwnerUserID: &owner}
	other := GameRecord{GameID: uuid.New(), Status: GameStatusEnrollmentOpen, OwnerUserID: &otherOwner}
	c.PutGame(owned)
	c.PutGame(other)
	c.EvictOwnerGames(owner)
	if _, ok := c.GetGame(owned.GameID); ok {
		t.Fatalf("EvictOwnerGames did not evict owned game")
	}
	if _, ok := c.GetGame(other.GameID); !ok {
		t.Fatalf("EvictOwnerGames evicted unrelated game")
	}
}
