from __future__ import annotations

import math
from typing import Dict, Optional

import tkinter as tk
from tkinter import messagebox, simpledialog

import typer

from .board import cube_to_pixel
from .data import Resource
from .game import Game, GameConfig


RESOURCE_COLORS: Dict[str, str] = {
    "brick": "#b5651d",
    "lumber": "#228b22",
    "wool": "#7ec850",
    "grain": "#f4d03f",
    "ore": "#95a5a6",
    "desert": "#f5deb3",
}


class CatanGUI:
    def __init__(self, game: Game):
        self.game = game
        self.root = tk.Tk()
        self.root.title("Catan GUI")
        self.canvas = tk.Canvas(self.root, width=900, height=700, bg="#1c1f24")
        self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.sidebar = tk.Frame(self.root, padx=10, pady=10)
        self.sidebar.pack(side=tk.RIGHT, fill=tk.Y)
        self.log_widget = tk.Text(self.sidebar, width=40, height=18, state=tk.DISABLED)
        self.log_widget.pack(pady=5)
        self._add_buttons()
        self.draw_board()
        self.update_status()

    def _add_buttons(self) -> None:
        button_specs = [
            ("Бросить кубики", self.roll_dice),
            ("Построить поселение", self.build_settlement),
            ("Построить город", self.build_city),
            ("Построить дорогу", self.build_road),
            ("Купить развитие", self.buy_development),
            ("Сыграть рыцаря", self.play_knight),
            ("Сыграть дор. стройку", self.play_road_building),
            ("Сыграть Изобилие", self.play_year_of_plenty),
            ("Сыграть Монополию", self.play_monopoly),
            ("Обмен с банком", self.trade_bank),
            ("Передвинуть разбойника", self.move_robber),
            ("Сбросить карты", self.discard_cards),
            ("Стартовая постройка", self.place_initial),
            ("Доступные действия", self.show_actions),
            ("Завершить ход", self.end_turn),
        ]
        for title, handler in button_specs:
            tk.Button(self.sidebar, text=title, command=handler, width=24).pack(pady=2)

    def run(self) -> None:
        self.root.mainloop()

    def log(self, message: str) -> None:
        self.log_widget.configure(state=tk.NORMAL)
        self.log_widget.insert(tk.END, message + "\n")
        self.log_widget.configure(state=tk.DISABLED)
        self.log_widget.see(tk.END)

    def draw_board(self) -> None:
        self.canvas.delete("all")
        size = 60
        for tile in self.game.board.hexes.values():
            px, py = cube_to_pixel(tile.coord, scale=size / math.sqrt(3))
            self._draw_hex(px + 350, py + 300, size * 0.6, RESOURCE_COLORS[tile.resource.value])
            label = tile.number_token or ""
            text = f"{label}"
            if tile.has_robber:
                text += " (R)"
            self.canvas.create_text(px + 350, py + 300, text=text, fill="black", font=("Arial", 12, "bold"))
        for edge in self.game.board.edges.values():
            if not edge.owner:
                continue
            a = self._corner_to_pixel(edge.corners[0])
            b = self._corner_to_pixel(edge.corners[1])
            color = self._player_color(edge.owner)
            self.canvas.create_line(a[0], a[1], b[0], b[1], fill=color, width=4)
        for corner_id, corner in self.game.board.corners.items():
            if not corner.owner:
                continue
            x, y = self._corner_to_pixel(corner_id)
            color = self._player_color(corner.owner)
            radius = 8 if corner.building == "settlement" else 12
            self.canvas.create_oval(
                x - radius,
                y - radius,
                x + radius,
                y + radius,
                fill=color,
                outline="white",
            )

    def _draw_hex(self, x: float, y: float, size: float, color: str) -> None:
        points = []
        for i in range(6):
            angle = math.pi / 3 * i + math.pi / 6
            px = x + size * math.cos(angle)
            py = y + size * math.sin(angle)
            points.extend([px, py])
        self.canvas.create_polygon(points, fill=color, outline="black", width=1)

    def _corner_to_pixel(self, coord) -> tuple[float, float]:
        px, py = cube_to_pixel(coord, scale=35 / math.sqrt(3))
        return px + 350, py + 300

    def _player_color(self, name: str) -> str:
        for player in self.game.players:
            if player.name == name:
                return player.color
        return "white"

    def update_status(self) -> None:
        self.log(
            f"Ход игрока {self.game.current_player.name} | Фаза: {self.game.phase.value}"
        )
        self.draw_board()

    def _ask_int(self, prompt: str, default: Optional[int] = None) -> int:
        value = simpledialog.askinteger("Ввод", prompt, initialvalue=default, parent=self.root)
        if value is None:
            raise ValueError("Операция отменена")
        return value

    def _ask_resource(self, prompt: str) -> Resource:
        value = simpledialog.askstring("Ввод", prompt, parent=self.root)
        if not value:
            raise ValueError("Операция отменена")
        return Resource[value.strip().upper()]

    def roll_dice(self) -> None:
        try:
            value = self.game.roll_dice()
            self.log(f"Бросок: {value}")
        except Exception as exc:  # noqa: BLE001
            messagebox.showerror("Ошибка", str(exc))
        self.update_status()

    def build_settlement(self) -> None:
        try:
            corner = self._ask_int("ID перекрестка")
            self.game.build_settlement(corner)
            self.log(f"Построено поселение #{corner}")
        except Exception as exc:
            messagebox.showerror("Ошибка", str(exc))
        self.update_status()

    def build_city(self) -> None:
        try:
            corner = self._ask_int("ID перекрестка")
            self.game.build_city(corner)
            self.log(f"Построен город #{corner}")
        except Exception as exc:
            messagebox.showerror("Ошибка", str(exc))
        self.update_status()

    def build_road(self) -> None:
        try:
            edge = self._ask_int("ID ребра")
            self.game.build_road(edge)
            self.log(f"Построена дорога #{edge}")
        except Exception as exc:
            messagebox.showerror("Ошибка", str(exc))
        self.update_status()

    def buy_development(self) -> None:
        try:
            card = self.game.buy_development_card()
            self.log(f"Куплена карта {card.value}")
        except Exception as exc:
            messagebox.showerror("Ошибка", str(exc))
        self.update_status()

    def play_knight(self) -> None:
        try:
            hex_id = self._ask_int("ID гекса для разбойника")
            victim = simpledialog.askstring("Ввод", "Кого грабим? (опционально)", parent=self.root)
            self.game.play_knight(hex_id, victim or None)
            self.log("Сыгран рыцарь")
        except Exception as exc:
            messagebox.showerror("Ошибка", str(exc))
        self.update_status()

    def play_road_building(self) -> None:
        try:
            first = self._ask_int("ID первой дороги")
            second = self._ask_int("ID второй дороги")
            self.game.play_road_building([first, second])
            self.log("Сыграна дорожная стройка")
        except Exception as exc:
            messagebox.showerror("Ошибка", str(exc))
        self.update_status()

    def play_year_of_plenty(self) -> None:
        try:
            res1 = self._ask_resource("Ресурс 1")
            res2 = self._ask_resource("Ресурс 2")
            self.game.play_year_of_plenty([res1, res2])
            self.log("Сыграна карта Изобилия")
        except Exception as exc:
            messagebox.showerror("Ошибка", str(exc))
        self.update_status()

    def play_monopoly(self) -> None:
        try:
            res = self._ask_resource("Какой ресурс монополизировать?")
            self.game.play_monopoly(res)
            self.log("Сыграна Монополия")
        except Exception as exc:
            messagebox.showerror("Ошибка", str(exc))
        self.update_status()

    def trade_bank(self) -> None:
        try:
            give = self._ask_resource("Что отдать?")
            receive = self._ask_resource("Что получить?")
            amount = self._ask_int("Сколько карт получить? (по умолчанию 1)", default=1)
            self.game.trade_with_bank(give, receive, amount or 1)
            self.log("Обмен с банком завершён")
        except Exception as exc:
            messagebox.showerror("Ошибка", str(exc))
        self.update_status()

    def move_robber(self) -> None:
        try:
            hex_id = self._ask_int("ID гекса для разбойника")
            victim = simpledialog.askstring("Ввод", "Кого грабим? (опционально)", parent=self.root)
            self.game.move_robber(hex_id, victim or None)
            self.log("Разбойник передвинут")
        except Exception as exc:
            messagebox.showerror("Ошибка", str(exc))
        self.update_status()

    def discard_cards(self) -> None:
        try:
            player = simpledialog.askstring("Ввод", "Кто сбрасывает?", parent=self.root)
            if not player:
                raise ValueError("Нужно указать игрока.")
            cards_str = simpledialog.askstring(
                "Ввод", "Введите пары ресурс-количество, напр. 'brick 2 wheat 1'", parent=self.root
            )
            if not cards_str:
                raise ValueError("Нужно ввести карты для сброса.")
            tokens = cards_str.split()
            losses = {}
            idx = 0
            while idx < len(tokens):
                res = Resource[tokens[idx].upper()]
                amount = int(tokens[idx + 1])
                losses[res] = amount
                idx += 2
            self.game.discard_cards(player, losses)
            self.log(f"{player} сбросил карты.")
        except Exception as exc:
            messagebox.showerror("Ошибка", str(exc))
        self.update_status()

    def place_initial(self) -> None:
        try:
            corner = self._ask_int("ID перекрестка")
            road = self._ask_int("ID ребра")
            self.game.place_initial_settlement(corner, road)
            self.log("Стартовая постройка размещена")
        except Exception as exc:
            messagebox.showerror("Ошибка", str(exc))
        self.update_status()

    def show_actions(self) -> None:
        env = CatanEnv(self.game.config)
        env.game = self.game
        actions = env.legal_actions()
        text = "\n".join(f"{a.type.value}: {a.payload}" for a in actions)
        messagebox.showinfo("Действия", text or "Нет действий")

    def end_turn(self) -> None:
        try:
            self.game.end_turn()
            self.log("Ход завершён")
        except Exception as exc:
            messagebox.showerror("Ошибка", str(exc))
        self.update_status()


cli = typer.Typer(help="Запуск графического интерфейса Catan")


@cli.command()
def run(
    players: str = typer.Option("Alice,Bob,Carla", help="Игроки через запятую"),
    seed: Optional[int] = typer.Option(None, help="Seed генератора"),
) -> None:
    names = [name.strip() for name in players.split(",") if name.strip()]
    config = GameConfig(player_names=names, seed=seed)
    gui = CatanGUI(Game(config))
    gui.run()


def main() -> None:
    cli()


if __name__ == "__main__":
    main()
