import chess

from ..base import BaseDetector
from ...context import MoveContext
from ...i18n import format_piece_square
from ...utils import get_piece_value


class RemovingDefenderMaterialDetector(BaseDetector):
    """
    Детектор уничтожения защитника для выигрыша материала.

    Взятие фигуры, которая защищала другую фигуру.
    Теперь та фигура висит или можно взять с выгодой.
    """

    key = "removing_defender_material"

    def __init__(self):
        self._exposed_sq: int | None = None

    def detect(self, ctx: MoveContext) -> bool:
        self._exposed_sq = None

        if not ctx.is_capture:
            return False

        captured_sq = ctx.move.to_square

        # Какие поля защищала взятая фигура?
        defended_squares = ctx.board_before.attacks(captured_sq)

        for target_sq in defended_squares:
            target = ctx.board_after.piece_at(target_sq)

            # Должна быть фигура противника
            if target is None:
                continue
            if target.color != ctx.opponent_side:
                continue

            # Пешки и короли не считаются
            if target.piece_type in {chess.PAWN, chess.KING}:
                continue

            target_value = get_piece_value(target.piece_type)

            # Атакуем ли мы эту фигуру?
            our_attackers = ctx.board_after.attackers(ctx.moving_side, target_sq)

            # Если наша берущая фигура под боем — её отберут,
            # и она не успеет атаковать оставшуюся цель
            if ctx.moved_piece_is_attacked and captured_sq in our_attackers:
                our_attackers = our_attackers & ~chess.SquareSet([captured_sq])

            if not our_attackers:
                continue

            # Защищена ли она теперь?
            their_defenders = ctx.board_after.attackers(ctx.opponent_side, target_sq)

            # Теперь не защищена — висит!
            if not their_defenders:
                self._exposed_sq = target_sq
                return True

            # Можем взять более дешёвой фигурой?
            min_attacker_value = 100
            for att_sq in our_attackers:
                attacker = ctx.board_after.piece_at(att_sq)
                if attacker and attacker.piece_type != chess.KING:
                    min_attacker_value = min(
                        min_attacker_value,
                        get_piece_value(attacker.piece_type)
                    )

            if min_attacker_value < target_value:
                self._exposed_sq = target_sq
                return True

        return False

    def format_details(self, ctx: MoveContext, lang: str) -> str:
        if self._exposed_sq is None:
            return ""
        captured = ctx.captured_piece
        exposed = ctx.board_after.piece_at(self._exposed_sq)
        if not captured or not exposed:
            return ""
        c = format_piece_square(captured, ctx.move.to_square, lang)
        e = format_piece_square(exposed, self._exposed_sq, lang)
        return f"{c} \u2192 {e}"

    def get_arrows(self, ctx: MoveContext) -> list[tuple[int, int]]:
        if self._exposed_sq is None:
            return []
        return [
            (ctx.move.to_square, self._exposed_sq),
            (self._exposed_sq, self._exposed_sq),
        ]


class RemovingDefenderMateDetector(BaseDetector):
    """
    Детектор уничтожения защитника для мата.

    Взятие фигуры, которая защищала ключевое поле.
    Теперь появляется угроза мата в 1 ход.
    """

    key = "removing_defender_mate"

    def __init__(self):
        self._mate_move: chess.Move | None = None

    def detect(self, ctx: MoveContext) -> bool:
        self._mate_move = None

        if not ctx.is_capture:
            return False

        # Если ход уже мат — это не "угроза мата", а просто мат
        if ctx.board_after.is_checkmate():
            return False

        if ctx.is_check:
            # При шахе проверяем: мат сохраняется после КАЖДОГО ответа?
            return self._mate_survives_check(ctx)

        # Без шаха — стандартная проверка (мат в 1 при свободном ходе)
        mate_move = self._find_mate_in_one(ctx.board_after, ctx.moving_side)
        if mate_move:
            self._mate_move = mate_move
            return True
        return False

    def _mate_survives_check(self, ctx: MoveContext) -> bool:
        """При шахе: мат в 1 после КАЖДОГО ответа противника?"""
        board = ctx.board_after.copy()
        for response in list(board.legal_moves):
            board.push(response)
            mate_move = self._find_mate_in_one(board, ctx.moving_side)
            board.pop()
            if not mate_move:
                return False  # Есть ответ, убирающий угрозу мата
            if self._mate_move is None:
                self._mate_move = mate_move
        return True

    def _find_mate_in_one(self, board: chess.Board, side: chess.Color) -> chess.Move | None:
        """Находит мат в 1 ход для стороны side."""
        original_turn = board.turn
        board.turn = side
        result = None
        for move in board.legal_moves:
            board.push(move)
            if board.is_checkmate():
                board.pop()
                result = move
                break
            board.pop()
        board.turn = original_turn
        return result

    def format_details(self, ctx: MoveContext, lang: str) -> str:
        captured = ctx.captured_piece
        if not captured:
            return ""
        return format_piece_square(captured, ctx.move.to_square, lang)

    def get_arrows(self, ctx: MoveContext) -> list[tuple[int, int]]:
        if self._mate_move is None:
            return []
        return [
            (self._mate_move.from_square, self._mate_move.to_square),
            (self._mate_move.to_square, self._mate_move.to_square),
        ]
