from __future__ import annotations

import os
import uuid
from pathlib import Path

from rich import box
from rich.align import Align
from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from rich.text import Text

from openpoker.types import (
    AIProvider,
    EngineOptions,
    GameConfig,
    PlayerConfig,
    PlayerType,
)

console = Console()

GOLD = "gold1"
MUTED = "grey50"
WHITE = "white"
RED = "red3"

OPENAI_MODELS = [
    "gpt-5.2",
    "gpt-5.1",
    "gpt-5",
    "gpt-5-mini",
]

ANTHROPIC_MODELS = [
    "claude-opus-4.6",
    "claude-sonnet-4.5",
    "claude-haiku-4.5",
]

OPENROUTER_MODELS = [
    "moonshotai/kimi-k2.5",
    "z-ai/glm-4.7",
    "nvidia/nemotron-3-nano-30b-a3b",
    "anthropic/claude-sonnet-4.5",
    "anthropic/claude-opus-4.6",
    "anthropic/claude-haiku-4.5",
    "openai/gpt-5.2",
    "openai/gpt-5.1",
    "openai/gpt-5",
    "openai/gpt-5-mini",
]


def prompt_int(label: str, default: int, min_value: int | None = None, max_value: int | None = None) -> int:
    while True:
        result = console.input(f"[{WHITE}]{label}[/{WHITE}] [{MUTED}]({default})[/{MUTED}]: ")
        if not result.strip():
            value = default
        else:
            try:
                value = int(result.strip())
            except ValueError:
                console.print(f"[{RED}]Enter a number[/{RED}]")
                continue

        if min_value is not None and value < min_value:
            console.print(f"[{RED}]Must be >= {min_value}[/{RED}]")
            continue
        if max_value is not None and value > max_value:
            console.print(f"[{RED}]Must be <= {max_value}[/{RED}]")
            continue
        return value


def prompt_str(label: str, default: str) -> str:
    result = console.input(f"[{WHITE}]{label}[/{WHITE}] [{MUTED}]({default})[/{MUTED}]: ")
    return result.strip() if result.strip() else default


def prompt_bool(label: str, default: bool) -> bool:
    default_label = "y" if default else "n"
    while True:
        result = console.input(f"[{WHITE}]{label} (y/n)[/{WHITE}] [{MUTED}]({default_label})[/{MUTED}]: ")
        value = result.strip().lower()
        if value == "":
            return default
        if value in ("y", "yes"):
            return True
        if value in ("n", "no"):
            return False
        console.print(f"[{RED}]Use y or n[/{RED}]")


def show_welcome() -> None:
    console.clear()
    title = Text()
    title.append("OPENPOKER", style=f"bold {GOLD}")
    console.print()
    console.print(
        Panel(
            Align.center(title),
            border_style=GOLD,
            padding=(1, 4),
            box=box.SQUARE,
        )
    )
    console.print()


def choose_ai_provider(player_num: int) -> AIProvider:
    console.print(f"\n[{GOLD}]AI Player #{player_num}[/{GOLD}]")
    table = Table(show_header=False, box=box.SIMPLE, padding=(0, 2))
    table.add_column(style=MUTED)
    table.add_column(style=WHITE)
    table.add_row("1", "OpenAI")
    table.add_row("2", "Anthropic")
    table.add_row("3", "OpenRouter")
    console.print(table)
    while True:
        choice = console.input(f"[{WHITE}]Provider[/{WHITE}] [{MUTED}](1)[/{MUTED}]: ").strip() or "1"
        if choice == "1":
            return AIProvider.OPENAI
        if choice == "2":
            return AIProvider.ANTHROPIC
        if choice == "3":
            return AIProvider.OPENROUTER
        console.print(f"[{RED}]Choose 1, 2, or 3[/{RED}]")


def choose_model(provider: AIProvider) -> str:
    if provider == AIProvider.OPENAI:
        models = OPENAI_MODELS
        default_idx = 4
    elif provider == AIProvider.ANTHROPIC:
        models = ANTHROPIC_MODELS
        default_idx = 1
    else:
        models = OPENROUTER_MODELS
        default_idx = 1
    table = Table(show_header=False, box=box.SIMPLE, padding=(0, 2))
    table.add_column(style=MUTED)
    table.add_column(style=WHITE)
    for idx, model in enumerate(models, start=1):
        table.add_row(str(idx), model)
    console.print(table)
    while True:
        choice = console.input(
            f"[{WHITE}]Model[/{WHITE}] [{MUTED}]({default_idx})[/{MUTED}]: "
        ).strip() or str(default_idx)
        if choice.isdigit():
            selected = int(choice)
            if 1 <= selected <= len(models):
                return models[selected - 1]
        console.print(f"[{RED}]Choose 1-{len(models)}[/{RED}]")


def get_required_providers(players: list[PlayerConfig]) -> set[AIProvider]:
    providers = set()
    for player in players:
        if player.player_type == PlayerType.AI and player.provider:
            providers.add(player.provider)
    return providers


def check_and_setup_api_keys(required_providers: set[AIProvider]) -> None:
    env_path = Path(".env")
    existing_env = {}

    if env_path.exists():
        with open(env_path) as f:
            for line in f:
                line = line.strip()
                if line and not line.startswith("#") and "=" in line:
                    key, value = line.split("=", 1)
                    existing_env[key.strip()] = value.strip()

    provider_key_map = {
        AIProvider.OPENAI: "OPENAI_API_KEY",
        AIProvider.ANTHROPIC: "ANTHROPIC_API_KEY",
        AIProvider.OPENROUTER: "OPENROUTER_API_KEY",
    }

    missing_keys = {}
    for provider in required_providers:
        key_name = provider_key_map[provider]
        if key_name not in existing_env or not existing_env[key_name]:
            missing_keys[provider] = key_name

    if not missing_keys:
        return

    console.print(f"\n[{GOLD}]API Key Setup[/{GOLD}]")
    console.print(f"[{MUTED}]The following API keys are needed but not found in .env:[/{MUTED}]")

    new_keys = {}
    for provider, key_name in missing_keys.items():
        provider_name = provider.value.capitalize()
        console.print(f"\n[{WHITE}]{provider_name} API Key[/{WHITE}]")
        while True:
            api_key = console.input(f"[{WHITE}]Enter your {provider_name} API key[/{WHITE}]: ").strip()
            if api_key:
                new_keys[key_name] = api_key
                os.environ[key_name] = api_key
                break
            console.print(f"[{RED}]API key cannot be empty[/{RED}]")

    if new_keys:
        with open(env_path, "a") as f:
            if env_path.stat().st_size > 0:
                f.write("\n")
            for key_name, api_key in new_keys.items():
                f.write(f"{key_name}={api_key}\n")
        console.print(f"\n[{MUTED}]API keys saved to .env[/{MUTED}]")


def setup_tui_game() -> tuple[GameConfig, EngineOptions, list[PlayerConfig], str]:
    show_welcome()

    console.print(f"[{GOLD}]Game Settings[/{GOLD}]")
    small_blind = prompt_int("Small blind", 10, min_value=1)
    big_blind = prompt_int("Big blind", 20, min_value=1)
    starting_stack = prompt_int("Starting stack", 1000, min_value=1)
    num_players = prompt_int("How many players (including you)", 4, min_value=2, max_value=9)

    console.print(f"\n[{GOLD}]Engine Options[/{GOLD}]")
    enable_memory = prompt_bool("Enable AI memory", True)
    enable_monte_carlo = prompt_bool("Enable Monte Carlo analysis", True)

    console.print()
    human_name = prompt_str("Your name", "Human Player")
    human_id = str(uuid.uuid4())

    players: list[PlayerConfig] = [
        PlayerConfig(
            id=human_id,
            name=human_name,
            player_type=PlayerType.HUMAN,
        )
    ]

    for i in range(1, num_players):
        provider = choose_ai_provider(i)
        model = choose_model(provider)
        ai_name = prompt_str("AI name", f"AI Player {i}")
        personality = prompt_str("AI personality (optional)", "")
        players.append(
            PlayerConfig(
                id=str(uuid.uuid4()),
                name=ai_name,
                player_type=PlayerType.AI,
                provider=provider,
                model=model,
                personality=personality if personality else None,
            )
        )

    required_providers = get_required_providers(players)
    check_and_setup_api_keys(required_providers)

    config = GameConfig(
        small_blind=small_blind,
        big_blind=big_blind,
        starting_stack=starting_stack,
        max_players=9,
    )
    options = EngineOptions(
        enable_memory=enable_memory,
        enable_monte_carlo=enable_monte_carlo,
        include_snapshot_in_events=True,
    )
    return config, options, players, human_id
