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


class Observer(ABC):
    @abstractmethod
    def update(self, event: str, data=None):
        pass


class ConsoleView(Observer):
    WALL_CHAR = '■'
    START_CHAR = 'S'
    EXIT_CHAR = 'E'
    PATH_CHAR = '•'  
    PLAYER_CHAR = 'P'
    PASSAGE_CHAR = ' '

    def update(self, event: str, data=None):
        if event == "search_started":
            print(f"\n Запущен поиск с помощью: {data['strategy']}")
        elif event == "path_found":
            stats = data['stats']
            print(f" Путь найден! {stats}")
        elif event == "path_not_found":
            stats = data['stats']
            print(f" Путь НЕ найден. {stats}")
        elif event == "step":
            self.render(data['maze'], data['current'], data.get('path'))

    @staticmethod
    def render(maze: Maze, player_pos: Optional[Cell] = None, path: Optional[List[Cell]] = None):
        os.system('cls' if os.name == 'nt' else 'clear')
        path_set = set(path) if path else set()

        print("┌" + "─" * maze.width + "┐")
        
        for y in range(maze.height):
            row_str = "│"
            for x in range(maze.width):
                cell = maze.getCell(x, y)
                
                if player_pos and cell == player_pos:
                    row_str += ConsoleView.PLAYER_CHAR
                elif cell.isStart:
                    row_str += ConsoleView.START_CHAR
                elif cell.isExit:
                    row_str += ConsoleView.EXIT_CHAR
                elif cell in path_set:
                    row_str += ConsoleView.PATH_CHAR
                elif cell.isWall:
                    row_str += ConsoleView.WALL_CHAR
                else:
                    row_str += ConsoleView.PASSAGE_CHAR
            row_str += "│"
            print(row_str)
        
        print("└" + "─" * maze.width + "┘")