import pytest
import sys
import os
import asyncio

# Add parent directory to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from player_cache import PlayerCache

# Mock classes to avoid DB connection
class MockPerson:
    def __init__(self):
        self.firstname = ""
        self.lastname = ""

class MockPlayer:
    def __init__(self):
        self.id = ""
        self.c = MockPerson()
        self.connection = 'disconnected'
        self.events = []

def test_player_cache_add_and_get():
    """Test adding and retrieving players"""
    cache = PlayerCache(max_size=3)

    player1 = MockPlayer()
    player1.id = "user1"
    player1.c.firstname = "Player1"

    cache.set("user1", player1)

    retrieved = cache.get("user1")
    assert retrieved is not None
    assert retrieved.id == "user1"
    assert retrieved.c.firstname == "Player1"

def test_player_cache_lru_eviction():
    """Test that LRU player is evicted when cache is full"""
    cache = PlayerCache(max_size=3, auto_save=False)

    # Add 3 players
    for i in range(1, 4):
        player = MockPlayer()
        player.id = f"user{i}"
        cache.set(f"user{i}", player)

    assert cache.size() == 3

    # Access user2 and user3 (making user1 least recently used)
    cache.get("user2")
    cache.get("user3")

    # Add user4 - should evict user1
    player4 = MockPlayer()
    player4.id = "user4"
    cache.set("user4", player4)

    assert cache.size() == 3
    assert cache.get("user1") is None  # Evicted
    assert cache.get("user2") is not None
    assert cache.get("user3") is not None
    assert cache.get("user4") is not None

@pytest.mark.asyncio
async def test_player_cache_save_on_eviction():
    """Test that evicted players are saved to database"""
    cache = PlayerCache(max_size=2, auto_save=False)  # Disable auto_save to avoid DB import

    saved_players = []

    # Mock save function
    async def mock_save(player):
        saved_players.append(player.id)

    cache.on_evict = mock_save

    # Fill cache
    for i in range(1, 4):
        player = MockPlayer()
        player.id = f"user{i}"
        player.connection = 'disconnected'  # Disconnected players can be evicted
        cache.set(f"user{i}", player)

    # user1 should have been evicted and callback called
    await asyncio.sleep(0.1)  # Let async callback complete

    assert "user1" in saved_players

def test_connected_players_not_evicted():
    """Test that connected players are never evicted"""
    cache = PlayerCache(max_size=2, auto_save=False)

    # Add connected player
    player1 = MockPlayer()
    player1.id = "user1"
    player1.connection = 'connected'
    cache.set("user1", player1)

    # Add disconnected player
    player2 = MockPlayer()
    player2.id = "user2"
    player2.connection = 'disconnected'
    cache.set("user2", player2)

    # Try to add third player - should evict user2, not user1
    player3 = MockPlayer()
    player3.id = "user3"
    player3.connection = 'disconnected'
    cache.set("user3", player3)

    assert cache.get("user1") is not None  # Connected, not evicted
    assert cache.get("user2") is None  # Disconnected, evicted
    assert cache.get("user3") is not None

def test_memory_estimation():
    """Test memory usage estimation"""
    cache = PlayerCache(max_size=100)

    for i in range(10):
        player = MockPlayer()
        player.id = f"user{i}"
        # Add some data
        player.events = [f"event{j}" for j in range(100)]
        cache.set(f"user{i}", player)

    memory_mb = cache.estimate_memory_mb()
    assert memory_mb > 0
    assert memory_mb < 50  # Should be reasonable for 10 players
