"""Ladder, Elo and terminal-play tests (fast: tiny games only)."""
import os

import chess

from engine import standard_weights
from ladder import (EnginePlayer, RandomPlayer, estimate_elo, make_level,
                    play_game, random_opening)
from play import parse_move, play, render

HERE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
WEIGHTS = os.path.join(HERE, "weights")


def test_elo_recovers_ordering_and_anchor():
    games = ([("A", "B", 0.0)] * 8 + [("A", "B", 1.0)] * 2 +
             [("B", "C", 0.0)] * 7 + [("B", "C", 0.5)] * 3 +
             [("A", "C", 0.0)] * 10)
    elo = estimate_elo(["A", "B", "C"], games, anchor="A")
    assert elo["A"] == 0.0
    assert 0 < elo["B"] < elo["C"]


def test_elo_two_players_closed_form():
    # 3 wins of B, 1 win of A, plus 1 virtual draw: A scores 1.5/5 = 0.3
    elo = estimate_elo(["A", "B"], [("A", "B", 0.0)] * 3 + [("A", "B", 1.0)], anchor="A")
    import math
    assert abs(elo["B"] - 400 * math.log10(0.7 / 0.3)) < 0.5


def test_engine_beats_random_in_short_game():
    eng = EnginePlayer("eng", standard_weights(), depth=2, qdepth=4, node_limit=3000)
    score, game, reason = play_game(eng, RandomPlayer(seed=1), random_opening(0, 2),
                                    max_plies=60, adjudicate_margin=3)
    assert score == 1.0, reason
    assert game.headers["White"] == "eng"


def test_shipped_weights_load_for_every_level():
    for lvl in ("L0", "L1", "L2", "L3"):
        p = make_level(lvl, WEIGHTS)
        m, _ = p.choose(chess.Board())
        assert m in chess.Board().legal_moves


def test_parse_move_san_and_uci():
    b = chess.Board()
    assert parse_move(b, "e4") == chess.Move.from_uci("e2e4")
    assert parse_move(b, "g1f3") == chess.Move.from_uci("g1f3")
    assert parse_move(b, "e5") is None
    assert "a" in render(b) and "8" in render(b, white_bottom=False, unicode=False)


def test_play_loop_with_scripted_input(tmp_path):
    script = iter(["e4", "nonsense", "moves", "undo", "d4", "Nf3", "quit"])
    lines = []
    pgn = tmp_path / "g.pgn"
    board = play("L1", "white", WEIGHTS, str(pgn), unicode=False,
                 input_fn=lambda prompt: next(script), out=lines.append, seed=0)
    text = "\n".join(lines)
    assert "Not a legal move" in text
    assert "Took back" in text
    assert board.move_stack[0] == chess.Move.from_uci("d2d4")   # e4 was undone
    assert len(board.move_stack) == 4
    assert "1. d4" in pgn.read_text()
