from __future__ import annotations

from openpoker.core.monte_carlo import calculate_win_probability
from openpoker.core.pot_odds import calculate_expected_value, calculate_pot_odds
from openpoker.types import ActionType, AIContext, AvailableAction, Player, PlayerStatus


class AIMessageBuilder:
    def __init__(self, enable_memory: bool, enable_monte_carlo: bool):
        self.enable_memory = enable_memory
        self.enable_monte_carlo = enable_monte_carlo

    def get_system_prompt(self, player: Player, memories: list[str]) -> str:
        memories_section = ""
        if self.enable_memory and memories:
            memories_list = "\n".join([f"- {memory}" for memory in memories])
            memories_section = (
                "\nYour opponent reads from previous hands:\n"
                f"{memories_list}\n\n"
                "Use these reads to exploit your opponents' tendencies.\n"
            )

        personality_section = ""
        if player.personality:
            personality_section = f"\nPersonality:\n{player.personality}\n"

        math_section = "Mathematical analysis is included in every turn."
        if not self.enable_monte_carlo:
            math_section = (
                "Mathematical analysis may be unavailable in this table configuration. "
                "When unavailable, rely on board texture, position, stack depth, and prior actions."
            )

        return (
            f"You are {player.name}, a skilled poker player competing in a No-Limit Texas Hold'em game. "
            "Your goal is to maximize your chip stack by playing optimally."
            f"{personality_section}\n"
            f"{memories_section}\n"
            f"{math_section}\n"
            "IMPORTANT: Provide concise poker table-talk explaining your decision in 2-3 sentences, "
            "then call exactly one action tool."
        )

    def build_context_message(self, context: AIContext, available_actions: list[AvailableAction]) -> str:
        action_rows = [self._format_available_action(action) for action in available_actions]
        action_str = "\n".join(action_rows)

        actions_by_street: dict[str, list[dict[str, object]]] = {}
        for action in context.action_history:
            action_dict = action.model_dump()
            street = str(action_dict.get("street", "unknown"))
            if street not in actions_by_street:
                actions_by_street[street] = []
            actions_by_street[street].append(action_dict)

        history_str = ""
        for street in ["preflop", "flop", "turn", "river"]:
            if street in actions_by_street:
                history_str += f"  <street name='{street}'>\n"
                for action in actions_by_street[street]:
                    action_text = (
                        f"    <action player='{action['player_name']}', type='{action['action_type']}'"
                    )
                    if action.get("amount") is not None:
                        action_text += f", amount='{action['amount']}'"
                    action_text += f", pot_after='{action.get('pot_after', 'unknown')}'/>"
                    history_str += action_text + "\n"
                history_str += "  </street>\n"

        opponents_str = ""
        for opponent in context.opponents:
            roles: list[str] = []
            if opponent.is_dealer:
                roles.append("dealer")
            if opponent.is_small_blind:
                roles.append("small_blind")
            if opponent.is_big_blind:
                roles.append("big_blind")
            role_str = ", ".join(roles) if roles else "none"
            opponents_str += (
                f"  <opponent position='{opponent.position}' name='{opponent.name}' "
                f"stack='{opponent.stack}' current_bet='{opponent.current_bet}' "
                f"status='{opponent.status.value}' role='{role_str}'/>\n"
            )

        your_roles: list[str] = []
        if context.is_dealer:
            your_roles.append("dealer")
        if context.is_small_blind:
            your_roles.append("small_blind")
        if context.is_big_blind:
            your_roles.append("big_blind")
        your_role_str = ", ".join(your_roles) if your_roles else "none"

        active_opponents_count = sum(
            1 for opponent in context.opponents if opponent.status == PlayerStatus.ACTIVE
        )
        math_analysis = "  <mathematical_analysis><status>disabled</status></mathematical_analysis>\n"
        if self.enable_monte_carlo:
            monte_carlo = calculate_win_probability(
                hole_cards=context.hole_cards,
                board_cards=context.board_cards,
                num_opponents=active_opponents_count if active_opponents_count > 0 else 1,
            )
            win_prob = monte_carlo.win_probability
            ev_fold = calculate_expected_value(
                pot_size=context.pot,
                bet_to_call=context.call_amount,
                win_probability=win_prob,
                your_stack=context.stack,
                action="fold",
            )
            ev_call = calculate_expected_value(
                pot_size=context.pot,
                bet_to_call=context.call_amount,
                win_probability=win_prob,
                your_stack=context.stack,
                action="call",
            )

            if context.call_amount > 0:
                pot_odds_result = calculate_pot_odds(
                    pot_size=context.pot,
                    bet_to_call=context.call_amount,
                    win_probability=win_prob,
                )
                pot_odds_block = f"""    <pot_odds>
      <pot_size>{context.pot}</pot_size>
      <bet_to_call>{context.call_amount}</bet_to_call>
      <required_equity>{pot_odds_result.required_equity:.1%}</required_equity>
      <your_equity>{pot_odds_result.equity:.1%}</your_equity>
      <ev>{pot_odds_result.expected_value:.2f}</ev>
    </pot_odds>"""
            else:
                pot_odds_block = """    <pot_odds>
      <status>not_applicable_for_check</status>
    </pot_odds>"""

            math_analysis = f"""  <mathematical_analysis>
    <monte_carlo>
      <win_probability>{monte_carlo.win_probability:.1%}</win_probability>
      <tie_probability>{monte_carlo.tie_probability:.1%}</tie_probability>
      <lose_probability>{monte_carlo.lose_probability:.1%}</lose_probability>
    </monte_carlo>
{pot_odds_block}
    <expected_values>
      <fold>{ev_fold.ev:.2f}</fold>
      <call>{ev_call.ev:.2f}</call>
    </expected_values>
  </mathematical_analysis>
"""

        return f"""<game_state>
  <hand>
    <hole_cards>{", ".join(context.hole_cards)}</hole_cards>
    <board_cards>{", ".join(context.board_cards) if context.board_cards else "None"}</board_cards>
    <street>{context.street.value}</street>
  </hand>

  <your_position>
    <position>{context.position}</position>
    <role>{your_role_str}</role>
    <stack>{context.stack}</stack>
    <current_bet>{context.current_bet}</current_bet>
    <call_amount>{context.call_amount}</call_amount>
  </your_position>

  <pot_size>{context.pot}</pot_size>
  <active_opponents>{active_opponents_count}</active_opponents>

  <opponents>
{opponents_str}  </opponents>

  <action_history>
{history_str if history_str else "    <no_actions/>"}  </action_history>

{math_analysis}
  <available_actions>
{action_str}
  </available_actions>
</game_state>

Provide short table-talk and call exactly one action tool."""

    def _format_available_action(self, action: AvailableAction) -> str:
        row = f"  <action type='{action.action_type.value}'"
        if action.min_amount is not None and action.action_type in {
            ActionType.BET,
            ActionType.RAISE,
            ActionType.CALL,
        }:
            row += f", min='{action.min_amount}'"
        if action.max_amount is not None and action.action_type in {
            ActionType.BET,
            ActionType.RAISE,
            ActionType.CALL,
        }:
            row += f", max='{action.max_amount}'"
        row += "/>"
        return row
