from __future__ import annotations

import chess
import chess.engine


class EngineNotFoundError(Exception):
    """Raised when Stockfish is not found on PATH."""

DIFFICULTY_PRESETS: dict[str, int | None] = {
    "newcomer": 1320,
    "beginner": 1400,
    "novice": 1500,
    "amateur": 1600,
    "casual": 1700,
    "intermediate": 1800,
    "proficient": 1900,
    "skilled": 2000,
    "advanced": 2100,
    "strong": 2200,
    "expert": 2300,
    "candidate": 2400,
    "master": 2500,
    "veteran": 2600,
    "grandmaster": 2700,
    "champion": 2800,
    "elite": 2900,
    "legendary": 3000,
    "supreme": 3100,
    "maximum": None,
}


class EnginePlayer:
    def __init__(
        self,
        difficulty: str = "intermediate",
        stockfish_path: str = "stockfish",
        threads: int = 1,
        hash_mb: int = 16,
        move_time: float | None = None,
    ) -> None:
        self.difficulty = difficulty
        self.elo = DIFFICULTY_PRESETS[difficulty]
        self.move_time = move_time

        try:
            self.engine = chess.engine.SimpleEngine.popen_uci(stockfish_path)
        except FileNotFoundError:
            raise EngineNotFoundError(
                "Stockfish engine not found on PATH.\n"
                "\n"
                "Install Stockfish:\n"
                "  macOS:   brew install stockfish\n"
                "  Ubuntu:  sudo apt install stockfish\n"
                "  Windows: Download from https://stockfishchess.org/download/"
            )

        self.engine.configure({"Threads": threads, "Hash": hash_mb})

        if self.elo is not None:
            self.engine.configure({
                "UCI_LimitStrength": True,
                "UCI_Elo": self.elo,
            })
        else:
            self.engine.configure({
                "UCI_LimitStrength": False,
            })

    def get_move(self, board: chess.Board) -> chess.Move:
        if self.move_time is not None:
            time_limit = self.move_time
        else:
            time_limit = 2.0 if self.difficulty == "maximum" else 1.0
        result = self.engine.play(board, chess.engine.Limit(time=time_limit))
        return result.move

    def get_evaluation(self, board: chess.Board) -> tuple[float, bool, int | None]:
        """Returns (eval_in_pawns, is_mate, mate_in) from White's perspective."""
        info = self.engine.analyse(board, chess.engine.Limit(time=0.5))
        score = info["score"].white()

        if score.is_mate():
            mate_in = score.mate()
            # Convert to pawns equivalent for the eval bar
            eval_pawns = 10.0 if mate_in > 0 else -10.0
            return eval_pawns, True, mate_in
        else:
            cp = score.score(mate_score=10000)
            return cp / 100.0, False, None

    def set_full_strength(self) -> None:
        self.engine.configure({
            "UCI_LimitStrength": False,
        })

    def restore_difficulty(self) -> None:
        if self.elo is not None:
            self.engine.configure({
                "UCI_LimitStrength": True,
                "UCI_Elo": self.elo,
            })

    def quit(self) -> None:
        try:
            self.engine.quit()
        except chess.engine.EngineTerminatedError:
            pass
