import time
from typing import List, Optional
from dataclasses import dataclass
from maze_model import Maze, Cell
from pathfinding_strategies import PathFindingStrategy


@dataclass
class SearchStats:
    algorithm_name: str
    path_length: int
    execution_time_ms: float
    path_found: bool
    
    def __post_init__(self):
        self.execution_time_ms = round(self.execution_time_ms, 3)
    
    def summary(self) -> str:
        if self.path_found:
            return f"{self.algorithm_name}: путь найден | длина={self.path_length} | время={self.execution_time_ms} мс"
        return f"{self.algorithm_name}: путь НЕ найден | время={self.execution_time_ms} мс"
    
    def detailed_report(self) -> str:
        separator = "=" * 50
        report = f"\n{separator}\nОтчёт о поиске пути\n{separator}\n"
        report += f"Алгоритм: {self.algorithm_name}\n"
        report += f"Путь найден: {'Да' if self.path_found else 'Нет'}\n"
        if self.path_found:
            report += f"Длина пути: {self.path_length} шагов\n"
        report += f"Время выполнения: {self.execution_time_ms} мс\n{separator}"
        return report


class MazeSolver:
    def __init__(self, maze: Maze, strategy: PathFindingStrategy = None):
        self._maze = maze
        self._strategy = strategy
        self._last_stats: Optional[SearchStats] = None
        self._validate_maze()
    
    def _validate_maze(self):
        if not self._maze.start_cell:
            raise ValueError("Лабиринт не имеет стартовой клетки")
        if not self._maze.exit_cell:
            raise ValueError("Лабиринт не имеет выходной клетки")
    
    def set_strategy(self, strategy: PathFindingStrategy) -> None:
        self._strategy = strategy
    
    def solve(self) -> List[Cell]:
        if self._strategy is None:
            raise ValueError("Стратегия не установлена")
        
        start_time = time.perf_counter()
        path = self._strategy.find_path(self._maze, self._maze.start_cell, self._maze.exit_cell)
        end_time = time.perf_counter()
        
        self._last_stats = SearchStats(
            algorithm_name=self._strategy.get_name(),
            path_length=len(path),
            execution_time_ms=(end_time - start_time) * 1000,
            path_found=len(path) > 0
        )
        return path
    
    def solve_with_stats(self) -> tuple:
        path = self.solve()
        return path, self._last_stats
    
    def get_last_stats(self) -> Optional[SearchStats]:
        return self._last_stats