"""
Output abstraction layer for BaoLife.

Provides an interface for game output operations, allowing switching
between WebSocket (production) and collector (testing) output backends.
"""
import os
import json
from typing import Protocol, Optional, Any


class IGameOutput(Protocol):
    """
    Interface for game output operations.

    Defines the contract that all output implementations must follow.
    """

    async def send_event_message(self, event_obj):
        """Send an event message."""
        ...

    async def send_user_info(self, player):
        """Send player information update."""
        ...

    async def send_dict(self, obj):
        """Send a dictionary object."""
        ...


class WebSocketOutput:
    """
    WebSocket output implementation (production).

    Wraps existing WebSocket send functions for compatibility.
    """

    def __init__(self, websocket):
        """
        Initialize WebSocket output.

        Args:
            websocket: The WebSocket connection
        """
        self.websocket = websocket

    async def send_event_message(self, event_obj):
        """
        Send an event message via WebSocket.

        Args:
            event_obj: Event object to send
        """
        if self.websocket:
            from app import sendEventMessage
            await sendEventMessage(self.websocket, event_obj)

    async def send_user_info(self, player):
        """
        Send player information update via WebSocket.

        Args:
            player: Player object to send
        """
        if self.websocket:
            from app import sendUserInfo
            await sendUserInfo(player, self.websocket)

    async def send_dict(self, obj):
        """
        Send a dictionary object via WebSocket.

        Args:
            obj: Dictionary object to send
        """
        if self.websocket:
            from app import sendDict
            await sendDict(self.websocket, obj)

    def __repr__(self):
        return f"WebSocketOutput(websocket={self.websocket})"


class CollectorOutput:
    """
    Collector output implementation (testing).

    Collects all output instead of sending via WebSocket.
    This is a synchronous wrapper around MockGameOutput for testing.
    """

    def __init__(self):
        """Initialize collector output."""
        from tests.mocks.output_mock import MockGameOutput
        self.collector = MockGameOutput()

    async def send_event_message(self, event_obj):
        """
        Collect an event message.

        Args:
            event_obj: Event object to collect
        """
        self.collector.send_event_message(event_obj)

    async def send_user_info(self, player):
        """
        Collect a player information update.

        Args:
            player: Player object to collect
        """
        self.collector.send_user_info(player)

    async def send_dict(self, obj):
        """
        Collect a dictionary object.

        Args:
            obj: Dictionary object to collect
        """
        self.collector.send_dict(obj)

    def get_events(self):
        """Get collected events."""
        return self.collector.events

    def get_questions(self):
        """Get collected questions."""
        return self.collector.questions

    def get_player_updates(self):
        """Get collected player updates."""
        return self.collector.player_updates

    def clear(self):
        """Clear all collected output."""
        self.collector.clear()

    def __repr__(self):
        return f"CollectorOutput({self.collector})"


def output_factory(websocket=None, test_mode: Optional[bool] = None) -> IGameOutput:
    """
    Factory function to create appropriate output backend.

    Args:
        websocket: WebSocket connection (for production mode)
        test_mode: Force test mode (True) or production mode (False).
                  If None, uses BAOLIFE_TEST_MODE environment variable.

    Returns:
        Output implementation (WebSocketOutput or CollectorOutput)
    """
    # Determine test mode
    if test_mode is None:
        test_mode = os.environ.get('BAOLIFE_TEST_MODE', 'false').lower() == 'true'

    if test_mode or websocket is None:
        return CollectorOutput()
    else:
        return WebSocketOutput(websocket)


def get_output(websocket=None) -> IGameOutput:
    """
    Get the current output backend.

    Args:
        websocket: Optional WebSocket connection

    Returns:
        Output implementation
    """
    return output_factory(websocket)
