from abc import ABC, abstractmethod
from itertools import product
import sys
import os.path as path

from task2.mazeObjects.maze import Maze
from task2.mazeObjects.cell import Cell
from task2.observerSubject import MazeEvent, MazeEventType, Subject

class MazeBuilder(ABC):
    """Интерфейс MazeBuilder с методом buildFromFile(filename)"""
    @abstractmethod
    def buildFromFile(self, filename: str):
        """Создание лабиринта из файла."""

class TextFileMazeBuilder(MazeBuilder):
    """Читает файл, парсит символы,
    создаёт объекты Cell,
    задаёт координаты и флаги,
    после чего возвращает готовый Maze."""

    start:dict
    end:dict

    def _cellStrategy(self, letter: str) -> Cell:
        if letter == '#':
            return Cell(isWall=True)
        elif letter == ' ':
            return Cell()
        elif letter == 'S':
            return Cell(isStart=True)
        elif letter == 'E':
            return Cell(isExit=True)
        else:
            sys.stderr.write(f"Неизвестный символ '{letter}' при загрузке из файла\n")
            return Cell()
    
    def _updateStartEnd(self, letter: str, x:int, y:int) -> None:
        if letter == 'S':
            self.start = {'x': x, 'y': y}
        elif letter == 'E':
            self.end = {'x': x, 'y': y}

    def _generate_row_from_txt(self, filename: str) -> list[str]:
        with open(filename) as file:
            text = file.read()
        text = text.strip()
        if not text:
            raise ValueError(f"Файл \"{filename}\" пуст")
        text = text.split('\n')
        return text

    def buildFromFile(self, filename: str):
        rows = self._generate_row_from_txt(filename)
        height = len(rows)
        width = len(rows[0])
        array = [[Cell() for j in range(width)] for i in range(height)]

        try:
            for x, y in product(range(width), range(height)):
                cell = self._cellStrategy(rows[y][x])
                self._updateStartEnd(rows[y][x], x, y)
                cell.x = x
                cell.y = y
                array[y][x] = cell
        except IndexError:
            raise ValueError(f"В файле {filename}: Строка {y+1} имеет длину {len(rows[y])}, ожидалось {width}")
        
        maze_name, _ = path.splitext(path.basename(filename))

        return Maze(array, self.start, self.end, name=maze_name)
   