# maze_solver/builders.py
from abc import ABC, abstractmethod
from models import Cell, Maze


class MazeBuilder(ABC):
    """Интерфейс строителя лабиринта."""
    @abstractmethod
    def buildFromFile(self, filename: str) -> Maze:
        """Строит объект Maze из файла."""
        pass


class TextFileMazeBuilder(MazeBuilder):
    """Строитель для текстового формата: ■ стена, ' ' проход, S старт, E выход."""
    
    # Поддерживаемые символы стен
    WALL_SYMBOLS = {'#', '■', '█', '▓', '▒', '░'}
    
    def __init__(self, require_exit: bool = True):
        """
        Args:
            require_exit: Если False, позволяет создавать лабиринты без выхода
        """
        self.require_exit = require_exit

    def buildFromFile(self, filename: str) -> Maze:
        with open(filename, 'r', encoding='utf-8') as f:
            lines = f.readlines()

        # Убираем символы новой строки и пустые строки в конце файла
        cleaned_lines = [line.rstrip('\n') for line in lines if line.strip() != '']

        if not cleaned_lines:
            raise ValueError("Файл лабиринта пуст")

        height = len(cleaned_lines)
        width = len(cleaned_lines[0])

        grid = []
        start_cell = None
        exit_cell = None

        for y, line in enumerate(cleaned_lines):
            row = []
            if len(line) != width:
                raise ValueError(
                    f"Строка {y} имеет длину {len(line)}, ожидалось {width}. "
                    f"Лабиринт должен быть прямоугольным."
                )

            for x, char in enumerate(line):
                is_wall = char in self.WALL_SYMBOLS
                cell = Cell(x, y, is_wall)

                if char == 'S':
                    cell.isStart = True
                    start_cell = cell
                elif char == 'E':
                    cell.isExit = True
                    exit_cell = cell

                row.append(cell)
            grid.append(row)

        if not start_cell:
            raise ValueError("В лабиринте не найдена стартовая позиция (S)")
        
        if self.require_exit and not exit_cell:
            raise ValueError("В лабиринте не найдена выходная позиция (E)")

        maze = Maze(width, height, grid)
        maze.start = start_cell
        maze.exit = exit_cell
        return maze