from __future__ import annotations

from enum import Enum
from typing import Annotated, Literal

from pydantic import BaseModel, Field

from openpoker.types.models import (
    ActionType,
    AIDecisionDetails,
    AvailableAction,
    GameSnapshot,
    ShowdownResult,
    Street,
)


class EngineEventType(str, Enum):
    GAME_CREATED = "game_created"
    HAND_STARTED = "hand_started"
    ACTION_REQUIRED = "action_required"
    ACTION_APPLIED = "action_applied"
    STREET_CHANGED = "street_changed"
    SHOWDOWN = "showdown"
    PLAYER_ELIMINATED = "player_eliminated"
    GAME_OVER = "game_over"
    AI_DECISION = "ai_decision"
    ENGINE_ERROR = "engine_error"


class GameCreatedPayload(BaseModel):
    kind: Literal["game_created"] = "game_created"
    game_name: str
    players: list[str]


class HandStartedPayload(BaseModel):
    kind: Literal["hand_started"] = "hand_started"
    players: list[str]
    dealer: str | None = None
    small_blind_player: str | None = None
    small_blind_amount: int | None = None
    big_blind_player: str | None = None
    big_blind_amount: int | None = None


class ActionRequiredPayload(BaseModel):
    kind: Literal["action_required"] = "action_required"
    player_id: str
    player_name: str
    available_actions: list[AvailableAction]


class ActionAppliedPayload(BaseModel):
    kind: Literal["action_applied"] = "action_applied"
    player_id: str
    player_name: str
    action_type: ActionType
    amount: int | None = None


class StreetChangedPayload(BaseModel):
    kind: Literal["street_changed"] = "street_changed"
    street: Street
    new_cards: list[str]


class ShowdownPayload(BaseModel):
    kind: Literal["showdown"] = "showdown"
    results: list[ShowdownResult]


class PlayerEliminatedPayload(BaseModel):
    kind: Literal["player_eliminated"] = "player_eliminated"
    player_id: str
    player_name: str


class GameOverPayload(BaseModel):
    kind: Literal["game_over"] = "game_over"
    winner_id: str
    winner_name: str
    winner_stack: int


class AIDecisionPayload(BaseModel):
    kind: Literal["ai_decision"] = "ai_decision"
    decision: AIDecisionDetails


class EngineErrorPayload(BaseModel):
    kind: Literal["engine_error"] = "engine_error"
    message: str
    player_id: str | None = None


EventPayload = Annotated[
    GameCreatedPayload
    | HandStartedPayload
    | ActionRequiredPayload
    | ActionAppliedPayload
    | StreetChangedPayload
    | ShowdownPayload
    | PlayerEliminatedPayload
    | GameOverPayload
    | AIDecisionPayload
    | EngineErrorPayload,
    Field(discriminator="kind"),
]


class EngineEvent(BaseModel):
    event_id: str
    sequence: int
    timestamp: float
    event_type: EngineEventType
    game_id: str
    hand_number: int
    payload: EventPayload
    snapshot: GameSnapshot | None = None
