from functools import cached_property

import chess

from .utils import get_piece_value


class MoveContext:
    """
    Контекст хода для детекторов.

    Хранит состояние доски до и после хода.
    Вычисления кэшируются — каждое свойство считается один раз.
    """

    def __init__(self, board: chess.Board, move: chess.Move):
        self.board_before = board.copy()
        self.move = move
        self.board_after = board.copy()
        self.board_after.push(move)

        # Кто сделал ход
        self.moving_side = board.turn
        self.opponent_side = not board.turn

    # === Шахи ===

    @cached_property
    def is_check(self) -> bool:
        """Шах ПОСЛЕ хода."""
        return self.board_after.is_check()

    # === Атаки на короля ===

    @cached_property
    def king_square(self) -> chess.Square:
        """Позиция короля противника (который под шахом)."""
        return self.board_after.king(self.opponent_side)

    @cached_property
    def attackers_on_king(self) -> chess.SquareSet:
        """Фигуры, атакующие короля противника ПОСЛЕ хода."""
        return self.board_after.attackers(self.moving_side, self.king_square)

    @cached_property
    def attackers_count(self) -> int:
        """Количество фигур, атакующих короля."""
        return len(self.attackers_on_king)

    # === Ходившая фигура ===

    @cached_property
    def moved_piece(self) -> chess.Piece | None:
        """Фигура, которая сделала ход."""
        return self.board_after.piece_at(self.move.to_square)

    @cached_property
    def piece_attacks(self) -> chess.SquareSet:
        """Поля, которые атакует ходившая фигура ПОСЛЕ хода."""
        return self.board_after.attacks(self.move.to_square)

    # === Взятия ===

    @cached_property
    def captured_piece(self) -> chess.Piece | None:
        """Взятая фигура (если было взятие)."""
        # En passant: пешка берёт на пустое поле, взятая пешка стоит рядом
        if self.board_before.is_en_passant(self.move):
            return chess.Piece(chess.PAWN, self.opponent_side)
        return self.board_before.piece_at(self.move.to_square)

    @cached_property
    def is_capture(self) -> bool:
        """Было ли взятие."""
        return self.captured_piece is not None

    # === Защита и висячие фигуры ===

    @cached_property
    def moved_piece_is_attacked(self) -> bool:
        """Атакована ли наша фигура после хода."""
        return self.board_after.is_attacked_by(self.opponent_side, self.move.to_square)

    @cached_property
    def moved_piece_is_defended(self) -> bool:
        """Защищена ли наша фигура после хода."""
        return self.board_after.is_attacked_by(self.moving_side, self.move.to_square)

    @cached_property
    def moved_piece_is_hanging(self) -> bool:
        """Висит ли наша фигура (под боем и не защищена)."""
        return self.moved_piece_is_attacked and not self.moved_piece_is_defended

    # === Утилиты ===

    def get_piece_value_at(self, square: chess.Square) -> int:
        """Получить ценность фигуры на поле."""
        piece = self.board_after.piece_at(square)
        return get_piece_value(piece.piece_type) if piece else 0

    def is_square_defended(self, square: chess.Square) -> bool:
        """Защищено ли поле противником."""
        return self.board_after.is_attacked_by(self.opponent_side, square)
