from __future__ import annotations

import random

from treys import Card, Deck, Evaluator

from openpoker.types import SimulationResult, WinProbabilityResult


def convert_card_format(card: str) -> int:
    rank = card[0].upper()
    suit = card[1].lower()
    return Card.new(rank + suit)


def simulate_hand(
    hole_cards: list[str],
    board_cards: list[str],
    num_opponents: int,
    iterations: int = 10000,
) -> SimulationResult:
    if len(hole_cards) != 2:
        raise ValueError("Must provide exactly 2 hole cards")
    if len(board_cards) > 5:
        raise ValueError("Board cannot have more than 5 cards")
    if num_opponents < 1:
        raise ValueError("Must have at least 1 opponent")

    evaluator = Evaluator()
    wins = 0
    ties = 0
    losses = 0

    hero_hole = [convert_card_format(c) for c in hole_cards]
    known_board = [convert_card_format(c) for c in board_cards] if board_cards else []
    known_cards_set = set(hero_hole + known_board)
    cards_needed = 5 - len(board_cards)

    for _ in range(iterations):
        full_deck = Deck.GetFullDeck()
        available_cards = [c for c in full_deck if c not in known_cards_set]
        random.shuffle(available_cards)

        simulated_board = known_board + available_cards[:cards_needed]
        remaining_cards = available_cards[cards_needed:]

        hero_score = evaluator.evaluate(simulated_board, hero_hole)

        opponent_scores: list[int] = []
        for i in range(num_opponents):
            opp_hole = remaining_cards[i * 2 : (i + 1) * 2]
            opp_score = evaluator.evaluate(simulated_board, opp_hole)
            opponent_scores.append(opp_score)

        best_opponent_score = min(opponent_scores)

        if hero_score < best_opponent_score:
            wins += 1
        elif hero_score == best_opponent_score:
            ties += 1
        else:
            losses += 1

    return SimulationResult(
        win=wins / iterations,
        tie=ties / iterations,
        lose=losses / iterations,
        iterations=iterations,
    )


def calculate_win_probability(
    hole_cards: list[str],
    board_cards: list[str],
    num_opponents: int,
    iterations: int = 10000,
) -> WinProbabilityResult:
    result = simulate_hand(hole_cards, board_cards, num_opponents, iterations=iterations)
    return WinProbabilityResult(
        win_probability=round(result.win, 4),
        tie_probability=round(result.tie, 4),
        lose_probability=round(result.lose, 4),
        win_percentage=round(result.win * 100, 2),
        iterations=result.iterations,
        message=f"Win: {result.win * 100:.1f}%, Tie: {result.tie * 100:.1f}%, Lose: {result.lose * 100:.1f}%",
    )
