import chess

from chess_detect import ChessDetector
from chess_detect.context import MoveContext
from chess_detect.detectors.tactics.pin import PinDetector


class TestPinDetector:
    """Тесты для детектора связки."""

    def test_absolute_pin_bishop_to_king(self):
        """Абсолютная связка: слон связывает коня на короля."""
        # Слон идёт на c4, связывая коня e6 на короля g8
        board = chess.Board("6k1/8/4n3/8/8/8/2B5/4K3 w - - 0 1")
        move = chess.Move.from_uci("c2c4")  # Нет, слон на c2 не атакует e6-g8

        # Правильная позиция: слон на b3 идёт на c4
        # Диагональ c4-e6-g8
        board = chess.Board("6k1/8/4n3/8/8/1B6/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("b3c4")  # Bc4 связывает коня e6 на короля g8

        ctx = MoveContext(board, move)
        detector = PinDetector()

        assert detector.detect(ctx) is True

    def test_absolute_pin_rook_to_king(self):
        """Абсолютная связка: ладья связывает коня на короля."""
        # Ладья на a1 идёт на a4, связывая коня a6 на короля a8
        # Конь НЕ может взять ладью на a4 (не L-ход)
        board = chess.Board("k7/8/n7/8/8/8/8/R3K3 w - - 0 1")
        move = chess.Move.from_uci("a1a4")  # Ra4 связывает коня a6 на короля a8

        ctx = MoveContext(board, move)
        detector = PinDetector()

        assert detector.detect(ctx) is True

    def test_relative_pin_more_valuable(self):
        """Относительная связка: слон связывает коня на ферзя."""
        # Слон связывает коня на ферзя (ферзь дороже коня)
        board = chess.Board("8/6q1/4n3/8/8/1B6/8/4K2k w - - 0 1")
        move = chess.Move.from_uci("b3c4")  # Bc4 связывает коня e6 на ферзя g8

        # Диагональ c4-e6-g8? Нет, g8 это не на диагонали c4
        # c4 -> d5 -> e6 -> f7 -> g8 — да!
        # Но ферзь на g7, не g8
        # c4 -> e6 -> g8 — нет, это не диагональ

        # Правильная диагональ: c4-d5-e6-f7-g8
        # Или a2-b3-c4-d5-e6-f7-g8

        # Позиция: слон b3, конь d5, ферзь f7
        board = chess.Board("7k/5q2/8/3n4/8/1B6/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("b3c4")  # Bc4 связывает коня d5 на ферзя f7?

        # c4 атакует d5? Слон с c4 атакует: a2, a6, b3, b5, d3, d5, e2, e6, f1, f7...
        # d5 — да! f7 — да!
        # Но между c4 и f7 есть d5 и e6. Нужно чтобы конь был первым.

        # Проще: слон a1, конь c3, ферзь e5
        board = chess.Board("7k/8/8/4q3/8/2n5/8/B3K3 w - - 0 1")
        # Слон a1 уже атакует диагональ a1-h8
        # a1 -> b2 -> c3 -> d4 -> e5
        # Конь c3, ферзь e5

        # Но нам нужен ХОД который создаёт связку
        # Слон с b2 идёт на a1
        board = chess.Board("7k/8/8/4q3/8/2n5/1B6/4K3 w - - 0 1")
        move = chess.Move.from_uci("b2a1")  # Ba1 связывает коня c3 на ферзя e5

        ctx = MoveContext(board, move)
        detector = PinDetector()

        assert detector.detect(ctx) is True

    def test_no_pin_second_less_valuable(self):
        """Нет связки: вторая фигура дешевле первой."""
        # Слон "связывает" ферзя на коня — не связка (ферзь дороже)
        board = chess.Board("7k/8/8/4n3/8/2q5/1B6/4K3 w - - 0 1")
        move = chess.Move.from_uci("b2a1")  # Ba1 — ферзь c3 первый, конь e5 второй

        # Ферзь (9) > Конь (3) — не связка, ферзь может уйти

        ctx = MoveContext(board, move)
        detector = PinDetector()

        assert detector.detect(ctx) is False

    def test_no_pin_defended_same_value(self):
        """Нет связки: фигура сзади защищена и атакующий не дешевле."""
        # Ферзь "связывает" коня на ферзя, но ферзь сзади защищён ладьями
        # Ферзь(9) >= Ферзь(9) — невыгодно пробивать
        board = chess.Board("3r3r/pppk1pp1/3pq1np/PP1Pp3/4P3/3P1NPb/5PBP/R2Q1RK1 b - - 0 19")
        move = chess.Move.from_uci("e6g4")  # Qg4 — ферзь "связывает" Nf3 на Qd1

        ctx = MoveContext(board, move)
        detector = PinDetector()

        # Ферзь d1 защищён ладьями a1 и f1, атакующий = ферзь (9) = ферзь (9)
        assert detector.detect(ctx) is False

    def test_relative_pin_attacker_cheaper_than_second(self):
        """Связка работает: атакующий дешевле защищённой фигуры сзади."""
        # Слон связывает коня на ферзя. Ферзь защищён, но слон (3) < ферзь (9)
        board = chess.Board("7k/5qr1/8/3n4/8/1B6/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("b3c4")  # Bc4 связывает коня d5 на ферзь f7 (защищён ладьёй g7)

        ctx = MoveContext(board, move)
        detector = PinDetector()

        # Слон (3) < Ферзь (9) — выгодно даже если ферзь защищён
        assert detector.detect(ctx) is True

    def test_no_pin_pinned_can_capture_pinner(self):
        """Нет связки: связанная фигура может взять связывающую без потерь."""
        # Слон h3 "связывает" слона g2 на ладью f1, но Bxh3 — равный размен
        board = chess.Board("2kr3r/pppq1pp1/2np1n1p/4p3/1PP1P3/P2P1NPb/4NPBP/R2Q1RK1 w - - 1 14")
        # Это позиция ПОСЛЕ хода 13...Bh3, но нам нужно создать контекст хода Bh3
        # Позиция ДО хода Bh3: слон был где-то (допустим g4), но проще — проверим напрямую
        # Используем позицию до хода, ход слоном на h3
        board_before = chess.Board("2kr3r/pppq1pp1/2np1n1p/4p3/1PP1P3/P2P1NP1/4NPBb/R2Q1RK1 b - - 0 13")
        move = chess.Move.from_uci("h2h3")  # Bh3

        ctx = MoveContext(board_before, move)
        detector = PinDetector()

        # Слон g2 может просто взять слона h3 (Bxh3) — равный размен, связка не работает
        assert detector.detect(ctx) is False

    def test_no_absolute_pin_if_pinned_captures_pinner(self):
        """Нет абсолютной связки: связанная фигура может выгодно взять связывающую."""
        # Bxc3 — слон на c3, пешка d2, король e1
        # Пешка d2 может взять слона c3 (dxc3) — пешка (1) берёт слона (3)
        board = chess.Board("r1bqk1nr/pppp1ppp/2n5/4p3/1b2P3/P1N2N2/1PPP1PPP/R1BQKB1R b KQkq - 0 4")
        move = chess.Move.from_uci("b4c3")  # Bxc3

        ctx = MoveContext(board, move)
        detector = PinDetector()

        # Пешка d2 берёт слона c3 выгодно — связка не работает
        assert detector.detect(ctx) is False

    def test_no_pin_own_piece_in_between(self):
        """Нет связки: своя фигура на луче."""
        # Между слоном и вражеским конём стоит своя пешка
        board = chess.Board("6k1/8/4n3/3P4/8/1B6/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("b3c4")  # Bc4 — но d5 занята своей пешкой

        ctx = MoveContext(board, move)
        detector = PinDetector()

        assert detector.detect(ctx) is False

    def test_no_pin_knight_cannot_pin(self):
        """Конь не может создавать связку."""
        board = chess.Board("6k1/8/4n3/8/3N4/8/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("d4e6")  # Конь берёт коня — не связка

        ctx = MoveContext(board, move)
        detector = PinDetector()

        assert detector.detect(ctx) is False

    def test_queen_pin(self):
        """Ферзь создаёт связку."""
        # Ферзь связывает ладью на короля по диагонали
        # Ладья НЕ может взять ферзя (не ходит по диагонали)
        board = chess.Board("7k/6r1/8/8/8/8/1Q6/4K3 w - - 0 1")
        move = chess.Move.from_uci("b2a1")  # Qa1 связывает ладью g7 на короля h8

        ctx = MoveContext(board, move)
        detector = PinDetector()

        assert detector.detect(ctx) is True


    def test_no_equal_pin_when_relative_pinned(self):
        """Bb5→Nd7→Ne8 не связка, т.к. Nd7 уже в относ. связке по d-вертикали."""
        board = chess.Board(
            "r1bqnrk1/p2n1pbp/1p2p1p1/2p1P3/2B2P2/2NQB2P/PPP1N1P1/3R1RK1 w - - 0 1"
        )
        move = chess.Move.from_uci("c4b5")  # Bb5

        ctx = MoveContext(board, move)
        detector = PinDetector()

        # Nd7 в относительной связке Qd3→Nd7→Qd8 — тема exploiting_pin, не pin
        assert detector.detect(ctx) is False


class TestPinIntegration:
    """Интеграционные тесты связки."""

    def test_analyze_detects_pin_ru(self):
        """Детектор находит связку на русском."""
        detector = ChessDetector(lang="ru")

        pgn = """[FEN "k7/8/n7/8/8/8/8/R3K3 w - - 0 1"]

1. Ra4"""

        result = detector.analyze(pgn)
        assert "Связка" in result

    def test_analyze_detects_pin_en(self):
        """Детектор находит связку на английском."""
        detector = ChessDetector(lang="en")

        pgn = """[FEN "k7/8/n7/8/8/8/8/R3K3 w - - 0 1"]

1. Ra4"""

        result = detector.analyze(pgn)
        assert "Pin" in result
