import aiohttp
from opengsq.protocol_base import ProtocolBase
from opengsq.protocol_socket import UdpClient
from opengsq.responses.eldewrito.status import Status, Player
import logging


class ElDewrito(ProtocolBase):
    """ElDewrito Protocol Implementation"""

    ELDEWRITO_BROADCAST_PORT = 11774
    ELDEWRITO_HTTP_PORT = 11775

    # ElDewrito broadcast query payload
    BROADCAST_QUERY = bytes(
        [
            0x01,
            0x62,
            0x6C,
            0x61,
            0x6D,
            0x00,
            0x00,
            0x00,
            0x09,
            0x81,
            0x00,
            0x02,
            0x00,
            0x01,
            0x2D,
            0xC3,
            0x04,
            0x93,
            0xDC,
            0x05,
            0xD9,
            0x95,
            0x40,
        ]
    )

    @property
    def full_name(self) -> str:
        return "ElDewrito Protocol"

    def __init__(
        self, host: str, port: int = ELDEWRITO_BROADCAST_PORT, timeout: float = 5.0
    ):
        super().__init__(host, port, timeout)
        self._allow_broadcast = True
        self.logger = logging.getLogger(f"{__name__}.ElDewrito")

    async def get_status(self) -> Status:
        """
        Get server status using ElDewrito's two-step discovery process:
        1. Send broadcast query to port 11774
        2. Get HTTP response from port 11775
        """
        # Step 1: Send broadcast query and wait for response
        try:
            data = await UdpClient.communicate(
                self, self.BROADCAST_QUERY, source_port=self.ELDEWRITO_BROADCAST_PORT
            )

            # Step 2: Validate response (must be > 120 bytes from port 11774)
            if not self._is_valid_broadcast_response(data):
                raise Exception("Invalid broadcast response")

            # Step 3: Query HTTP endpoint for detailed server info
            server_info = await self._query_http_endpoint()

            # Step 4: Parse and return status
            return self._parse_server_info(server_info)

        except Exception as e:
            self.logger.error(f"Error getting ElDewrito server status: {e}")
            raise

    def _is_valid_broadcast_response(self, data: bytes) -> bool:
        """
        Validate ElDewrito broadcast response.
        Response should be > 120 bytes and from port 11774.
        """
        return len(data) > 120

    async def _query_http_endpoint(self) -> dict:
        """
        Query the ElDewrito HTTP endpoint on port 11775 for server information.
        """
        url = f"http://{self._host}:{self.ELDEWRITO_HTTP_PORT}/"

        try:
            async with aiohttp.ClientSession(
                timeout=aiohttp.ClientTimeout(total=self._timeout)
            ) as session:
                async with session.get(url) as response:
                    if response.status == 200:
                        return await response.json()
                    else:
                        raise Exception(
                            f"HTTP request failed with status {response.status}"
                        )

        except Exception as e:
            self.logger.error(f"Error querying HTTP endpoint: {e}")
            raise

    def _parse_server_info(self, server_info: dict) -> Status:
        """
        Parse the JSON response from ElDewrito HTTP endpoint into Status object.
        """
        try:
            # Parse players list
            players = []
            for player_data in server_info.get("players", []):
                player = Player(
                    name=player_data.get("name", ""),
                    uid=player_data.get("uid", ""),
                    team=player_data.get("team", 0),
                    score=player_data.get("score", 0),
                    kills=player_data.get("kills", 0),
                    assists=player_data.get("assists", 0),
                    deaths=player_data.get("deaths", 0),
                    betrayals=player_data.get("betrayals", 0),
                    time_spent_alive=player_data.get("timeSpentAlive", 0),
                    suicides=player_data.get("suicides", 0),
                    best_streak=player_data.get("bestStreak", 0),
                )
                players.append(player)

            # Create Status object
            status = Status(
                name=server_info.get("name", "Unknown ElDewrito Server"),
                port=server_info.get("port", self.ELDEWRITO_BROADCAST_PORT),
                file_server_port=server_info.get("fileServerPort", 11778),
                host_player=server_info.get("hostPlayer", ""),
                sprint_state=server_info.get("sprintState", "2"),
                sprint_unlimited_enabled=server_info.get("sprintUnlimitedEnabled", "0"),
                dual_wielding=server_info.get("dualWielding", "1"),
                assassination_enabled=server_info.get("assassinationEnabled", "0"),
                vote_system_type=server_info.get("voteSystemType", 0),
                teams=server_info.get("teams", False),
                map=server_info.get("map", "Unknown Map"),
                map_file=server_info.get("mapFile", ""),
                variant=server_info.get("variant", "none"),
                variant_type=server_info.get("variantType", "none"),
                status=server_info.get("status", "Unknown"),
                num_players=server_info.get("numPlayers", 0),
                max_players=server_info.get("maxPlayers", 16),
                mod_count=server_info.get("modCount", 0),
                mod_package_name=server_info.get("modPackageName", ""),
                mod_package_author=server_info.get("modPackageAuthor", ""),
                mod_package_hash=server_info.get("modPackageHash", ""),
                mod_package_version=server_info.get("modPackageVersion", ""),
                xnkid=server_info.get("xnkid", ""),
                xnaddr=server_info.get("xnaddr", ""),
                players=players,
                is_dedicated=server_info.get("isDedicated", True),
                game_version=server_info.get("gameVersion", "Unknown"),
                eldewrito_version=server_info.get("eldewritoVersion", "Unknown"),
            )

            return status

        except Exception as e:
            self.logger.error(f"Error parsing server info: {e}")
            raise Exception(f"Failed to parse ElDewrito server info: {e}")

    async def discover_servers(
        self, broadcast_address: str = "255.255.255.255"
    ) -> list:
        """
        Discover ElDewrito servers using broadcast query.
        This method can be used for network discovery.
        """
        discovered_servers = []

        try:
            # Create a temporary instance for broadcast
            broadcast_client = ElDewrito(
                broadcast_address, self.ELDEWRITO_BROADCAST_PORT, self._timeout
            )

            # Send broadcast query - use regular communicate method
            data = await UdpClient.communicate(
                broadcast_client,
                self.BROADCAST_QUERY,
                source_port=self.ELDEWRITO_BROADCAST_PORT,
            )

            # Process response if valid
            if self._is_valid_broadcast_response(data):
                try:
                    # Create client for specific server
                    server_client = ElDewrito(
                        broadcast_address, self.ELDEWRITO_BROADCAST_PORT, self._timeout
                    )
                    status = await server_client.get_status()
                    discovered_servers.append(
                        ((broadcast_address, self.ELDEWRITO_BROADCAST_PORT), status)
                    )
                except Exception as e:
                    self.logger.debug(
                        f"Failed to get status from {broadcast_address}:{self.ELDEWRITO_BROADCAST_PORT}: {e}"
                    )

        except Exception as e:
            self.logger.error(f"Error during server discovery: {e}")

        return discovered_servers
