from __future__ import annotations

import pytest
from coolrl_lost_cities.games.classic.game import GameState, classic_config

pytest.importorskip("pygame")

from coolrl_lost_cities.games.classic import pygame_table  # noqa: E402


def test_argparser_defaults_to_final_candidate() -> None:
    args = pygame_table.build_argparser().parse_args([])

    assert args.checkpoint == str(pygame_table.DEFAULT_CHECKPOINT)
    assert args.seed is None
    assert args.width == 1600
    assert args.height == 1000
    assert args.offline is False


def test_argparser_accepts_overrides() -> None:
    args = pygame_table.build_argparser().parse_args(
        ["--checkpoint", "/tmp/ckpt", "--seed", "3", "--offline"]
    )

    assert args.checkpoint == "/tmp/ckpt"
    assert args.seed == 3
    assert args.offline is True


def test_one_suit_per_classic_color() -> None:
    config = classic_config()
    assert len(pygame_table.SUITS) == config.n_colors
    assert len(pygame_table.SUIT_PAINTERS) == config.n_colors


def test_card_key_distinguishes_wagers_from_numbers() -> None:
    config = classic_config()
    state = GameState.new_game(config, seed=0)
    keys = [pygame_table.card_key(card, config.min_rank) for card in state.deck]
    wagers = [key for key in keys if key[1] == 0]
    numbers = [key for key in keys if key[1] != 0]
    assert all(config.min_rank <= value <= config.max_rank for _, value in numbers)
    assert all(0 <= color < config.n_colors for color, _ in keys)
    assert wagers  # a shuffled deck slice still contains wager cards


def test_layout_scales_between_window_sizes() -> None:
    small = pygame_table.Layout(1024, 720, 5)
    large = pygame_table.Layout(1920, 1200, 5)

    for layout in (small, large):
        assert layout.board_top < layout.discard_cy < layout.board_bottom
        assert len(layout.col_x) == 5
        slots = layout.hand_slots(8)
        assert len(slots) == 8
        assert slots[0][0] >= 0
        assert slots[-1][0] + layout.hand_w <= layout.w
    assert large.hand_w >= small.hand_w


def _settle(app: pygame_table.TableApp) -> None:
    now = pygame_table.pygame.time.get_ticks() + 10_000
    app._assign_targets(now)
    for sprite in app.sprites:
        sprite.pos.update(sprite.target)
        sprite.release_at = 0
        sprite.pending_flip_at = None


def test_discard_draw_stays_public_and_survives_undo_redo() -> None:
    app = pygame_table.TableApp(seed=5, offline=True, headless=True)
    try:
        app.input_locked_until = 0
        # Human discards, draws from deck, then the rival takes that public card.
        human_discard = next(i for i in app.state.unified_legal_actions() if i % 2 == 1)
        discarded = app.state.hands[app.human_seat][human_discard // 2]
        app.apply_unified(human_discard)
        app.apply_unified(app.state.card_action_size)

        rival_discard = next(
            i
            for i in app.state.unified_legal_actions()
            if i % 2 == 1 and app.state.hands[app.ai_seat][i // 2].color != discarded.color
        )
        app.apply_unified(rival_discard)
        draw_discard = app.state.card_action_size + 1 + discarded.color
        assert app._legal(draw_discard)
        app.apply_unified(draw_discard)

        face = pygame_table.card_key(discarded, app.min_rank)
        assert app.public_hand_counts[app.ai_seat][face] == 1
        assert any(
            sprite.public and (sprite.color, sprite.value) == face
            for sprite in app.hand_zones[app.ai_seat]
        )

        before = app._copy_public_counts()
        app.undo()
        app.redo()
        assert app.public_hand_counts == before
        assert any(
            sprite.public and sprite.face_up and (sprite.color, sprite.value) == face
            for sprite in app.hand_zones[app.ai_seat]
        )
    finally:
        app.opponent.shutdown()
        pygame_table.pygame.quit()


def test_missing_checkpoint_is_visible_failure_not_heuristic_fallback(tmp_path) -> None:
    opponent = pygame_table.Opponent(tmp_path / "missing", None, seed=1, offline=False)
    try:
        opponent.load_future.result(timeout=5)
        assert opponent.policy is None
        assert opponent.load_error is not None
        assert opponent.label == "MODEL LOAD FAILED"
    finally:
        opponent.shutdown()


def test_offline_mode_explicitly_uses_heuristic() -> None:
    opponent = pygame_table.Opponent(None, None, seed=1, offline=True)
    try:
        opponent.load_future.result(timeout=5)
        assert opponent.policy is not None
        assert opponent.load_error is None
        assert "offline" in opponent.label
    finally:
        opponent.shutdown()
