"""
Mock storage implementations for testing.

This module provides in-memory storage implementations that comply with
the IGameStorage interface, allowing tests to run without database connections.
"""
from typing import Dict, Optional, List, Tuple, Any
import copy
import pickle


class InMemoryStorage:
    """
    In-memory game storage implementation.

    This class implements the IGameStorage interface using a simple
    dictionary to store player data in memory. All data is lost when
    the instance is destroyed.
    """

    def __init__(self):
        """Initialize in-memory storage."""
        self._games: Dict[str, Any] = {}  # userID -> player object
        self.save_count = 0
        self.load_count = 0

    def save_game(self, player) -> bool:
        """
        Save a game to memory.

        Args:
            player: The player object to save

        Returns:
            bool: True if successful, False otherwise
        """
        try:
            # Store deep copy to avoid reference issues
            # Use userID if available, otherwise generate one
            user_id = player.userID if hasattr(player, 'userID') else str(id(player))
            self._games[user_id] = copy.deepcopy(player)
            self.save_count += 1
            return True
        except Exception as e:
            print(f"Storage save error: {e}")
            return False

    def load_game(self, user_id: str):
        """
        Load a game from memory.

        Args:
            user_id: The user ID to load

        Returns:
            playerClass: Player object if found, None otherwise
        """
        self.load_count += 1
        if user_id not in self._games:
            return None
        # Return deep copy to avoid reference issues
        return copy.deepcopy(self._games[user_id])

    def load_games(self) -> List[Tuple]:
        """
        Load all game user IDs.

        Returns:
            List[Tuple]: List of (user_id,) tuples
        """
        return [(user_id,) for user_id in self._games.keys()]

    def game_exists(self, user_id: str) -> bool:
        """
        Check if a game exists.

        Args:
            user_id: User ID to check

        Returns:
            bool: True if game exists, False otherwise
        """
        return user_id in self._games

    # Legacy methods for backwards compatibility
    def save(self, player):
        """Legacy save method"""
        return self.save_game(player)

    def load(self, user_id: str):
        """Legacy load method"""
        return self.load_game(user_id)

    def exists(self, user_id: str) -> bool:
        """Legacy exists method"""
        return self.game_exists(user_id)

    def delete(self, user_id: str) -> bool:
        """Delete saved game"""
        if user_id in self._games:
            del self._games[user_id]
            return True
        return False

    def clear_all(self):
        """Clear all saved games"""
        self._games.clear()
        self.save_count = 0
        self.load_count = 0

    def get_stats(self) -> Dict:
        """
        Get storage statistics.

        Returns:
            Dict: Dictionary with storage stats
        """
        return {
            'game_count': len(self._games),
            'save_count': self.save_count,
            'load_count': self.load_count,
            'user_ids': list(self._games.keys())
        }

    def __repr__(self):
        return f"InMemoryStorage(games={len(self._games)})"


class MockStorage:
    """
    Mock storage with controllable behavior.

    This class extends InMemoryStorage with additional features for testing,
    such as simulating failures and tracking method calls.
    """

    def __init__(self):
        """Initialize mock storage."""
        self._storage = InMemoryStorage()
        self._should_fail_save = False
        self._should_fail_load = False
        self._save_delay = 0
        self._load_delay = 0
        self.method_calls = []

    def save_game(self, player) -> bool:
        """
        Save a game with controllable behavior.

        Args:
            player: The player object to save

        Returns:
            bool: True if successful (unless configured to fail)
        """
        user_id = player.userID if hasattr(player, 'userID') else str(id(player))
        self.method_calls.append(('save_game', user_id))

        if self._should_fail_save:
            return False

        if self._save_delay > 0:
            import time
            time.sleep(self._save_delay)

        return self._storage.save_game(player)

    def load_game(self, user_id: str):
        """
        Load a game with controllable behavior.

        Args:
            user_id: The user ID to load

        Returns:
            playerClass: Player object if found (unless configured to fail)
        """
        self.method_calls.append(('load_game', user_id))

        if self._should_fail_load:
            return None

        if self._load_delay > 0:
            import time
            time.sleep(self._load_delay)

        return self._storage.load_game(user_id)

    def load_games(self) -> List[Tuple]:
        """Load all game user IDs."""
        self.method_calls.append(('load_games',))
        return self._storage.load_games()

    def game_exists(self, user_id: str) -> bool:
        """Check if a game exists."""
        self.method_calls.append(('game_exists', user_id))
        return self._storage.game_exists(user_id)

    def delete(self, user_id: str) -> bool:
        """Delete a saved game."""
        self.method_calls.append(('delete', user_id))
        return self._storage.delete(user_id)

    def clear_all(self):
        """Clear all saved games and reset state."""
        self._storage.clear_all()
        self.method_calls.clear()
        self._should_fail_save = False
        self._should_fail_load = False

    def simulate_save_failure(self):
        """Configure storage to fail on next save."""
        self._should_fail_save = True

    def simulate_load_failure(self):
        """Configure storage to fail on next load."""
        self._should_fail_load = True

    def set_save_delay(self, seconds: float):
        """
        Set artificial delay for save operations.

        Args:
            seconds: Delay in seconds
        """
        self._save_delay = seconds

    def set_load_delay(self, seconds: float):
        """
        Set artificial delay for load operations.

        Args:
            seconds: Delay in seconds
        """
        self._load_delay = seconds

    def get_stats(self) -> Dict:
        """Get storage statistics."""
        stats = self._storage.get_stats()
        stats['method_calls'] = len(self.method_calls)
        return stats

    def assert_saved(self, user_id: str):
        """
        Assert that a save was attempted for the given user.

        Args:
            user_id: User ID to check

        Raises:
            AssertionError: If no save was attempted
        """
        saves = [call for call in self.method_calls if call[0] == 'save_game' and call[1] == user_id]
        assert len(saves) > 0, f"No save attempted for user {user_id}"

    def assert_loaded(self, user_id: str):
        """
        Assert that a load was attempted for the given user.

        Args:
            user_id: User ID to check

        Raises:
            AssertionError: If no load was attempted
        """
        loads = [call for call in self.method_calls if call[0] == 'load_game' and call[1] == user_id]
        assert len(loads) > 0, f"No load attempted for user {user_id}"

    def __repr__(self):
        return f"MockStorage(games={len(self._storage._games)}, calls={len(self.method_calls)})"
