from __future__ import annotations

from dataclasses import dataclass


@dataclass(frozen=True)
class Cell:
    x: int
    y: int
    is_wall: bool = False
    is_start: bool = False
    is_exit: bool = False
    weight: int = 1
    symbol: str = " "

    def is_passable(self) -> bool:
        return not self.is_wall

    def isPassable(self) -> bool:
        return self.is_passable()


class Maze:
    def __init__(self, cells: list[list[Cell]], start: Cell, exit: Cell) -> None:
        if not cells or not cells[0]:
            raise ValueError("Maze must contain at least one cell")

        width = len(cells[0])
        if any(len(row) != width for row in cells):
            raise ValueError("Maze rows must have equal width")

        self.cells = cells
        self.height = len(cells)
        self.width = width
        self.start = start
        self.exit = exit

    def get_cell(self, x: int, y: int) -> Cell | None:
        if 0 <= x < self.width and 0 <= y < self.height:
            return self.cells[y][x]
        return None

    def getCell(self, x: int, y: int) -> Cell | None:
        return self.get_cell(x, y)

    def get_neighbors(self, cell: Cell) -> list[Cell]:
        neighbors: list[Cell] = []
        for dx, dy in ((0, -1), (1, 0), (0, 1), (-1, 0)):
            neighbor = self.get_cell(cell.x + dx, cell.y + dy)
            if neighbor is not None and neighbor.is_passable():
                neighbors.append(neighbor)
        return neighbors

    def getNeighbors(self, cell: Cell) -> list[Cell]:
        return self.get_neighbors(cell)

    def to_text(self, path: list[Cell] | None = None, player: Cell | None = None) -> str:
        path_cells = {(cell.x, cell.y) for cell in path or []}
        lines: list[str] = []

        for row in self.cells:
            chars: list[str] = []
            for cell in row:
                position = (cell.x, cell.y)
                if player is not None and position == (player.x, player.y):
                    chars.append("@")
                elif cell.is_start:
                    chars.append("S")
                elif cell.is_exit:
                    chars.append("E")
                elif cell.is_wall:
                    chars.append("#")
                elif position in path_cells:
                    chars.append(".")
                elif cell.weight > 1:
                    chars.append(str(cell.weight))
                else:
                    chars.append(" ")
            lines.append("".join(chars))

        return "\n".join(lines)
