import chess

from chess_detect import ChessDetector
from chess_detect.context import MoveContext
from chess_detect.detectors.tactics.fork import ForkDetector


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

    def test_knight_fork_king_and_queen(self):
        """Коневая вилка: король + ферзь."""
        # Конь прыгает на c7, атакуя короля на e8 и ферзя на a8
        board = chess.Board("q3k3/8/8/4N3/8/8/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("e5c6")  # Nc6 — но не атакует короля

        # Лучше позиция: конь на e5 идёт на d7, атакуя e8 (нет)
        # Конь с c7 атакует a8 и e8
        board = chess.Board("q3k3/8/8/8/8/2N5/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("c3b5")  # Не атакует

        # Точная позиция: конь идёт на c7
        board = chess.Board("q3k3/8/8/1N6/8/8/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("b5c7")  # Nc7+ вилка на короля e8 и ферзя a8

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

        assert detector.detect(ctx) is True

    def test_knight_fork_queen_and_rook(self):
        """Коневая вилка: ферзь + ладья (обе дороже коня)."""
        # Конь на d4 идёт на f5, атакуя ферзя на g7 и ладью на e3
        board = chess.Board("4k3/6q1/8/8/3N4/4r3/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("d4f5")  # Nf5 атакует Qg7 и Re3

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

        assert detector.detect(ctx) is True

    def test_knight_fork_king_and_defended_pawn_not_fork(self):
        """Шах + защищённая пешка — НЕ вилка."""
        # Конь даёт шах и атакует защищённую пешку
        # Пешка на a6 защищена пешкой b7
        board = chess.Board("4k3/1p6/p7/8/8/2N5/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("c3b5")  # Nb5 атакует a7? Нет

        # Конь с b5 атакует: a3, a7, c3, c7, d4, d6
        # Король на e8, пешка на a7 защищена? Нет короля рядом
        board = chess.Board("4k3/p7/8/1N6/8/8/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("b5c7")  # Nc7+ атакует короля e8 и пешку a7?

        # Конь с c7 атакует: a6, a8, b5, d5, e6, e8
        # Не атакует a7. Нужна другая позиция.

        # Позиция: конь атакует короля и защищённую пешку
        # Конь на d5 атакует короля на e7 и пешку на c7 (защищена b8)
        board = chess.Board("1n2k3/2p5/8/3N4/8/8/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("d5f6")  # Nf6+ шах королю e7? Нет, король на e8

        # Упрощаю: король e8, пешка c7 защищена конём b8, конь идёт на e6
        # Конь с e6 атакует: c5, c7, d4, d8, f4, f8, g5, g7
        # Атакует c7 (пешка) и d8/f8 (не король)
        board = chess.Board("1n2k3/2p5/8/8/4N3/8/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("e4d6")  # Nd6+ шах?

        # Конь с d6 атакует: b5, b7, c4, c8, e4, e8, f5, f7
        # Атакует e8 (король!) и c8 (пусто), b7 (пусто)
        # Это просто шах, не вилка с пешкой

        # Финальная позиция: шах + защищённая пешка
        board = chess.Board("4k3/2p5/1p6/8/8/2N5/8/4K3 w - - 0 1")
        # Пешка c7 защищена пешкой b6
        move = chess.Move.from_uci("c3d5")  # Nd5 атакует c7 и e7?

        # Конь с d5 атакует: b4, b6, c3, c7, e3, e7, f4, f6
        # Атакует c7 (пешка защищена b6) и e7 (пусто)
        # Не шах, не вилка

        # Нужен шах + пешка
        board = chess.Board("4k3/8/2p5/8/8/8/3N4/4K3 w - - 0 1")
        # Пешка c6 (без защиты пока)
        # Добавим защитника
        board = chess.Board("4k3/1b6/2p5/8/8/8/3N4/4K3 w - - 0 1")
        # Слон b7 защищает c6
        move = chess.Move.from_uci("d2e4")  # Ne4 атакует c5? Нет

        # Конь с e4 атакует: c3, c5, d2, d6, f2, f6, g3, g5
        # Не атакует c6

        # Простой тест: конь атакует короля и пешку, пешка защищена
        board = chess.Board("4k3/8/5p2/8/8/6N1/8/4K3 w - - 0 1")
        # Конь g3 идёт на e4, атакуя f6 и d6
        move = chess.Move.from_uci("g3e4")

        # Конь с e4 атакует: c3, c5, d2, d6, f2, f6, g3, g5
        # f6 — пешка. Защищена? Нет защитников, она висит!

        # Добавим защитника пешке
        board = chess.Board("4k3/5p2/5p2/8/8/6N1/8/4K3 w - - 0 1")
        # Пешка f6 защищена пешкой f7
        move = chess.Move.from_uci("g3e4")

        # Теперь: конь атакует f6 (защищена) и d6 (пусто)
        # Нет шаха, и только одна цель (f6 защищена, не висит, дешевле)
        # Это НЕ вилка

        # Нужен ШААХ + защищённая пешка
        board = chess.Board("4k3/5p2/4Np2/8/8/8/8/4K3 w - - 0 1")
        # Конь уже на e6, это не ход
        # Надо конь ходит и даёт шах + атакует защищённую пешку

        board = chess.Board("4k3/5p2/5p2/8/4N3/8/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("e4g5")  # Ng5 атакует f7 и h7

        # Конь с g5 атакует: e4, e6, f3, f7, h3, h7
        # f7 — пешка, защищает f6
        # Не шах

        # Ок, создам точную позицию программно
        # Король d8, конь идёт на c6+, пешка на e7 защищена королём
        board = chess.Board("3k4/4p3/8/8/1N6/8/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("b4c6")  # Nc6+

        # Конь с c6 атакует: a5, a7, b4, b8, d4, d8, e5, e7
        # d8 — король (шах!)
        # e7 — пешка, защищена королём d8

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

        # Шах + пешка защищённая → НЕ вилка
        assert detector.detect(ctx) is False

    def test_knight_two_hanging_pawns_is_fork(self):
        """Две висящие пешки — вилка."""
        # Конь атакует две незащищённые пешки
        board = chess.Board("4k3/8/p3p3/8/8/2N5/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("c3b5")  # Nb5 атакует a7 и c7? Нет

        # Конь с b5 атакует: a3, a7, c3, c7, d4, d6
        # a7 — пусто, пешка на a6
        # Конь не атакует a6

        # Новая позиция
        board = chess.Board("4k3/8/8/p5p1/8/4N3/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("e3f5")  # Nf5 атакует g7? Нет, пешки на a5 и g5

        # Конь с f5 атакует: d4, d6, e3, e7, g3, g7, h4, h6
        # g7 — пусто

        # Пешки на d6 и h6
        board = chess.Board("4k3/8/3p3p/8/4N3/8/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("e4f6")  # Nf6 атакует d6? Нет

        # Конь с f6 атакует: d5, d7, e4, e8, g4, g8, h5, h7
        # Не атакует d6 и h6

        # Конь атакует d5 и h5
        board = chess.Board("4k3/8/8/3p3p/8/4N3/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("e3f5")  # Nf5?

        # Нет, это тоже не то. Давай проще — конь на e4 атакует d6 и f6
        board = chess.Board("4k3/8/3p1p2/8/8/2N5/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("c3e4")  # Ne4 атакует d6 и f6

        # Конь с e4 атакует: c3, c5, d2, d6, f2, f6, g3, g5
        # d6 и f6 — пешки, обе висят!

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

        assert detector.detect(ctx) is True

    def test_two_defended_pawns_not_fork(self):
        """Две защищённые пешки — НЕ вилка."""
        # Конь атакует две защищённые пешки
        board = chess.Board("4k3/2p1p3/3p1p2/8/8/2N5/8/4K3 w - - 0 1")
        # Пешки d6 и f6 защищены пешками c7/e7

        move = chess.Move.from_uci("c3e4")  # Ne4 атакует d6 и f6

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

        assert detector.detect(ctx) is False

    def test_pawn_fork_two_pawns_not_fork(self):
        """Пешка атакует две пешки — НЕ вилка."""
        # b6 атакует a7 и c7 — обе пешки, это не тактика
        board = chess.Board("3r3r/pppk1pp1/1P1p2np/P2Pp3/4P1q1/3P1NPb/5PBP/R2Q1RK1 b - - 0 20")
        # Позиция после b6, но нам нужен контекст хода b5-b6
        board_before = chess.Board("3r3r/pppk1pp1/3p2np/PP1Pp3/4P1q1/3P1NPb/5PBP/R2Q1RK1 w - - 0 20")
        move = chess.Move.from_uci("b5b6")  # b6 атакует a7 и c7

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

        assert detector.detect(ctx) is False

    def test_pawn_fork_two_pieces(self):
        """Пешечная вилка: две фигуры."""
        # Пешка атакует два коня — обе ценнее пешки
        # Важно: пешка не должна висеть! Слон атаковал бы d5, поэтому используем коней
        board = chess.Board("4k3/8/2n1n3/8/3P4/8/8/4K3 w - - 0 1")
        move = chess.Move.from_uci("d4d5")  # d5 атакует c6 и e6 (оба коня)

        # Конь c6 атакует: a5, a7, b4, b8, d4, d8, e5, e7 — не атакует d5!
        # Конь e6 атакует: c5, c7, d4, d8, f4, f8, g5, g7 — не атакует d5!
        # Пешка не висит

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

        assert detector.detect(ctx) is True


    def test_no_fork_target_captures_forker(self):
        """Не вилка — цель может взять вилку (равный размен)."""
        # Qg2 атакует Qh3 (ферзь=9) и Ne2 (конь=3, висит)
        # Но Qh3 может взять Qxg2+ — равный размен, и конь спасён
        board = chess.Board("2r4r/1pP1kpp1/3p4/3Pp3/4P3/3P1NPq/4n2P/2R2RQK w - - 5 30")
        move = chess.Move.from_uci("g1g2")  # Qg2

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

        assert detector.detect(ctx) is False


    def test_no_fork_defended_equal_value_target(self):
        """Не вилка — защищённая цель равной ценности (равный размен)."""
        # Ne6 атакует ферзя d4 (9 > 3, ценная) и слона f8 (3 = 3, защищён Ke8+Rh8)
        # Ферзь уходит, Кxf8 — король или ладья забирает коня. Итого: 0
        board = chess.Board("1n2kb1r/6pp/1p1p2p1/1r4N1/3qp3/B2P4/2PK2bP/8 w k - 0 28")
        move = chess.Move.from_uci("g5e6")  # Ne6

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

        # Слон f8 защищён — взятие коня на слона это равный размен, не выигрыш
        assert detector.detect(ctx) is False


class TestForkIntegration:
    """Интеграционные тесты вилки."""

    def test_analyze_detects_fork_ru(self):
        """Детектор находит вилку и добавляет комментарий."""
        detector = ChessDetector(lang="ru")

        pgn = """[FEN "q3k3/8/8/1N6/8/8/8/4K3 w - - 0 1"]

1. Nc7+"""

        result = detector.analyze(pgn)
        assert "Вилка" in result

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

        pgn = """[FEN "q3k3/8/8/1N6/8/8/8/4K3 w - - 0 1"]

1. Nc7+"""

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