from abc import ABC, abstractmethod
from maze_model import Maze


class MazeBuilder(ABC):
    @abstractmethod
    def build_from_file(self, filename: str) -> Maze:
        pass
    
    @abstractmethod
    def build_from_string(self, content: str) -> Maze:
        pass


class TextFileMazeBuilder(MazeBuilder):
    def __init__(self):
        self._maze = None
        self._lines = []
        self._start_found = False
        self._exit_found = False
    
    def build_from_file(self, filename: str) -> Maze:
        try:
            with open(filename, 'r', encoding='utf-8') as file:
                content = file.read()
            return self.build_from_string(content)
        except FileNotFoundError:
            raise FileNotFoundError(f"Файл не найден: {filename}")
    
    def build_from_string(self, content: str) -> Maze:
        self._reset()
        self._lines = [line.rstrip('\n\r') for line in content.split('\n')]
        while self._lines and not self._lines[-1].strip():
            self._lines.pop()
        
        if not self._lines:
            raise ValueError("Пустой файл лабиринта")
        
        height = len(self._lines)
        width = max(len(line) for line in self._lines)
        
        for i, line in enumerate(self._lines):
            if len(line) != width:
                self._lines[i] = line.ljust(width)
        
        self._maze = Maze(width, height)
        
        for y, line in enumerate(self._lines):
            for x, char in enumerate(line):
                self._parse_cell(x, y, char)
        
        self._validate_maze()
        return self._maze
    
    def _reset(self):
        self._maze = None
        self._lines = []
        self._start_found = False
        self._exit_found = False
    
    def _parse_cell(self, x: int, y: int, char: str):
        cell = self._maze.get_cell(x, y)
        if char == '#':
            cell.is_wall = True
        elif char == 'S':
            if self._start_found:
                raise ValueError(f"Найден второй старт в ({x}, {y})")
            self._maze.set_start(x, y)
            self._start_found = True
        elif char == 'E':
            if self._exit_found:
                raise ValueError(f"Найден второй выход в ({x}, {y})")
            self._maze.set_exit(x, y)
            self._exit_found = True
        elif char == ' ':
            pass
        else:
            raise ValueError(f"Неизвестный символ '{char}' в ({x}, {y})")
    
    def _validate_maze(self):
        if not self._start_found:
            raise ValueError("В лабиринте не найден старт (символ 'S')")
        if not self._exit_found:
            raise ValueError("В лабиринте не найден выход (символ 'E')")