from __future__ import annotations

from datetime import datetime

import chess
import chess.pgn


class GameState:
    def __init__(
        self,
        player_color: chess.Color = chess.WHITE,
        difficulty: str = "intermediate",
        opponent_elo: int | None = None,
        opponent_name: str = "Stockfish",
        site: str = "Local",
    ) -> None:
        self.board = chess.Board()
        self.player_color = player_color
        self.difficulty = difficulty
        self.opponent_elo = opponent_elo
        self.opponent_name = opponent_name
        self.site = site
        self.move_history: list[chess.Move] = []
        self.san_history: list[str] = []
        self.result: str | None = None

    def make_move(self, san: str) -> chess.Move:
        move = self.board.parse_san(san)
        san_str = self.board.san(move)
        self.board.push(move)
        self.move_history.append(move)
        self.san_history.append(san_str)
        return move

    def make_engine_move(self, move: chess.Move) -> None:
        san_str = self.board.san(move)
        self.board.push(move)
        self.move_history.append(move)
        self.san_history.append(san_str)

    def undo_move(self) -> int:
        """Undo moves. Returns number of moves undone."""
        if not self.move_history:
            return 0
        count = 0
        # If it's the player's turn, undo 2 (engine + player)
        # If it's the engine's turn, undo 1 (player's last move)
        target = 2 if self.is_player_turn() and len(self.move_history) >= 2 else 1
        for _ in range(min(target, len(self.move_history))):
            self.board.pop()
            self.move_history.pop()
            self.san_history.pop()
            count += 1
        return count

    def is_player_turn(self) -> bool:
        return self.board.turn == self.player_color

    def get_game_over_message(self) -> str | None:
        outcome = self.board.outcome()
        if outcome is None:
            return None

        termination = outcome.termination
        winner = outcome.winner

        if termination == chess.Termination.CHECKMATE:
            if winner == self.player_color:
                self.result = "1-0" if self.player_color == chess.WHITE else "0-1"
                return "Checkmate! You win!"
            else:
                self.result = "0-1" if self.player_color == chess.WHITE else "1-0"
                return f"Checkmate! {self.opponent_name} wins."
        elif termination == chess.Termination.STALEMATE:
            self.result = "1/2-1/2"
            return "Stalemate! Draw."
        elif termination == chess.Termination.INSUFFICIENT_MATERIAL:
            self.result = "1/2-1/2"
            return "Draw by insufficient material."
        elif termination == chess.Termination.FIFTY_MOVES:
            self.result = "1/2-1/2"
            return "Draw by fifty-move rule."
        elif termination == chess.Termination.THREEFOLD_REPETITION:
            self.result = "1/2-1/2"
            return "Draw by threefold repetition."
        else:
            self.result = "1/2-1/2"
            return "Game over. Draw."

    def format_move_list(self) -> str:
        parts: list[str] = []
        for i, san in enumerate(self.san_history):
            if i % 2 == 0:
                parts.append(f"{i // 2 + 1}. {san}")
            else:
                parts[-1] += f" {san}"
        return "  ".join(parts)

    def to_pgn(self) -> chess.pgn.Game:
        game = chess.pgn.Game()
        game.headers["Event"] = "Chess CLI Game"
        game.headers["Site"] = self.site
        game.headers["Date"] = datetime.now().strftime("%Y.%m.%d")

        elo_str = f" ({self.opponent_elo})" if self.opponent_elo else ""
        opp_label = f"{self.opponent_name}{elo_str}"
        if self.player_color == chess.WHITE:
            game.headers["White"] = "Human"
            game.headers["Black"] = opp_label
        else:
            game.headers["White"] = opp_label
            game.headers["Black"] = "Human"

        game.headers["Result"] = self.result or "*"

        node = game
        for move in self.move_history:
            node = node.add_variation(move)

        return game

    def save_pgn(self, filepath: str) -> None:
        pgn_game = self.to_pgn()
        with open(filepath, "w") as f:
            exporter = chess.pgn.FileExporter(f)
            pgn_game.accept(exporter)
