"""HTTP client for BotVegas API.

This module wraps the BotVegas REST API with typed methods.
All MCP tools delegate to this client.
"""

from dataclasses import dataclass
from typing import Any

import httpx


class BotVegasAPIError(Exception):
    """Error from BotVegas API."""

    def __init__(self, status_code: int, message: str, details: dict | None = None):
        self.status_code = status_code
        self.message = message
        self.details = details or {}
        super().__init__(f"[{status_code}] {message}")


@dataclass
class BotVegasClient:
    """HTTP client for BotVegas Poker API.

    All methods are async and return typed responses.
    Errors are raised as BotVegasAPIError.
    """

    http: httpx.AsyncClient

    # =========================================================================
    # Helper Methods
    # =========================================================================

    def _auth_headers(self, token: str) -> dict[str, str]:
        """Build authorization headers."""
        return {"Authorization": f"Bearer {token}"}

    async def _request(
        self,
        method: str,
        path: str,
        *,
        token: str | None = None,
        json: dict | None = None,
        params: dict | None = None,
    ) -> dict[str, Any]:
        """Make an API request and handle errors."""
        headers = self._auth_headers(token) if token else {}

        response = await self.http.request(
            method,
            path,
            headers=headers,
            json=json,
            params=params,
        )

        if response.status_code >= 400:
            try:
                error_data = response.json()
                message = error_data.get("detail", str(error_data))
                if isinstance(message, dict):
                    message = message.get("message", str(message))
            except Exception:
                message = response.text or f"HTTP {response.status_code}"

            raise BotVegasAPIError(
                status_code=response.status_code,
                message=message,
                details=error_data if "error_data" in dir() else {},
            )

        return response.json()

    # =========================================================================
    # Authentication & Identity
    # =========================================================================

    async def register_identity(
        self,
        name: str,
        description: str | None = None,
    ) -> dict[str, Any]:
        """Register or retrieve an identity."""
        payload = {"name": name}
        if description:
            payload["description"] = description

        return await self._request("POST", "/auth/identity", json=payload)

    async def create_player(
        self,
        identity_id: str,
        handle: str,
    ) -> dict[str, Any]:
        """Create a new player under an identity."""
        return await self._request(
            "POST",
            "/auth/player",
            json={"identity_id": identity_id, "handle": handle},
        )

    async def authenticate(
        self,
        handle: str,
        token: str,
    ) -> dict[str, Any]:
        """Authenticate and get player info."""
        # The API uses token auth, we just verify by getting player info
        return await self._request("GET", "/players/me", token=token)

    async def retire_player(self, token: str) -> dict[str, Any]:
        """Retire the current player."""
        return await self._request("POST", "/auth/retire", token=token)

    async def get_stake_tiers(self) -> dict[str, Any]:
        """Get available stake tiers."""
        # This might be a static endpoint or part of config
        # For now, return hardcoded tiers matching the backend
        return {
            "tiers": [
                {
                    "name": "micro",
                    "small_blind": 5,
                    "big_blind": 10,
                    "min_buy_in": 200,
                    "max_buy_in": 1000,
                    "min_chips_required": 200,
                },
                {
                    "name": "low",
                    "small_blind": 25,
                    "big_blind": 50,
                    "min_buy_in": 1000,
                    "max_buy_in": 5000,
                    "min_chips_required": 1000,
                },
                {
                    "name": "medium",
                    "small_blind": 100,
                    "big_blind": 200,
                    "min_buy_in": 4000,
                    "max_buy_in": 20000,
                    "min_chips_required": 4000,
                },
                {
                    "name": "high",
                    "small_blind": 500,
                    "big_blind": 1000,
                    "min_buy_in": 20000,
                    "max_buy_in": 100000,
                    "min_chips_required": 20000,
                },
                {
                    "name": "high_roller",
                    "small_blind": 2500,
                    "big_blind": 5000,
                    "min_buy_in": 100000,
                    "max_buy_in": 500000,
                    "min_chips_required": 100000,
                },
            ]
        }

    # =========================================================================
    # Lobby
    # =========================================================================

    async def list_games(
        self,
        stake_tier: str | None = None,
        status: str | None = None,
        limit: int = 20,
        offset: int = 0,
    ) -> dict[str, Any]:
        """List available games."""
        params: dict[str, Any] = {"limit": limit, "offset": offset}
        if stake_tier:
            params["stake_tier"] = stake_tier
        if status:
            params["status"] = status

        return await self._request("GET", "/games", params=params)

    async def create_game(
        self,
        token: str,
        stake_tier: str,
        max_players: int,
        min_players_to_start: int,
        turn_timeout_seconds: int,
        buy_in: int,
    ) -> dict[str, Any]:
        """Create a new game."""
        return await self._request(
            "POST",
            "/games",
            token=token,
            json={
                "stake_tier_name": stake_tier,  # API expects stake_tier_name
                "max_players": max_players,
                "min_players_to_start": min_players_to_start,
                "turn_timeout_seconds": turn_timeout_seconds,
                "buy_in": buy_in,
            },
        )

    async def join_game(
        self,
        token: str,
        game_id: str,
        buy_in: int,
    ) -> dict[str, Any]:
        """Join an existing game."""
        return await self._request(
            "POST",
            f"/games/{game_id}/join",
            token=token,
            params={"buy_in": buy_in},
        )

    async def leave_game(
        self,
        token: str,
        game_id: str,
    ) -> dict[str, Any]:
        """Leave a game."""
        return await self._request(
            "POST",
            f"/games/{game_id}/leave",
            token=token,
        )

    async def get_leaderboards(
        self,
        board_type: str = "players",
        category: str = "chips",
        limit: int = 20,
        offset: int = 0,
    ) -> dict[str, Any]:
        """Get leaderboard data."""
        endpoint = f"/leaderboards/{board_type}"
        params = {
            "sort_by": category,
            "limit": limit,
            "offset": offset,
        }
        return await self._request("GET", endpoint, params=params)

    # =========================================================================
    # In-Game
    # =========================================================================

    async def start_game(
        self,
        token: str,
        game_id: str,
    ) -> dict[str, Any]:
        """Start a game (deal first hand)."""
        return await self._request(
            "POST",
            f"/games/{game_id}/start",
            token=token,
        )

    async def get_game_state(
        self,
        token: str,
        game_id: str,
    ) -> dict[str, Any]:
        """Get full game state."""
        return await self._request(
            "GET",
            f"/games/{game_id}/state",
            token=token,
        )

    async def get_game(
        self,
        game_id: str,
    ) -> dict[str, Any]:
        """Get game info (public)."""
        return await self._request("GET", f"/games/{game_id}")

    async def get_game_players(
        self,
        token: str,
        game_id: str,
        include_identity: bool = True,
    ) -> dict[str, Any]:
        """Get detailed player info for a game."""
        # This combines game state with player/identity lookups
        state = await self.get_game_state(token, game_id)
        players = state.get("players", [])

        if include_identity:
            # Enrich with identity info
            for player in players:
                if "player_id" in player:
                    try:
                        player_info = await self._request(
                            "GET", f"/players/{player['handle']}"
                        )
                        player["identity"] = {
                            "identity_name": player_info.get("identity_name"),
                            "total_games_played": player_info.get("games_played", 0),
                            "total_games_won": player_info.get("games_won", 0),
                            "win_rate": player_info.get("win_rate", 0.0),
                            "total_players_created": 1,  # Would need identity endpoint
                        }
                    except BotVegasAPIError:
                        player["identity"] = None

        return {"game_id": game_id, "players": players}

    async def take_action(
        self,
        token: str,
        game_id: str,
        action: str,
        amount: int | None = None,
        expression: str | None = None,
    ) -> dict[str, Any]:
        """Take a poker action."""
        payload: dict[str, Any] = {"action": action}
        if amount is not None:
            payload["amount"] = amount
        if expression:
            payload["expression"] = expression

        return await self._request(
            "POST",
            f"/games/{game_id}/action",
            token=token,
            json=payload,
        )

    async def get_hand_history(
        self,
        token: str,
        game_id: str,
        hand_number: int | None = None,
    ) -> dict[str, Any]:
        """Get hand history.
        
        NOTE: This endpoint is not yet implemented in the backend.
        Returns a placeholder response for now.
        """
        # TODO: Implement when backend has /games/{game_id}/history endpoint
        return {
            "game_id": game_id,
            "hand_number": hand_number or 1,
            "actions": [],
            "is_complete": False,
            "winners": None,
            "message": "Hand history endpoint not yet implemented",
        }

    async def get_my_stats(self, token: str) -> dict[str, Any]:
        """Get current player's stats."""
        return await self._request("GET", "/players/me", token=token)

    async def start_next_hand(
        self,
        token: str,
        game_id: str,
    ) -> dict[str, Any]:
        """Start the next hand."""
        return await self._request(
            "POST",
            f"/games/{game_id}/next-hand",
            token=token,
        )

    # =========================================================================
    # Escrow (Staked Games)
    # =========================================================================

    async def set_wallet_address(
        self,
        token: str,
        wallet_address: str,
    ) -> dict[str, Any]:
        """Set wallet address on the player's identity.
        
        Required for staked games. The wallet address is a Solana pubkey.
        """
        return await self._request(
            "PATCH",
            "/auth/identity/wallet",
            token=token,
            json={"wallet_address": wallet_address},
        )

    async def get_escrow_status(
        self,
        game_id: str,
    ) -> dict[str, Any]:
        """Get escrow status for a game.
        
        Returns escrow state for staked games.
        """
        return await self._request(
            "GET",
            f"/escrow/{game_id}/status",
        )

    async def get_player_deposit(
        self,
        game_id: str,
        wallet_address: str,
    ) -> dict[str, Any]:
        """Get a player's deposit status in a game escrow."""
        return await self._request(
            "GET",
            f"/escrow/{game_id}/deposit/{wallet_address}",
        )

    async def get_deposit_instruction(
        self,
        token: str,
        game_id: str,
    ) -> dict[str, Any]:
        """Get deposit instruction data for client-side signing.
        
        Returns the instruction data needed to build a deposit transaction.
        The player must sign this with their wallet.
        """
        return await self._request(
            "GET",
            f"/escrow/{game_id}/deposit-instruction",
            token=token,
        )

    async def get_withdraw_instruction(
        self,
        token: str,
        game_id: str,
    ) -> dict[str, Any]:
        """Get withdraw instruction data for client-side signing.
        
        Returns the instruction data needed to build a withdraw transaction.
        The player must sign this with their wallet.
        """
        return await self._request(
            "GET",
            f"/escrow/{game_id}/withdraw-instruction",
            token=token,
        )

    async def create_staked_game(
        self,
        token: str,
        stake_tier: str,
        max_players: int,
        min_players_to_start: int,
        turn_timeout_seconds: int,
        buy_in: int,
    ) -> dict[str, Any]:
        """Create a new staked game.
        
        Same as create_game but with game_mode='staked'.
        Requires wallet address to be set on identity.
        """
        return await self._request(
            "POST",
            "/games",
            token=token,
            json={
                "stake_tier": stake_tier,
                "max_players": max_players,
                "min_players_to_start": min_players_to_start,
                "turn_timeout_seconds": turn_timeout_seconds,
                "buy_in": buy_in,
                "game_mode": "staked",
            },
        )

    async def list_staked_games(
        self,
        stake_tier: str | None = None,
        status: str | None = None,
        limit: int = 20,
        offset: int = 0,
    ) -> dict[str, Any]:
        """List staked games only."""
        params: dict[str, Any] = {
            "limit": limit,
            "offset": offset,
            "game_mode": "staked",
        }
        if stake_tier:
            params["stake_tier"] = stake_tier
        if status:
            params["status"] = status

        return await self._request("GET", "/games", params=params)
