from __future__ import annotations

from dataclasses import dataclass
from typing import Literal, Sequence, TypedDict

from rshogi.core import Move32, Move

EngineExtraValue = str | int | float | bool
StrictMode = Literal["strict", "permissive"]


class TimeControlDict(TypedDict, total=False):
    base_seconds: int
    byoyomi_seconds: int
    increment_seconds: int


class MoveEngineInfoDict(TypedDict, total=False):
    eval: int | None
    depth: int | None
    nodes: int | None
    seldepth: int | None
    extras: dict[str, EngineExtraValue]


class MoveRecordDict(TypedDict, total=False):
    move: MoveRecord | Move32 | Move | int | str
    time_ms: int | None
    comment: str | None
    engine_info: MoveEngineInfo | MoveEngineInfoDict | None


class GameRecordMetadataDict(TypedDict, total=False):
    event: str | None
    site: str | None
    black_player: str | None
    white_player: str | None
    game_name: str | None
    game_type: str | None
    time_control: TimeControl | TimeControlDict | str | None
    black_time_control: TimeControl | TimeControlDict | str | None
    white_time_control: TimeControl | TimeControlDict | str | None
    max_moves: int | None
    impasse_rule: str | None
    start_date: str | None
    end_date: str | None
    updated_date: str | None
    comment: str | None
    attributes: dict[str, str]


class GameResultInfoDict(TypedDict, total=False):
    result: int | str | GameResult
    ply_count: int
    reason: str | None
    end_time_ms: int | None
    end_comment: str | None


class GameRecordDict(TypedDict, total=False):
    version: int
    init_position_sfen: str
    moves: list[MoveRecordDict]
    metadata: GameRecordMetadata | GameRecordMetadataDict
    result: GameResultInfoDict | int | str | GameResult


class GameResult:
    value: int
    name: str
    __members__: dict[str, GameResult]
    BLACK_WIN: GameResult
    WHITE_WIN: GameResult
    DRAW_BY_REPETITION: GameResult
    ERROR: GameResult
    BLACK_WIN_BY_DECLARATION: GameResult
    WHITE_WIN_BY_DECLARATION: GameResult
    DRAW_BY_MAX_PLIES: GameResult
    BLACK_WIN_BY_FORFEIT: GameResult
    WHITE_WIN_BY_FORFEIT: GameResult
    DRAW_BY_IMPASSE: GameResult
    BLACK_WIN_BY_TRY_RULE: GameResult
    WHITE_WIN_BY_TRY_RULE: GameResult
    INVALID: GameResult
    BLACK_WIN_BY_ILLEGAL_MOVE: GameResult
    WHITE_WIN_BY_ILLEGAL_MOVE: GameResult
    BLACK_WIN_BY_TIMEOUT: GameResult
    WHITE_WIN_BY_TIMEOUT: GameResult
    PAUSED: GameResult
    def __init__(self, value: int) -> None: ...
    @classmethod
    def from_str(cls, name: str) -> GameResult: ...
    def is_black_win(self) -> bool: ...
    def is_white_win(self) -> bool: ...
    def is_draw(self) -> bool: ...
    def is_win(self) -> bool: ...
    def is_win_by_declaration(self) -> bool: ...
    def __int__(self) -> int: ...
    def __hash__(self) -> int: ...
    def __eq__(self, other: object) -> bool: ...
    def __ne__(self, other: object) -> bool: ...
    def __repr__(self) -> str: ...
    def __str__(self) -> str: ...


class TimeControl:
    base_seconds: int
    byoyomi_seconds: int
    increment_seconds: int
    def __init__(self, base_seconds: int, byoyomi_seconds: int, increment_seconds: int) -> None: ...
    def to_spec(self) -> str: ...
    @classmethod
    def from_spec(cls, spec: str) -> TimeControl: ...
    @classmethod
    def normalize_spec(cls, spec: str) -> str: ...
    def __repr__(self) -> str: ...


class MoveEngineInfo:
    eval: int | None
    depth: int | None
    nodes: int | None
    seldepth: int | None
    wall_time_ms: int | None
    latency_delta_ms: int | None
    extras: dict[str, EngineExtraValue]
    def __init__(
        self,
        eval: int | None = ...,
        depth: int | None = ...,
        nodes: int | None = ...,
        seldepth: int | None = ...,
        wall_time_ms: int | None = ...,
        latency_delta_ms: int | None = ...,
        extras: dict[str, EngineExtraValue] | None = ...,
    ) -> None: ...
    def set_extra(self, key: str, value: EngineExtraValue) -> None: ...
    def remove_extra(self, key: str) -> EngineExtraValue | None: ...
    def clear_extras(self) -> None: ...
    def __repr__(self) -> str: ...


class MoveRecord:
    move: Move
    comment: str | None
    time_ms: int | None
    engine_info: MoveEngineInfo | None
    def __init__(
        self,
        move: MoveRecord | Move32 | Move | int | str,
        *,
        time_ms: int | None = ...,
        comment: str | None = ...,
        engine_info: MoveEngineInfo | MoveEngineInfoDict | None = ...,
    ) -> None: ...
    def __repr__(self) -> str: ...


class SpecialMoveRecord:
    kind: str
    unknown_name: str | None
    result: GameResult
    comment: str | None
    time_ms: int | None
    raw: str | None
    def __init__(
        self,
        kind: str,
        result: GameResult | int | str,
        unknown_name: str | None = ...,
        comment: str | None = ...,
        time_ms: int | None = ...,
        raw: str | None = ...,
    ) -> None: ...
    def __repr__(self) -> str: ...


class GameResultInfo:
    result: GameResult
    ply_count: int
    reason: str | None
    end_time_ms: int | None
    end_comment: str | None
    def __repr__(self) -> str: ...


class GameRecordMetadata:
    event: str | None
    site: str | None
    black_player: str | None
    white_player: str | None
    game_name: str | None
    game_type: str | None
    time_control: TimeControl | None
    black_time_control: TimeControl | None
    white_time_control: TimeControl | None
    max_moves: int | None
    impasse_rule: str | None
    start_date: str | None
    end_date: str | None
    updated_date: str | None
    comment: str | None
    attributes: dict[str, str]
    def __init__(
        self,
        event: str | None = ...,
        site: str | None = ...,
        black_player: str | None = ...,
        white_player: str | None = ...,
        game_name: str | None = ...,
        game_type: str | None = ...,
        time_control: TimeControl | None = ...,
        black_time_control: TimeControl | None = ...,
        white_time_control: TimeControl | None = ...,
        max_moves: int | None = ...,
        impasse_rule: str | None = ...,
        start_date: str | None = ...,
        end_date: str | None = ...,
        updated_date: str | None = ...,
        comment: str | None = ...,
        attributes: dict[str, str] | None = ...,
    ) -> None: ...
    def set_attribute(self, key: str, value: str) -> None: ...
    def remove_attribute(self, key: str) -> str | None: ...
    def clear_attributes(self) -> None: ...
    def __repr__(self) -> str: ...


class GameRecord:
    init_position_sfen: str
    moves: list[MoveRecord]
    metadata: GameRecordMetadata
    result: GameResult
    result_info: GameResultInfo
    main_terminal: SpecialMoveRecord | None
    game_name: str | None
    game_type: str | None
    updated_date: str | None
    black_time_control: TimeControl | None
    white_time_control: TimeControl | None
    end_time_ms: int | None
    end_comment: str | None
    def __init__(
        self,
        init_position_sfen: str,
        moves: list[MoveRecord] | None = ...,
        metadata: GameRecordMetadata | None = ...,
        terminal: SpecialMoveRecord | None = ...,
    ) -> None: ...
    @property
    def move_count(self) -> int: ...
    def update_metadata(
        self,
        patch: GameRecordMetadata | GameRecordMetadataDict,
        *,
        strict: bool = ...,
    ) -> None: ...
    def set_metadata_attribute(self, key: str, value: str) -> None: ...
    def remove_metadata_attribute(self, key: str) -> str | None: ...
    def clear_metadata_attributes(self) -> None: ...
    def extend_main_line(self, moves: list[MoveRecord]) -> list[int]: ...
    def set_main_terminal(self, terminal: SpecialMoveRecord) -> int: ...
    def to_kif(self) -> str: ...
    def to_ki2(self) -> str: ...
    def to_csa(self) -> str: ...
    def to_jkf(self) -> str: ...
    def write_kif(self, path: str | bytes | bytearray, encoding: str | None = ...) -> None: ...
    def write_ki2(self, path: str | bytes | bytearray, encoding: str | None = ...) -> None: ...
    def write_csa(self, path: str | bytes | bytearray, encoding: str | None = ...) -> None: ...
    def write_jkf(self, path: str | bytes | bytearray, encoding: str | None = ...) -> None: ...
    def to_pack(self) -> bytes: ...
    def to_sbinpack(
        self,
        *,
        stem_score: int = ...,
        include_main: bool = ...,
        include_variations: bool = ...,
    ) -> bytes: ...
    def root_node_id(self) -> int: ...
    def main_line_ids(self) -> list[int]: ...
    def node_parent(self, node_id: int) -> int | None: ...
    def node_move(self, node_id: int) -> MoveRecord | None: ...
    def node_terminal(self, node_id: int) -> SpecialMoveRecord | None: ...
    def children(self, node_id: int) -> list[int]: ...
    def to_dict(self) -> GameRecordDict: ...
    def to_psv(self, *, include_main: bool = ..., include_variations: bool = ...) -> list[bytes]: ...
    @classmethod
    def from_kif_str(cls, text: str) -> GameRecord: ...
    @classmethod
    def from_ki2_str(cls, text: str) -> GameRecord: ...
    @classmethod
    def from_csa_str(cls, text: str) -> GameRecord: ...
    @classmethod
    def from_kif_file(cls, path: str | bytes | bytearray, encoding: str | None = ...) -> GameRecord: ...
    @classmethod
    def from_ki2_file(cls, path: str | bytes | bytearray, encoding: str | None = ...) -> GameRecord: ...
    @classmethod
    def from_csa_file(cls, path: str | bytes | bytearray, encoding: str | None = ...) -> GameRecord: ...
    @classmethod
    def from_pack(cls, data: bytes | bytearray) -> GameRecord: ...
    @classmethod
    def from_sbinpack(cls, data: bytes | bytearray) -> GameRecord: ...
    @classmethod
    def from_main_line(
        cls,
        init_position_sfen: str,
        moves: list[MoveRecord],
        terminal: SpecialMoveRecord | None = ...,
        metadata: GameRecordMetadata | None = ...,
    ) -> GameRecord: ...
    @classmethod
    def from_dict(cls, data: GameRecordDict, *, strict: bool = ...) -> GameRecord: ...
    def __repr__(self) -> str: ...


@dataclass(slots=True)
class GameRecordMetadataBuilder:
    event: str | None = ...
    site: str | None = ...
    black_player: str | None = ...
    white_player: str | None = ...
    game_name: str | None = ...
    game_type: str | None = ...
    time_control: TimeControl | None = ...
    black_time_control: TimeControl | None = ...
    white_time_control: TimeControl | None = ...
    max_moves: int | None = ...
    impasse_rule: str | None = ...
    start_date: str | None = ...
    end_date: str | None = ...
    updated_date: str | None = ...
    comment: str | None = ...
    attributes: dict[str, str] = ...
    def build(self) -> GameRecordMetadata: ...


@dataclass(slots=True)
class GameRecordBuilder:
    init_position_sfen: str
    moves: list[MoveRecord] = ...
    metadata: GameRecordMetadata | GameRecordMetadataBuilder | None = ...
    terminal: SpecialMoveRecord | None = ...
    def build(self) -> GameRecord: ...


def read_pack(data: bytes | bytearray) -> list[GameRecord]: ...
def read_pack_file(path: str | bytes | bytearray) -> list[GameRecord]: ...
def write_pack(records: Sequence[GameRecord]) -> bytes: ...
def write_pack_file(path: str | bytes | bytearray, records: Sequence[GameRecord]) -> None: ...
