import chess

from ..base import BaseDetector
from ...context import MoveContext
from ...i18n import format_piece_square
from ...utils import cast_ray, get_piece_value, RAY_PIECE_TYPES, BISHOP_DIRECTIONS, ROOK_DIRECTIONS, QUEEN_DIRECTIONS, is_piece_vulnerable


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

    Копьё — обратная связка. Фигура атакует ценную фигуру (или короля),
    за которой стоит менее ценная. Первая фигура вынуждена уйти,
    и мы забираем вторую.

    Условия:
    - Первая фигура = король (обязан уйти) ИЛИ
    - Первая фигура дороже второй (выгоднее уйти)
    """

    key = "skewer"

    def __init__(self):
        self._front_sq: int | None = None
        self._back_sq: int | None = None

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

        piece = ctx.moved_piece

        if piece is None:
            return False

        # Только лучевые фигуры могут создавать копьё
        if piece.piece_type not in RAY_PIECE_TYPES:
            return False

        to_sq = ctx.move.to_square
        defender_color = ctx.opponent_side
        skewer_value = get_piece_value(piece.piece_type)

        # Выбираем направления по типу фигуры
        if piece.piece_type == chess.BISHOP:
            directions = BISHOP_DIRECTIONS
        elif piece.piece_type == chess.ROOK:
            directions = ROOK_DIRECTIONS
        else:  # QUEEN
            directions = QUEEN_DIRECTIONS

        # Проверяем каждое направление
        for direction in directions:
            pieces_on_ray = cast_ray(ctx.board_after, to_sq, direction, limit=2)

            # Нужно минимум 2 фигуры на луче
            if len(pieces_on_ray) < 2:
                continue

            front_sq, front_piece = pieces_on_ray[0]
            back_sq, back_piece = pieces_on_ray[1]

            # Обе фигуры должны быть противника
            if front_piece.color != defender_color:
                continue
            if back_piece.color != defender_color:
                continue

            front_value = ctx.get_piece_value_at(front_sq)
            back_value = ctx.get_piece_value_at(back_sq)

            # Копьё на короля — король обязан уйти (шах)
            is_royal_skewer = front_piece.piece_type == chess.KING

            if is_royal_skewer:
                # Если нашу фигуру выгодно забрать — копьё не работает
                if is_piece_vulnerable(ctx.board_after, to_sq):
                    continue

                # Задняя фигура должна быть выгодно взятой после ухода короля
                if skewer_value >= back_value:
                    back_defenders = ctx.board_after.attackers(defender_color, back_sq)
                    if back_defenders:
                        continue

                self._front_sq = front_sq
                self._back_sq = back_sq
                return True

            # Копьё без шаха — первая фигура дороже или равна и уязвима
            is_equal_and_vulnerable = (
                front_value == back_value
                and is_piece_vulnerable(ctx.board_after, front_sq)
            )
            if front_value > back_value or is_equal_and_vulnerable:
                # Проверяем: если наша фигура висит — противник может взять её
                if ctx.moved_piece_is_hanging:
                    continue

                # Задняя фигура должна быть выгодно взятой после ухода передней
                if skewer_value >= back_value:
                    back_defenders = ctx.board_after.attackers(defender_color, back_sq)
                    if back_defenders:
                        continue  # Заднюю фигуру не взять выгодно

                # Можно ли заблокировать луч (поставить фигуру между)?
                between = self._get_between_squares(to_sq, front_sq, direction)
                if between:
                    between_set = set(between)
                    can_block = any(
                        m.to_square in between_set and m.from_square != front_sq
                        for m in ctx.board_after.legal_moves
                    )
                    if can_block:
                        continue

                self._front_sq = front_sq
                self._back_sq = back_sq
                return True

        return False

    def format_details(self, ctx: MoveContext, lang: str) -> str:
        if self._front_sq is None or self._back_sq is None:
            return ""
        attacker = ctx.board_after.piece_at(ctx.move.to_square)
        front = ctx.board_after.piece_at(self._front_sq)
        back = ctx.board_after.piece_at(self._back_sq)
        if not attacker or not front or not back:
            return ""
        a = format_piece_square(attacker, ctx.move.to_square, lang)
        f = format_piece_square(front, self._front_sq, lang)
        b = format_piece_square(back, self._back_sq, lang)
        return f"{a} \u2192 {f}, {b}"

    def get_arrows(self, ctx: MoveContext) -> list[tuple[int, int]]:
        if self._front_sq is None or self._back_sq is None:
            return []
        to_sq = ctx.move.to_square
        return [
            (to_sq, self._front_sq),
            (self._front_sq, self._front_sq),
            (to_sq, self._back_sq),
            (self._back_sq, self._back_sq),
        ]

    @staticmethod
    def _get_between_squares(start_sq: int, end_sq: int, direction: tuple) -> list:
        """Пустые клетки между start_sq и end_sq на луче."""
        squares = []
        rank = chess.square_rank(start_sq)
        file = chess.square_file(start_sq)
        dr, df = direction

        rank += dr
        file += df

        while 0 <= rank < 8 and 0 <= file < 8:
            sq = chess.square(file, rank)
            if sq == end_sq:
                break
            squares.append(sq)
            rank += dr
            file += df

        return squares
