from __future__ import annotations

from abc import ABC, abstractmethod
from dataclasses import dataclass
from enum import Enum

from .models import Cell, Maze


class Direction(Enum):
    UP = (0, -1)
    RIGHT = (1, 0)
    DOWN = (0, 1)
    LEFT = (-1, 0)

    @classmethod
    def from_key(cls, key: str) -> "Direction":
        mapping = {
            "w": cls.UP,
            "d": cls.RIGHT,
            "s": cls.DOWN,
            "a": cls.LEFT,
        }
        try:
            return mapping[key.lower()]
        except KeyError as exc:
            raise ValueError("Use W/A/S/D for movement") from exc


@dataclass
class Player:
    maze: Maze
    current_cell: Cell

    @classmethod
    def at_start(cls, maze: Maze) -> "Player":
        return cls(maze=maze, current_cell=maze.start)

    def move_to(self, cell: Cell) -> None:
        if not cell.is_passable():
            raise ValueError("Player cannot move into a wall")
        self.current_cell = cell


class Command(ABC):
    @abstractmethod
    def execute(self) -> bool:
        raise NotImplementedError

    @abstractmethod
    def undo(self) -> bool:
        raise NotImplementedError


class MoveCommand(Command):
    def __init__(self, player: Player, direction: Direction) -> None:
        self.player = player
        self.direction = direction
        self.previous_cell: Cell | None = None
        self.executed = False

    def execute(self) -> bool:
        dx, dy = self.direction.value
        current = self.player.current_cell
        target = self.player.maze.get_cell(current.x + dx, current.y + dy)
        if target is None or not target.is_passable():
            return False

        self.previous_cell = current
        self.player.move_to(target)
        self.executed = True
        return True

    def undo(self) -> bool:
        if not self.executed or self.previous_cell is None:
            return False
        self.player.move_to(self.previous_cell)
        self.executed = False
        return True
