from __future__ import annotations

import argparse
import asyncio
import sys
import traceback

from dotenv import load_dotenv
from rich.console import Console
from rich.live import Live

from openpoker.adapters.tui.renderer import (
    build_snapshot_renderable,
    clear_screen,
    format_event_message,
    prompt_action,
    render_snapshot,
)
from openpoker.adapters.tui.setup import setup_tui_game
from openpoker.engine import PokerEngine
from openpoker.types import (
    ActionAppliedPayload,
    EngineEvent,
    EngineEventType,
    GameOverPayload,
    GameSnapshot,
    PlayerSnapshot,
    PlayerStatus,
)

console = Console()


async def run_tui(debug: bool = False) -> None:
    config, options, players, human_player_id = setup_tui_game()
    options.debug = debug

    engine = PokerEngine(options=options)
    game_over_payload: GameOverPayload | None = None
    event_log: list[str] = []

    def on_event(event: EngineEvent) -> None:
        nonlocal game_over_payload
        message = format_event_message(event)
        if message:
            if event.event_type == EngineEventType.ACTION_APPLIED:
                assert isinstance(event.payload, ActionAppliedPayload)
                waiting_prefix = f"Waiting for {event.payload.player_name}..."
                for i in range(len(event_log) - 1, -1, -1):
                    if event_log[i] == waiting_prefix:
                        event_log[i] = message
                        break
                else:
                    event_log.append(message)
            else:
                event_log.append(message)
            if len(event_log) > 80:
                event_log[:] = event_log[-80:]
        if event.event_type == EngineEventType.GAME_OVER:
            assert isinstance(event.payload, GameOverPayload)
            game_over_payload = event.payload

    engine.subscribe(on_event)

    game_id = engine.create_game(
        game_name="OpenPoker TUI",
        config=config,
        players=players,
    )
    engine.start_hand(game_id)

    status_message: str | None = None

    while True:
        snapshot = engine.get_snapshot(game_id=game_id, viewer_id=human_player_id)
        if not snapshot.hand_active and snapshot.showdown_results:
            snapshot = engine.get_snapshot(
                game_id=game_id,
                viewer_id=human_player_id,
                reveal_all_hole_cards=True,
            )
        render_snapshot(
            snapshot=snapshot,
            human_player_id=human_player_id,
            event_log=event_log,
            status_message=status_message,
        )
        status_message = None

        if game_over_payload is not None:
            console.print(f"\nWinner: {game_over_payload.winner_name} (${game_over_payload.winner_stack})\n")
            return

        if not snapshot.hand_active:
            if _is_game_over(snapshot):
                winner = _find_last_active(snapshot)
                if winner:
                    console.print(f"\nWinner: {winner.name} (${winner.stack})\n")
                else:
                    console.print("\nGame ended.\n")
                return
            console.input("\nPress Enter for next hand...")
            engine.start_hand(game_id)
            await asyncio.sleep(0.05)
            continue

        if snapshot.current_actor_id == human_player_id:
            available_actions = engine.get_available_actions(
                game_id=game_id,
                player_id=human_player_id,
            )
            hero_hole_cards: list[str] | None = None
            for player in snapshot.players:
                if player.id == human_player_id:
                    hero_hole_cards = player.hole_cards
                    break
            action = prompt_action(available_actions, hero_hole_cards=hero_hole_cards)
            await engine.submit_action(
                game_id=game_id,
                player_id=human_player_id,
                action=action,
            )
            await asyncio.sleep(0.05)
            continue

        waiting_id = snapshot.current_actor_id
        waiting_name = snapshot.current_actor_name
        if waiting_name:
            event_log.append(f"Waiting for {waiting_name}...")

        clear_screen()
        renderable = build_snapshot_renderable(
            snapshot,
            human_player_id,
            event_log,
            waiting_for=waiting_id,
        )
        with Live(renderable, console=console, refresh_per_second=10, screen=False):
            await engine.step(game_id)

        await asyncio.sleep(0.05)


def _is_game_over(snapshot: GameSnapshot) -> bool:
    active = [
        player
        for player in snapshot.players
        if player.stack > 0 and player.status != PlayerStatus.SITTING_OUT
    ]
    return len(active) < 2


def _find_last_active(snapshot: GameSnapshot) -> PlayerSnapshot | None:
    active = [
        player
        for player in snapshot.players
        if player.stack > 0 and player.status != PlayerStatus.SITTING_OUT
    ]
    return active[0] if active else None


def main() -> None:
    load_dotenv()
    try:
        parser = argparse.ArgumentParser(description="OpenPoker Terminal UI")
        parser.add_argument("--debug", action="store_true", help="Enable debug mode")
        args = parser.parse_args()
        asyncio.run(run_tui(debug=args.debug))
    except KeyboardInterrupt:
        console.print("\n\nGame interrupted.\n")
        sys.exit(0)
    except Exception as e:
        console.print(f"\n\nError: {e}\n")
        traceback.print_exc()
        sys.exit(1)
