import time

from rich.console import Console

from .game import Game, MoveResult
from .lang import color_name, set_lang, t
from .network import Connection, RoomClient, RoomHost
from .pieces import Color, Position
from .rules import Rules
from .ui import GameUI

COL_LETTERS = "abcdefghi"


# ── Input parsing ──────────────────────────────────────────────────────


def parse_position(text: str) -> Position | None:
    text = text.strip().lower()
    if len(text) == 2 and text[0] in COL_LETTERS and text[1].isdigit():
        col = COL_LETTERS.index(text[0])
        row = int(text[1])
        if 0 <= row <= 9:
            return Position(col, row)
    return None


def parse_input(text: str) -> tuple | None:
    text = text.strip().lower()
    if not text:
        return ("command", "cancel")

    commands = {
        "r": "resign", "resign": "resign",
        "d": "draw", "draw": "draw",
        "h": "help", "help": "help", "?": "help",
        "q": "quit", "quit": "quit",
        "u": "undo", "undo": "undo",
    }
    if text in commands:
        return ("command", commands[text])

    parts = text.replace("-", " ").split()
    if len(parts) == 2:
        p1, p2 = parse_position(parts[0]), parse_position(parts[1])
        if p1 and p2:
            return ("move", p1, p2)

    if len(text) == 4 and text[0].isalpha():
        p1, p2 = parse_position(text[:2]), parse_position(text[2:])
        if p1 and p2:
            return ("move", p1, p2)

    if len(parts) == 1:
        p = parse_position(parts[0])
        if p:
            return ("select", p)
    return None


# ── Result message ─────────────────────────────────────────────────────


def _result_msg(result: MoveResult, game: Game) -> str:
    if result == MoveResult.GENERAL_CAPTURED and game.winner:
        wc = "red1" if game.winner == Color.RED else "green3"
        wn = color_name(game.winner.value)
        return f"[bold {wc}]{t('general_captured', color=wn)}[/]"
    if result == MoveResult.STALEMATE:
        return f"[bold yellow]{t('draw_stalemate')}[/]"
    if result == MoveResult.REPETITION:
        return f"[bold yellow]{t('draw_repetition')}[/]"
    if result == MoveResult.CHECK:
        return f"[bold red]{t('check')}[/]"
    return ""


# ── Shared move handler ────────────────────────────────────────────────

def _handle_select(game, pos, selected, valid_moves, allowed_color=None):
    """Handle a 'select' action. Returns (selected, valid_moves, message)."""
    color = allowed_color or game.current_turn

    if selected and pos in valid_moves:
        result = game.make_move(selected, pos)
        if result == MoveResult.INVALID:
            return None, [], f"[red]{t('invalid_move')}[/]", (selected, pos), result
        return None, [], _result_msg(result, game), (selected, pos), result

    piece = game.board.get_piece(pos)
    if piece and piece.color == color:
        moves = Rules.get_valid_moves(game.board, piece)
        if moves:
            return pos, moves, "", None, None
        return None, [], f"[yellow]{t('no_moves')}[/]", None, None
    if selected:
        return selected, valid_moves, f"[red]{t('invalid_dest')}[/]", None, None
    return None, [], f"[red]{t('no_piece')}[/]", None, None


# ── Local two-player game ─────────────────────────────────────────────


def play_local(console: Console, ui: GameUI):
    game = Game()
    selected: Position | None = None
    valid_moves: list[Position] = []
    last_move: tuple[Position, Position] | None = None
    message = ""

    while not game.game_over:
        ui.render_game_screen(
            game, Color.RED, selected, valid_moves, last_move, message,
        )
        if selected:
            prompt = f"  {t('dest_prompt')}: "
        else:
            prompt = f"  {t('select_prompt')} ({t('help_hint')}): "

        parsed = parse_input(ui.get_input(prompt))
        message = ""

        if parsed is None:
            message = f"[red]{t('invalid_cmd')}[/]"
            continue

        kind = parsed[0]

        if kind == "command":
            cmd = parsed[1]
            if cmd == "cancel":
                selected, valid_moves = None, []
            elif cmd == "resign":
                game.resign(game.current_turn)
                wn = color_name(game.winner.value) if game.winner else ""
                message = f"[bold]{t('resigned_win', color=wn)}[/]"
            elif cmd == "draw":
                game.game_over = True
                game.draw = True
                message = f"[bold yellow]{t('draw_agree')}[/]"
            elif cmd == "help":
                ui.render_help()
            elif cmd == "undo":
                if game.undo_move():
                    selected, valid_moves, last_move = None, [], None
                    message = f"[dim]{t('undo_done')}[/]"
                else:
                    message = f"[red]{t('undo_none')}[/]"
            elif cmd == "quit":
                return
            continue

        if kind == "move":
            from_pos, to_pos = parsed[1], parsed[2]
            result = game.make_move(from_pos, to_pos)
            if result == MoveResult.INVALID:
                message = f"[red]{t('invalid_move')}[/]"
                continue
            selected, valid_moves = None, []
            last_move = (from_pos, to_pos)
            message = _result_msg(result, game)
            continue

        if kind == "select":
            sel, vm, msg, lm, _res = _handle_select(
                game, parsed[1], selected, valid_moves
            )
            selected, valid_moves = sel, vm
            if msg:
                message = msg
            if lm:
                last_move = lm
            continue

    ui.render_game_screen(game, Color.RED, last_move=last_move, message=message)
    ui.render_game_over(game)
    ui.get_input(f"  {t('press_enter')}")


# ── Network game ───────────────────────────────────────────────────────


def host_game(console: Console, ui: GameUI):
    host = RoomHost()
    room_id, local_ip, tcp_port = host.start()
    ui.render_room_info(room_id, local_ip, tcp_port)
    console.print(f"  [dim]{t('room_cancel')}[/]\n")

    try:
        conn = host.wait_for_player(timeout=300)
    except KeyboardInterrupt:
        host.stop()
        console.print(f"\n  [yellow]{t('room_cancelled')}[/]")
        time.sleep(1)
        return

    if conn is None:
        host.stop()
        console.print(f"  [red]{t('room_timeout')}[/]")
        time.sleep(2)
        return

    conn.send({"type": "game_start", "your_color": "black"})
    my_color = Color.RED
    ui.render_connected(my_color)
    time.sleep(1.5)
    _play_network(console, ui, conn, my_color)
    conn.close()
    host.stop()


def join_game(console: Console, ui: GameUI):
    ui.clear()
    console.print()
    console.print(f"  [bold]{t('join_title')}[/]")
    console.print(f"  [dim]{t('join_prompt')}[/]")
    console.print()
    room_input = ui.get_input("  > ")

    if not room_input or room_input.lower() == "q":
        return

    client = RoomClient()
    conn: Connection | None = None

    if ":" in room_input:
        parts = room_input.rsplit(":", 1)
        try:
            ip, port = parts[0].strip(), int(parts[1].strip())
            console.print(f"  [italic]{t('join_connecting', addr=f'{ip}:{port}')}[/]")
            conn = client.connect_direct(ip, port)
        except (ValueError, IndexError):
            console.print(f"  [red]{t('join_invalid_addr')}[/]")
            time.sleep(2)
            return
    else:
        rid = room_input.upper().strip()
        if len(rid) != 4:
            console.print(f"  [red]{t('join_invalid_id')}[/]")
            time.sleep(2)
            return
        console.print(f"  [italic]{t('join_searching', rid=rid)}[/]")
        conn = client.find_and_join(rid, timeout=30)

    if conn is None:
        console.print(f"  [red]{t('join_not_found')}[/]")
        time.sleep(2)
        return

    msg = conn.receive(timeout=10)
    if not msg or msg.get("type") != "game_start":
        console.print(f"  [red]{t('join_error')}[/]")
        conn.close()
        time.sleep(2)
        return

    my_color = Color.RED if msg["your_color"] == "red" else Color.BLACK
    ui.render_connected(my_color)
    time.sleep(1.5)
    _play_network(console, ui, conn, my_color)
    conn.close()


def _play_network(console: Console, ui: GameUI, conn: Connection, my_color: Color):
    game = Game()
    selected: Position | None = None
    valid_moves: list[Position] = []
    last_move: tuple[Position, Position] | None = None
    message = ""

    while not game.game_over:
        ui.render_game_screen(
            game, my_color, selected, valid_moves, last_move, message,
            is_network=True, my_color=my_color,
        )

        if not conn.connected:
            message = f"[red]{t('disconnected')}[/]"
            break

        # ── Opponent's turn ────────────────────────────────────────────
        if game.current_turn != my_color:
            console.print(f"  [dim italic]{t('waiting')}[/]")
            msg = conn.receive(timeout=300)

            if msg is None or msg.get("type") == "disconnected":
                message = f"[red]{t('disconnected')}[/]"
                break

            if msg["type"] == "move":
                fp = Position(msg["from"][0], msg["from"][1])
                tp = Position(msg["to"][0], msg["to"][1])
                result = game.make_move(fp, tp)
                last_move, selected, valid_moves = (fp, tp), None, []
                message = _result_msg(result, game)
                continue

            if msg["type"] == "resign":
                game.game_over, game.winner = True, my_color
                message = f"[bold green]{t('opponent_resigned')}[/]"
                break

            if msg["type"] == "draw_offer":
                ui.render_game_screen(
                    game, my_color, last_move=last_move,
                    message=f"[yellow]{t('draw_offer_recv')}[/]",
                    is_network=True, my_color=my_color,
                )
                ans = ui.get_input("  > ").lower()
                if ans in ("y", "yes"):
                    conn.send({"type": "draw_response", "accept": True})
                    game.game_over, game.draw = True, True
                    message = f"[bold yellow]{t('draw_agree')}[/]"
                else:
                    conn.send({"type": "draw_response", "accept": False})
                    message = f"[dim]{t('you_declined')}[/]"
                continue

            if msg["type"] == "draw_response":
                if msg.get("accept"):
                    game.game_over, game.draw = True, True
                    message = f"[bold yellow]{t('draw_accepted')}[/]"
                else:
                    message = f"[dim]{t('draw_declined')}[/]"
                continue
            continue

        # ── My turn ────────────────────────────────────────────────────
        if selected:
            prompt = f"  {t('dest_prompt')}: "
        else:
            prompt = f"  {t('select_prompt')} ({t('help_hint')}): "

        parsed = parse_input(ui.get_input(prompt))
        message = ""

        if parsed is None:
            message = f"[red]{t('invalid_cmd')}[/]"
            continue

        kind = parsed[0]

        if kind == "command":
            cmd = parsed[1]
            if cmd == "cancel":
                selected, valid_moves = None, []
            elif cmd == "resign":
                conn.send({"type": "resign"})
                game.resign(my_color)
                message = f"[red]{t('you_resigned')}[/]"
            elif cmd == "draw":
                conn.send({"type": "draw_offer"})
                message = f"[dim italic]{t('draw_sent')}[/]"
                resp = conn.receive(timeout=60)
                if resp and resp.get("type") == "draw_response":
                    if resp.get("accept"):
                        game.game_over, game.draw = True, True
                        message = f"[bold yellow]{t('draw_accepted')}[/]"
                    else:
                        message = f"[dim]{t('draw_declined')}[/]"
                elif resp and resp.get("type") == "move":
                    fp = Position(resp["from"][0], resp["from"][1])
                    tp = Position(resp["to"][0], resp["to"][1])
                    game.make_move(fp, tp)
                    last_move = (fp, tp)
                    message = f"[dim]{t('opp_declined_moved')}[/]"
            elif cmd == "help":
                ui.render_help()
            elif cmd == "undo":
                message = f"[dim]{t('no_undo_net')}[/]"
            elif cmd == "quit":
                conn.send({"type": "resign"})
                return
            continue

        if kind == "move":
            fp, tp = parsed[1], parsed[2]
            result = game.make_move(fp, tp)
            if result == MoveResult.INVALID:
                message = f"[red]{t('invalid_move')}[/]"
                continue
            conn.send({"type": "move", "from": list(fp), "to": list(tp)})
            selected, valid_moves = None, []
            last_move = (fp, tp)
            message = _result_msg(result, game)
            continue

        if kind == "select":
            sel, vm, msg_text, lm, _res = _handle_select(
                game, parsed[1], selected, valid_moves, my_color
            )
            selected, valid_moves = sel, vm
            if msg_text:
                message = msg_text
            if lm:
                conn.send({"type": "move", "from": list(lm[0]), "to": list(lm[1])})
                last_move = lm
            continue

    ui.render_game_screen(
        game, my_color, last_move=last_move, message=message,
        is_network=True, my_color=my_color,
    )
    ui.render_game_over(game)
    ui.get_input(f"  {t('press_enter')}")


# ── Main entry point ───────────────────────────────────────────────────


def main():
    console = Console()
    ui = GameUI(console)

    while True:
        ui.render_main_menu()
        choice = ui.get_input(f"  {t('choose')} [1-5]: ")

        if choice == "1":
            ui.render_network_menu()
            nc = ui.get_input(f"  {t('choose')} [1-3]: ")
            if nc == "1":
                host_game(console, ui)
            elif nc == "2":
                join_game(console, ui)
        elif choice == "2":
            play_local(console, ui)
        elif choice == "3":
            ui.render_help()
        elif choice == "4":
            ui.render_lang_menu()
            lc = ui.get_input(f"  {t('choose')} [1-2]: ")
            if lc == "1":
                set_lang("vi")
            elif lc == "2":
                set_lang("en")
        elif choice in ("5", "q"):
            ui.clear()
            console.print(f"\n  [bold]{t('goodbye')}[/]\n")
            break
        else:
            console.print(f"  [red]{t('invalid_choice')}[/]")
            time.sleep(1)
