"""Type stubs for the Rust-based core engine bindings.

This module provides Python type hints for the PyO3-generated bindings
from `crates/cardgame/src/python.rs`.
"""


import numpy as np
import numpy.typing as npt

# Module-level constants
STATE_TENSOR_SIZE: int  # 328
ACTION_SPACE_SIZE: int  # 422

class PyGame:
    """Single-game interface to the Essence Wars engine.

    Provides a Gymnasium-compatible API for running individual games
    with full access to bot actions (greedy, random, MCTS, alpha-beta).
    """

    def __new__(
        cls,
        deck1: str | None = None,
        deck2: str | None = None,
        game_mode: str | None = None,
    ) -> PyGame:
        """Create a new game instance.

        Args:
            deck1: Name of player 1's deck (from list_decks()). Random if None.
            deck2: Name of player 2's deck (from list_decks()). Random if None.
            game_mode: Game mode string. Default if None.

        Returns:
            A new PyGame instance.

        Raises:
            ValueError: If deck name is invalid.
        """
        ...

    def reset(self, seed: int) -> None:
        """Reset the game to initial state with the given seed.

        Args:
            seed: Random seed for deterministic game setup.
        """
        ...

    def observe(self) -> npt.NDArray[np.float32]:
        """Get the current game state observation.

        Returns:
            Float32 array of shape (328,) representing the game state
            from the current player's perspective.
        """
        ...

    def action_mask(self) -> npt.NDArray[np.float32]:
        """Get the mask of legal actions.

        Returns:
            Float32 array of shape (422,) where 1.0 indicates a legal action
            and 0.0 indicates an illegal action.
        """
        ...

    def step(self, action: int) -> tuple[float, bool]:
        """Execute an action and advance the game state.

        Args:
            action: Action index (0-421). Must be legal per action_mask().

        Returns:
            Tuple of (reward, done) where reward is the immediate reward
            for the action and done indicates if the game has ended.

        Raises:
            ValueError: If action is illegal.
        """
        ...

    def current_player(self) -> int:
        """Get the index of the current player (0 or 1)."""
        ...

    def is_done(self) -> bool:
        """Check if the game has ended."""
        ...

    def get_reward(self, player: int) -> float:
        """Get the final reward for a player.

        Args:
            player: Player index (0 or 1).

        Returns:
            1.0 for win, -1.0 for loss, 0.0 for draw or game not ended.
        """
        ...

    def fork(self) -> PyGame:
        """Create an independent copy of the current game state.

        Returns:
            A new PyGame instance with identical state.
        """
        ...

    def greedy_action(self) -> int:
        """Get the greedy bot's recommended action.

        Returns:
            Action index chosen by the greedy heuristic bot.
        """
        ...

    def random_action(self) -> int:
        """Get a random legal action.

        Returns:
            A randomly selected legal action index.
        """
        ...

    def mcts_action(self, simulations: int = 100) -> int:
        """Get the MCTS bot's recommended action.

        Args:
            simulations: Number of MCTS simulations to run.

        Returns:
            Action index chosen by Monte Carlo Tree Search.
        """
        ...

    def alphabeta_action(self, depth: int = 6) -> int:
        """Get the alpha-beta bot's recommended action.

        Args:
            depth: Search depth for alpha-beta pruning.

        Returns:
            Action index chosen by alpha-beta search.
        """
        ...

    def alphabeta_ranked_actions(self, depth: int = 6) -> list[tuple[int, float]]:
        """Get all legal actions ranked by alpha-beta evaluation.

        Args:
            depth: Search depth for alpha-beta pruning.

        Returns:
            List of (action_index, score) tuples, sorted best-first.
        """
        ...

    def turn_number(self) -> int:
        """Get the current turn number."""
        ...

    @staticmethod
    def list_decks() -> list[str]:
        """List all available deck names.

        Returns:
            List of deck name strings that can be passed to __new__().
        """
        ...


class PyParallelGames:
    """Batched game interface for vectorized environments.

    Provides efficient parallel execution of multiple games,
    suitable for RL training with vectorized environments.
    """

    def __new__(
        cls,
        num_envs: int,
        deck1: str | None = None,
        deck2: str | None = None,
        game_mode: str | None = None,
        opponent: str | None = None,
    ) -> PyParallelGames:
        """Create a batch of parallel game instances.

        Args:
            num_envs: Number of parallel environments.
            deck1: Name of player 1's deck for all games. Random if None.
            deck2: Name of player 2's deck for all games. Random if None.
            game_mode: Game mode string. Default if None.
            opponent: Built-in opponent: "greedy" (default), "random", or "self".

        Returns:
            A new PyParallelGames instance.

        Raises:
            ValueError: If deck name is invalid.
        """
        ...

    @property
    def num_envs(self) -> int:
        """Number of parallel environments."""
        ...

    def reset(self, seeds: list[int]) -> None:
        """Reset all games with the given seeds.

        Args:
            seeds: List of seeds, one per environment. Length must match num_envs.

        Raises:
            ValueError: If seeds length doesn't match num_envs.
        """
        ...

    def observe_batch(self) -> npt.NDArray[np.float32]:
        """Get observations for all games.

        Returns:
            Float32 array of shape (num_envs, 328).
        """
        ...

    def action_mask_batch(self) -> npt.NDArray[np.float32]:
        """Get action masks for all games.

        Returns:
            Float32 array of shape (num_envs, 422).
        """
        ...

    def step_batch(
        self, actions: npt.NDArray[np.uint16]
    ) -> tuple[npt.NDArray[np.float32], npt.NDArray[np.bool_]]:
        """Execute actions in all games.

        Args:
            actions: Uint8 array of shape (num_envs,) with action indices.

        Returns:
            Tuple of (rewards, dones) arrays, each of shape (num_envs,).

        Raises:
            ValueError: If any action is illegal.
        """
        ...

    def current_players(self) -> list[int]:
        """Get current player indices for all games.

        Returns:
            List of player indices (0 or 1), one per environment.
        """
        ...

    def dones(self) -> list[bool]:
        """Check which games have ended.

        Returns:
            List of done flags, one per environment.
        """
        ...

    def reset_single(self, idx: int, seed: int) -> None:
        """Reset a single game in the batch.

        Args:
            idx: Environment index to reset.
            seed: Random seed for the reset.

        Raises:
            IndexError: If idx is out of bounds.
        """
        ...

    def reset_all(self, base_seed: int) -> None:
        """Reset all games with sequential seeds.

        Args:
            base_seed: Base seed. Game i gets seed (base_seed + i).
        """
        ...
