from collections import Counter
from dataclasses import dataclass
from enum import Enum, auto

from .board import Board
from .pieces import Color, Piece, PieceType, Position
from .rules import Rules


class MoveResult(Enum):
    NORMAL = auto()
    CHECK = auto()
    GENERAL_CAPTURED = auto()
    STALEMATE = auto()
    REPETITION = auto()
    INVALID = auto()


@dataclass
class MoveRecord:
    from_pos: Position
    to_pos: Position
    piece: Piece
    captured: Piece | None


class Game:
    def __init__(self):
        self.board = Board()
        self.current_turn = Color.RED
        self.move_history: list[MoveRecord] = []
        self.red_captures: list[Piece] = []
        self.black_captures: list[Piece] = []
        self.game_over = False
        self.winner: Color | None = None
        self.draw = False
        self.move_count = 0

        self._position_counts: Counter = Counter()
        self._position_keys: list[tuple] = []
        initial_key = self.board.position_key()
        self._position_counts[initial_key] = 1
        self._position_keys.append(initial_key)

    def make_move(self, from_pos: Position, to_pos: Position) -> MoveResult:
        if self.game_over:
            return MoveResult.INVALID

        piece = self.board.get_piece(from_pos)
        if not piece or piece.color != self.current_turn:
            return MoveResult.INVALID

        if not Rules.is_valid_move(self.board, from_pos, to_pos, self.current_turn):
            return MoveResult.INVALID

        captured = self.board.move_piece(from_pos, to_pos)
        self.move_history.append(MoveRecord(from_pos, to_pos, piece, captured))
        self.move_count += 1

        if captured:
            if piece.color == Color.RED:
                self.red_captures.append(captured)
            else:
                self.black_captures.append(captured)

            if captured.piece_type == PieceType.GENERAL:
                self.game_over = True
                self.winner = self.current_turn
                return MoveResult.GENERAL_CAPTURED

        key = self.board.position_key()
        self._position_counts[key] += 1
        self._position_keys.append(key)

        opponent = self.current_turn.opponent

        if Rules.has_no_moves(self.board, opponent):
            self.game_over = True
            self.draw = True
            return MoveResult.STALEMATE

        if self._position_counts[key] >= 3:
            self.game_over = True
            self.draw = True
            return MoveResult.REPETITION

        in_check = Rules.is_in_check(self.board, opponent)
        self.current_turn = opponent

        return MoveResult.CHECK if in_check else MoveResult.NORMAL

    def apply_opponent_move(self, from_pos: Position, to_pos: Position) -> MoveResult:
        return self.make_move(from_pos, to_pos)

    def undo_move(self) -> bool:
        if not self.move_history:
            return False

        if self._position_keys:
            last_key = self._position_keys.pop()
            self._position_counts[last_key] -= 1
            if self._position_counts[last_key] <= 0:
                del self._position_counts[last_key]

        record = self.move_history.pop()
        piece = self.board.get_piece(record.to_pos)

        self.board.grid[record.from_pos.row][record.from_pos.col] = piece
        if piece:
            piece.position = record.from_pos

        if record.captured:
            self.board.grid[record.to_pos.row][record.to_pos.col] = record.captured
            record.captured.position = record.to_pos
            self.board.pieces.append(record.captured)
            if record.piece.color == Color.RED:
                self.red_captures.pop()
            else:
                self.black_captures.pop()
        else:
            self.board.grid[record.to_pos.row][record.to_pos.col] = None

        self.current_turn = self.current_turn.opponent
        self.move_count -= 1
        self.game_over = False
        self.winner = None
        self.draw = False
        return True

    def resign(self, color: Color):
        self.game_over = True
        self.winner = color.opponent
