# maze_builder.py
from abc import ABC, abstractmethod
from maze import Maze, Cell


class MazeBuilder(ABC):
    """Интерфейс строителя лабиринта (Builder pattern)."""
    
    @abstractmethod
    def build_from_file(self, filename: str) -> Maze:
        """Принимает путь к файлу, возвращает готовый Maze."""
        pass


class TextFileMazeBuilder(MazeBuilder):
    """
    Строитель лабиринта из текстового файла.
    
    Формат файла:
        # — стена
        . или пробел — проход
        S — старт
        E — выход
    """
    
    def build_from_file(self, filename: str) -> Maze:
        with open(filename, 'r', encoding='utf-8') as f:
            lines = [line.rstrip('\n') for line in f.readlines()]
        
        if not lines:
            raise ValueError("Файл лабиринта пуст")
        
        height = len(lines)
        width = max(len(line) for line in lines)
        
        maze = Maze(width, height)
        start_found = False
        exit_found = False
        
        for y, line in enumerate(lines):
            for x, char in enumerate(line):
                is_wall = (char == '#')
                is_start = (char == 'S')
                is_exit = (char == 'E')
                
                if is_start:
                    start_found = True
                if is_exit:
                    exit_found = True
                
                cell = Cell(x, y, is_wall=is_wall, is_start=is_start, is_exit=is_exit)
                maze.set_cell(x, y, cell)
        
        if not start_found or not exit_found:
            raise ValueError("Лабиринт должен содержать старт (S) и выход (E)")
        
        return maze


class RandomMazeBuilder(MazeBuilder):
    """
    Строитель случайного лабиринта.
    Алгоритм: рекурсивный бэктрекинг.
    """
    
    def __init__(self, width: int, height: int):
        self.width = width
        self.height = height
    
    def build_from_file(self, filename: str = None) -> Maze:
        """
        filename игнорируется — лабиринт генерируется случайно.
        Название метода общее для всех Builder'ов.
        """
        import random
        
        maze = Maze(self.width, self.height)
        
        # Шаг 1: Заполняем всё стенами
        for y in range(self.height):
            for x in range(self.width):
                maze.set_cell(x, y, Cell(x, y, is_wall=True))
        
        # Шаг 2: Рекурсивно прокладываем пути
        visited = set()
        
        def carve(x, y):
            """Прокладывает проход из точки (x, y)."""
            visited.add((x, y))
            maze.set_cell(x, y, Cell(x, y, is_wall=False))
            
            # 4 направления, перемешанные случайно
            directions = [(0, -2), (0, 2), (-2, 0), (2, 0)]
            random.shuffle(directions)
            
            for dx, dy in directions:
                nx, ny = x + dx, y + dy
                
                # Проверяем границы и что ещё не посещали
                if (0 <= nx < self.width and 0 <= ny < self.height 
                        and (nx, ny) not in visited):
                    
                    # Убираем стену между текущей и новой клеткой
                    wall_x = x + dx // 2
                    wall_y = y + dy // 2
                    maze.set_cell(wall_x, wall_y, Cell(wall_x, wall_y, is_wall=False))
                    
                    # Рекурсия в новую клетку
                    carve(nx, ny)
        
        # Начинаем с (1, 1) — нечётные координаты для коридоров
        carve(1, 1)
        
        # Шаг 3: Устанавливаем старт и выход в углах лабиринта
        maze.set_cell(1, 1, Cell(1, 1, is_wall=False, is_start=True))
        maze.set_cell(
            self.width - 2, self.height - 2,
            Cell(self.width - 2, self.height - 2, is_wall=False, is_exit=True)
        )
        
        return maze