import chess

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


class ForkDetector(BaseDetector):
    """
    Детектор вилки.

    Вилка — фигура атакует 2+ ценных целей одновременно.

    Цель считается ценной если:
    1. Король (всегда)
    2. Фигура дороже атакующей (выгодное взятие даже с защитой)
    3. Фигура равной ценности, но не защищена (бесплатно)
    4. Фигура дешевле, но висит (бесплатно)
    """

    key = "fork"

    def __init__(self):
        self._target_squares: list[chess.Square] = []

    def detect(self, ctx: MoveContext) -> bool:
        self._target_squares = []

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

        attacker_value = get_piece_value(piece.piece_type)
        attacked_valuable = []
        has_king_attack = False

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

            if target is None:
                continue

            # Только фигуры противника
            if target.color == ctx.moving_side:
                continue

            target_value = ctx.get_piece_value_at(target_sq)

            # 1. Король — всегда ценная цель
            if target.piece_type == chess.KING:
                attacked_valuable.append(target_sq)
                has_king_attack = True

            # 2. Фигура (не пешка) дороже атакующей — выгодное взятие
            elif target.piece_type != chess.PAWN and target_value > attacker_value:
                attacked_valuable.append(target_sq)

            # 3. Фигура равной ценности, но не защищена — бесплатно заберём
            elif target.piece_type != chess.PAWN and target_value == attacker_value and not ctx.is_square_defended(target_sq):
                attacked_valuable.append(target_sq)

            # 4. Фигура дешевле, но висит — бесплатно заберём
            elif not ctx.is_square_defended(target_sq):
                attacked_valuable.append(target_sq)

        # Нужно 2+ ценных цели
        if len(attacked_valuable) < 2:
            return False

        # Если вилка без шаха и наша фигура висит — противник просто возьмёт её
        if not has_king_attack and ctx.moved_piece_is_hanging:
            return False

        # Может ли какая-то вражеская фигура ЛЕГАЛЬНО взять наш форкер?
        # Используем generate_legal_moves, а не attackers() —
        # attackers() не учитывает связки (связанная фигура не может взять).
        to_sq = ctx.move.to_square
        for capture_move in ctx.board_after.generate_legal_moves(
            to_mask=chess.BB_SQUARES[to_sq]
        ):
            att = ctx.board_after.piece_at(capture_move.from_square)
            if att is None or att.piece_type == chess.KING:
                continue

            att_value = get_piece_value(att.piece_type)

            # Вилка не защищена — берут бесплатно
            if not ctx.moved_piece_is_defended:
                return False

            # Защищена, но размен равный/выгодный для противника
            if att_value <= attacker_value:
                return False

        # Для не-шаховых вилок: может ли ценная цель уйти И защитить дешёвую?
        # (например, Nc4 атакует Rd2 и e3, но Rd2→d3 уходит и защищает e3)
        # Проверяем только если взятие дешёвой цели НЕ выгодно при защите:
        # если форкер дешевле цели, взятие выгодно даже при отбитии (пешка берёт коня).
        if not has_king_attack:
            cheapest_sq = min(
                attacked_valuable,
                key=lambda sq: ctx.get_piece_value_at(sq),
            )
            cheapest_value = ctx.get_piece_value_at(cheapest_sq)

            if cheapest_value <= attacker_value:
                for escape_sq in attacked_valuable:
                    if escape_sq == cheapest_sq:
                        continue
                    for move in ctx.board_after.generate_legal_moves(
                        from_mask=chess.BB_SQUARES[escape_sq]
                    ):
                        ctx.board_after.push(move)
                        defended = ctx.board_after.is_attacked_by(
                            ctx.opponent_side, cheapest_sq
                        )
                        ctx.board_after.pop()
                        if defended:
                            return False

        self._target_squares = attacked_valuable
        return True

    def format_details(self, ctx: MoveContext, lang: str) -> str:
        if not self._target_squares:
            return ""
        forker = ctx.board_after.piece_at(ctx.move.to_square)
        if not forker:
            return ""
        forker_str = format_piece_square(forker, ctx.move.to_square, lang)
        targets = []
        for sq in self._target_squares:
            piece = ctx.board_after.piece_at(sq)
            if piece:
                targets.append(format_piece_square(piece, sq, lang))
        return f"{forker_str} \u2192 {', '.join(targets)}"

    def get_arrows(self, ctx: MoveContext) -> list[tuple[int, int]]:
        result = []
        to_sq = ctx.move.to_square
        for sq in self._target_squares:
            result.append((to_sq, sq))
            result.append((sq, sq))
        return result
