import time
from dataclasses import dataclass

from maze_model import Maze, Cell
from strategies import PathFindingStrategy
from observer import Observer


@dataclass
class SearchStats:
    time_ms: float      
    visited_cells: int  
    path_length: int     
    path: list[Cell]   


class MazeSolver:
    def __init__(self, maze: Maze, strategy: PathFindingStrategy | None = None):
        self.maze = maze
        self.strategy = strategy
        self._observers: list[Observer] = []

    def set_strategy(self, strategy: PathFindingStrategy) -> None:
        self.strategy = strategy

    def add_observer(self, observer: Observer) -> None:
        self._observers.append(observer)

    def remove_observer(self, observer: Observer) -> None:
        self._observers.remove(observer)

    def _notify(self, event: dict) -> None:
        for obs in self._observers:
            obs.update(event)

    def solve(self) -> SearchStats:
        if self.strategy is None:
            raise RuntimeError("Стратегия не задана. Используйте set_strategy().")

        self._notify({"type": "maze_loaded", "maze": self.maze})

        t_start = time.perf_counter()
        path = self.strategy.find_path(self.maze, self.maze.start, self.maze.exit)
        t_end = time.perf_counter()

        time_ms = (t_end - t_start) * 1000
        visited = getattr(self.strategy, "visited_count", 0)

        stats = SearchStats(
            time_ms=time_ms,
            visited_cells=visited,
            path_length=len(path),
            path=path,
        )

        if path:
            self._notify({"type": "path_found", "maze": self.maze, "path": path})
        else:
            self._notify({"type": "no_path"})

        return stats
