from abc import ABC, abstractmethod
from typing import List, Optional, Any
from models import Cell, Maze


class Observer(ABC):
    
    @abstractmethod
    def update(self, event_type: str, data: Any = None) -> None:
        pass


class Subject:
    
    def __init__(self):
        self._observers: List[Observer] = []
    
    def attach(self, observer: Observer) -> None:
        self._observers.append(observer)
    
    def detach(self, observer: Observer) -> None:
        self._observers.remove(observer)
    
    def notify(self, event_type: str, data: Any = None) -> None:
        for observer in self._observers:
            observer.update(event_type, data)


class ConsoleView(Observer):
    
    def __init__(self):
        self.last_path: List[Cell] = []
        self.player_pos: Optional[Cell] = None
    
    def update(self, event_type: str, data: Any = None) -> None:
        if event_type == "path_found":
            self.last_path = data.get("path", [])
            print(f"\n=== Путь найден! Длина: {len(self.last_path)} ===")
        elif event_type == "path_not_found":
            print("\n=== Путь не найден! ===")
        elif event_type == "player_moved":
            self.player_pos = data.get("position")
            if data.get("redraw", True):
                self.render(data.get("maze"), self.player_pos, self.last_path)
        elif event_type == "maze_loaded":
            print("Лабиринт загружен")
            self.render(data.get("maze"), None, [])
    
    def render(self, maze: Maze, player_pos: Optional[Cell] = None, 
               path: Optional[List[Cell]] = None) -> None:
        path_set = set(path) if path else set()
        
        print("\n" + "=" * (maze.width + 2))
        for y in range(maze.height):
            line = "|"
            for x in range(maze.width):
                cell = maze.get_cell(x, y)
                if player_pos and cell == player_pos:
                    line += "P"
                elif cell == maze.start:
                    line += "S"
                elif cell == maze.exit:
                    line += "E"
                elif cell in path_set and cell != maze.start and cell != maze.exit:
                    line += "."
                elif cell.is_wall:
                    line += "#"
                else:
                    line += " "
            line += "|"
            print(line)
        print("=" * (maze.width + 2))
        
        if path:
            print(f"Длина пути: {len(path)}")