import chess

from ..base import BaseDetector
from ...context import MoveContext
from ...i18n import format_piece_square
from ...utils import get_piece_value, is_piece_vulnerable, find_relative_pin


class ExploitingPinDetector(BaseDetector):
    """
    Детектор использования связки.

    Четыре типа:
    1. Атака на связанную фигуру — фигура не может убежать
       (d3 нападает на связанного коня e4 — конь не может уйти)
    2. Взятие через связанного защитника — защитник не может отбить
       (Rxh6 берёт коня — пешка g7 связана и не может взять обратно)
    3. Ход на поле, контролируемое связанной пешкой — пешка не может взять
       (f6 — пешка g7 связана по вертикали g и не может взять gxf6)
    4. Атака на фигуру в относительной связке — фигура не может уйти,
       иначе откроет более ценную за ней
       (Bb5 атакует Nd7, связанного Qd3→Nd7→Qd8 — конь не может уйти)
    """

    key = "exploiting_pin"

    def __init__(self):
        self._detail_piece: chess.Piece | None = None
        self._detail_sq: int | None = None

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

        piece = ctx.moved_piece
        if piece is None:
            return False

        board = ctx.board_after
        to_sq = ctx.move.to_square

        # Тип 2: Взятие через связанного защитника
        if ctx.is_capture:
            if self._check_capture_through_pin(ctx, board, to_sq):
                return True

        # Тип 1: Атака на связанную фигуру
        if self._check_attack_on_pinned(ctx, board, to_sq, piece):
            return True

        # Тип 3: Ход на поле связанной пешки
        if self._check_advance_through_pinned_pawn(ctx, board, to_sq):
            return True

        # Тип 4: Атака на фигуру в относительной связке
        if self._check_attack_on_relative_pinned(ctx, board, to_sq, piece):
            return True

        return False

    def _check_capture_through_pin(self, ctx, board, to_sq):
        """Взятие, где все защитники связаны и не могут отбить."""
        captured = ctx.captured_piece
        if captured is None or captured.piece_type in {chess.PAWN, chess.KING}:
            return False

        # Есть ли у противника атакующие наше поле?
        attackers = board.attackers(ctx.opponent_side, to_sq)
        if not attackers:
            return False  # Нет защитников — обычное взятие висящей

        # ВСЕ атакующие должны быть связаны
        pinned_attackers = []
        for att_sq in attackers:
            if not board.is_pinned(ctx.opponent_side, att_sq):
                return False  # Есть несвязанный защитник
            # Связка должна была существовать ДО нашего хода
            if not ctx.board_before.is_pinned(ctx.opponent_side, att_sq):
                return False  # Связка создана нашим ходом
            pinned_attackers.append(att_sq)

        # Есть хотя бы один связанный "защитник"
        self._detail_piece = captured
        self._detail_sq = to_sq
        return True

    def _check_attack_on_pinned(self, ctx, board, to_sq, piece):
        """Атака на связанную фигуру, которая не может убежать."""
        attacker_value = get_piece_value(piece.piece_type)

        for target_sq in ctx.piece_attacks:
            target = board.piece_at(target_sq)
            if target is None or target.color == ctx.moving_side:
                continue
            if target.piece_type in {chess.PAWN, chess.KING}:
                continue

            # Фигура должна быть связана
            if not board.is_pinned(ctx.opponent_side, target_sq):
                continue

            # Связка должна была существовать ДО нашего хода
            if not ctx.board_before.is_pinned(ctx.opponent_side, target_sq):
                continue

            target_value = get_piece_value(target.piece_type)

            # Наш атакующий дешевле — выгодная угроза
            if attacker_value >= target_value:
                continue

            # Связанная фигура не может убежать на безопасное поле
            has_safe_move = False
            for m in board.generate_legal_moves(
                from_mask=chess.BB_SQUARES[target_sq]
            ):
                board.push(m)
                safe = not is_piece_vulnerable(board, m.to_square)
                board.pop()
                if safe:
                    has_safe_move = True
                    break

            if has_safe_move:
                continue

            self._detail_piece = target
            self._detail_sq = target_sq
            return True

        return False

    def _check_advance_through_pinned_pawn(self, ctx, board, to_sq):
        """Ход на поле, контролируемое связанной пешкой (пешка не может взять)."""
        # Вражеские пешки, которые атакуют наше поле
        pawn_attackers = board.attackers(ctx.opponent_side, to_sq) & board.pawns
        for pawn_sq in pawn_attackers:
            if not board.is_pinned(ctx.opponent_side, pawn_sq):
                continue

            # Связка должна была существовать ДО нашего хода
            if not ctx.board_before.is_pinned(ctx.opponent_side, pawn_sq):
                continue

            # Пешка не может легально взять на наше поле
            if chess.Move(pawn_sq, to_sq) in board.legal_moves:
                continue

            self._detail_piece = board.piece_at(pawn_sq)
            self._detail_sq = pawn_sq
            return True

        return False

    def _check_attack_on_relative_pinned(self, ctx, board, to_sq, piece):
        """Тип 4: Атака на фигуру в относительной связке (не абсолютной)."""
        if ctx.moved_piece_is_hanging:
            return False

        attacker_value = get_piece_value(piece.piece_type)

        for target_sq in ctx.piece_attacks:
            target = board.piece_at(target_sq)
            if target is None or target.color == ctx.moving_side:
                continue
            if target.piece_type in {chess.PAWN, chess.KING}:
                continue

            # Абсолютно связана — обрабатывается Типом 1
            if board.is_pinned(ctx.opponent_side, target_sq):
                continue

            target_value = get_piece_value(target.piece_type)

            # Атакующий не дороже цели
            if attacker_value > target_value:
                continue

            # Ищем относительную связку от ДРУГОЙ нашей фигуры
            rel_pin = find_relative_pin(
                board, target_sq, ctx.moving_side, exclude_sq=to_sq
            )
            if rel_pin is None:
                continue

            # Связка должна была существовать ДО нашего хода
            rel_pin_before = find_relative_pin(
                ctx.board_before, target_sq, ctx.moving_side
            )
            if rel_pin_before is None:
                continue

            # Цель не может безопасно уйти
            _, behind_sq, _ = rel_pin
            has_safe_escape = False
            for m in board.generate_legal_moves(
                from_mask=chess.BB_SQUARES[target_sq]
            ):
                board.push(m)
                behind_exposed = board.is_attacked_by(ctx.moving_side, behind_sq)
                target_safe = not is_piece_vulnerable(board, m.to_square)
                board.pop()
                if not behind_exposed and target_safe:
                    has_safe_escape = True
                    break

            if has_safe_escape:
                continue

            self._detail_piece = target
            self._detail_sq = target_sq
            return True

        return False

    def format_details(self, ctx: MoveContext, lang: str) -> str:
        if self._detail_piece is None or self._detail_sq is None:
            return ""
        return format_piece_square(self._detail_piece, self._detail_sq, lang)

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