import time
from dataclasses import dataclass
from typing import List, Optional

@dataclass
#Статистика поиска пути
class SearchStats:
    time_ms: float          #Время выполнения в миллисекундах
    visited_cells: int      #Количество посещенных клеток
    path_length: int        #Длина найденного пути (0 если путь не найден)
    path_found: bool        #Найден ли путь
    
    def __repr__(self):
        status = "Найден" if self.path_found else "Не найден"
        return f"Stats({status}, время={self.time_ms:.2f}мс, посещено={self.visited_cells}, длина={self.path_length})"
    
    def to_dict(self):
        """Преобразует статистику в словарь для CSV"""
        return {
            'time_ms': f"{self.time_ms:.2f}",
            'visited_cells': self.visited_cells,
            'path_length': self.path_length,
            'path_found': self.path_found
        }


class MazeSolver:
    """Оркестратор для решения лабиринта"""
    #Инициализация решателя лабиринта
    def __init__(self, maze, strategy=None):
        self.maze = maze
        self._strategy = strategy
        self._last_path = None
    
    #Динамическая смена стратегии поиска
    def set_strategy(self, strategy):
        self._strategy = strategy
        print(f"Стратегия изменена на: {strategy.name}")
    
    def solve(self) -> SearchStats:
        if self._strategy is None:
            raise ValueError("Стратегия не установлена! Используйте set_strategy()")
        
        if self.maze.start is None:
            raise ValueError("В лабиринте нет стартовой клетки!")
        
        if self.maze.exit is None:
            raise ValueError("В лабиринте нет выходной клетки!")
        
        # Замер времени
        start_time = time.perf_counter()
        
        # Поиск пути
        path = self._strategy.find_path(self.maze, self.maze.start, self.maze.exit)
        
        end_time = time.perf_counter()
        time_ms = (end_time - start_time) * 1000
        
        self._last_path = path
        
        return SearchStats(
            time_ms=time_ms,
            visited_cells=self._strategy.visited_count,
            path_length=len(path),
            path_found=len(path) > 0
        )
    
    def get_path(self) -> Optional[List]:
        """Возвращает последний найденный путь"""
        return self._last_path
    
    #Выводит лабиринт с найденным путем
    def print_maze_with_path(self):
        if not self._last_path:
            print("Путь еще не найден. Сначала вызовите solve()")
            return
        
        path_set = set(self._last_path)
        
        print(f"\n  {' ' * 4}", end="")
        for x in range(self.maze.width):
            print(f"{x} ", end="")
        print()
        
        for y in range(self.maze.height):
            print(f"  {y} │ ", end="")
            for x in range(self.maze.width):
                cell = self.maze.get_cell(x, y)
                if cell == self.maze.start:
                    print("S ", end="")
                elif cell == self.maze.exit:
                    print("E ", end="")
                elif cell in path_set:
                    print("● ", end="")
                elif cell.is_wall:
                    print("# ", end="")
                else:
                    print("· ", end="")
            print()
        
        print("\n  Условные обозначения:")
        print("    # - стена    · - проход    ● - путь")
        print("    S - старт    E - выход")

    #Сравнивает несколько стратегий на одном лабиринте
    def compare_strategies(self, strategies: List) -> dict:
        results = {}

        print(f"СРАВНЕНИЕ СТРАТЕГИЙ")
        print(f"Лабиринт: {self.maze.width}x{self.maze.height}")
        
        for strategy in strategies:
            self.set_strategy(strategy)
            stats = self.solve()
            results[strategy.name] = {
                'stats': stats,
                'path': self.get_path()
            }
            
            # Вывод результатов
            status = "OK" if stats.path_found else "BULLSHIT"
            print(f"\n  {status} {strategy.name}:")
            print(f"      Время: {stats.time_ms:.2f} мс")
            print(f"      Посещено клеток: {stats.visited_cells}")
            print(f"      Длина пути: {stats.path_length}")
        
        return results