"""Shared schemas for Connect 4."""

from dataclasses import dataclass
from datetime import datetime
from enum import Enum, unique
from typing import Optional, Tuple

# Four (row, col) cells forming a connect-four, ``row`` 0 = top, ``col`` 0 = left.
WinningLine = Tuple[Tuple[int, int], Tuple[int, int], Tuple[int, int], Tuple[int, int]]

GRID_ROWS = 6
GRID_COLUMNS = 7
DISCS_PER_PLAYER = 21


@unique
class PlayerType(Enum):
    BOT = 1
    HUMAN = 2


@unique
class BotDifficulty(Enum):
    EASY = 1
    MEDIUM = 2
    HARD = 3


@dataclass
class Gamestats:
    player_name: str
    player_id: int
    games_won: int = 0
    games_lost: int = 0
    games_draw: int = 0
    games_played: int = 0


@dataclass
class Player:
    """Represents a player in the game."""

    name: str
    # id being 1 or 2
    id: int
    type: PlayerType
    difficulty: Optional[BotDifficulty] = None

    def __post_init__(self) -> None:
        """Validate player configuration."""
        if self.type == PlayerType.BOT and self.difficulty is None:
            raise ValueError("Bot player must include a bot difficulty.")
        if self.type == PlayerType.HUMAN and self.difficulty is not None:
            raise ValueError("Human player cannot have bot difficulty.")


@unique
class GameStatus(Enum):
    RUNNING = 1
    COMPLETED = 2


@unique
class GameOutcome(Enum):
    PLAYER_1_WIN = 1
    PLAYER_2_WIN = 2
    DRAW = 3


@dataclass
class Game:
    """Immutable-friendly snapshot of current game state."""

    game_id: str
    # references player.id on this position 1. This player starts the game
    position_1_player_id: int
    # references player.id on this position 2
    position_2_player_id: int
    # stores player position 1 or 2 when the spot is taken
    grid: list[list[int]]
    # returns player by positions, ie (position_1, position_2)
    players: Tuple[Player, Player]
    started_at: datetime
    completed_at: Optional[datetime]
    status: GameStatus
    outcome: Optional[GameOutcome]
    next_move_player_id: int
    winner_id: Optional[int]
    discs_remaining: Tuple[int, int]
    winning_line: Optional[WinningLine] = None


@unique
class ExceptionType(Enum):
    INVALID_PLACEMENT = 1
    PLACEMENT_NOT_ALLOWED = 2


@dataclass
class GameException(Exception):
    message: str
    code: int
    type: ExceptionType
