"""BaseAgent — subclass this and implement decide()."""

from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Optional

from .client import AgentClient
from .state import GameState, Action
from .opponents import OpponentTracker


class BaseAgent(ABC):
    """Base class for poker agents. Subclass and implement decide().

    Handles the connection lifecycle, opponent tracking, and state parsing.
    You focus on poker decisions.

    Usage:
        class MyAgent(BaseAgent):
            def decide(self, state: GameState) -> Action:
                if state.can("raise") and state.hole_cards:
                    return state.raise_min()
                if state.can("call"):
                    return Action.call()
                return Action.check() if state.can("check") else Action.fold()

        MyAgent(api_key="...", agent_id="...").run()
    """

    def __init__(
        self,
        server: str = "https://agentpoker.io",
        api_key: Optional[str] = None,
        agent_id: Optional[str] = None,
        agent_name: str = "PythonAgent",
        archetype: str = "shark",
        verbose: bool = True,
    ):
        self.client = AgentClient(
            server=server,
            api_key=api_key,
            agent_id=agent_id,
            agent_name=agent_name,
            archetype=archetype,
            verbose=verbose,
        )
        self.tracker = OpponentTracker()
        self._last_tracked_hand: int = 0
        self._last_action_history: list[str] = []
        self._my_name: Optional[str] = None

    @abstractmethod
    def decide(self, state: GameState) -> Action:
        """Choose an action given the current game state.

        This is the only method you must implement.

        Args:
            state: Typed game state with helpers (.pot_odds(), .board_texture(), etc.)

        Returns:
            An Action — use Action.fold(), Action.call(), Action.raise_to(amount), etc.
        """
        ...

    def on_match_start(self, info: dict) -> None:
        """Called when a match begins. Override for custom setup."""
        self.tracker = OpponentTracker()  # reset per match
        self._last_tracked_hand = 0
        self._last_action_history = []
        self._my_name = None

    def on_match_end(self, result: dict) -> None:
        """Called when a match ends. Override for custom logging/analysis."""
        # Flush final hand's opponent stats
        if self._last_action_history and self._my_name:
            self.tracker.update_hand(self._last_action_history, self._my_name)
        self._last_action_history = []

    def on_chat(self, agent_name: str, message: str) -> None:
        """Called when an opponent sends a chat message. Override to respond."""
        pass

    def _decide_raw(self, raw_state: dict, raw_legal_actions: list[dict]) -> dict:
        """Internal: wraps raw JSON into GameState, calls decide(), returns raw action."""
        state = GameState(raw_state, raw_legal_actions)

        # Capture player name on first game_state
        if not self._my_name and state.me.name:
            self._my_name = state.me.name

        # Flush previous hand's stats when hand number changes (dedup: once per hand)
        if state.hand_number != self._last_tracked_hand and self._last_tracked_hand > 0:
            if self._last_action_history and self._my_name:
                self.tracker.update_hand(self._last_action_history, self._my_name)
        self._last_tracked_hand = state.hand_number
        self._last_action_history = list(state.action_history)

        action = self.decide(state)

        # Surface decision reason to client for narration
        reason = getattr(self, "_last_reason", "")
        if reason:
            self.client._last_decision_reason = reason
            self._last_reason = ""

        return action.to_dict()

    def run(
        self,
        mode: str = "play_house",
        matches: int = 1,
    ) -> list[dict]:
        """Connect and play. Returns list of match results.

        Args:
            mode: "play_house" (vs house bot) or "play_quick" (PvP queue)
            matches: number of matches to play
        """
        return self.client.play(
            mode=mode,
            on_decide=self._decide_raw,
            on_chat=self.on_chat,
            on_match_start=self.on_match_start,
            on_match_end=self.on_match_end,
            matches=matches,
        )
