import heapq
from itertools import count

from task2.strategyObjects.pathFindingStrategy import PathFindingStrategy
from task2.strategyObjects.util import restorePath

from task2.mazeObjects.maze import Maze
from task2.mazeObjects.cell import Cell
from task2.mazeObjects.path import Path

class AStar(PathFindingStrategy):
    """Алгоритм с эвристикой (etc. манхэттенское расстояние) – компромисс между скоростью и оптимальностью."""
    def heuristic(self, first: Cell, second: Cell) -> int:
        return abs(first.x - second.x) + abs(first.y - second.y)

    def findPath(self, maze: Maze, start: Cell, exit: Cell) -> Path:
        tie_breaker = count()
        start_heuristic = self.heuristic(start, exit)
        heap: list[tuple[int, int, int, Cell]] = [
            (start_heuristic, start_heuristic, next(tie_breaker), start)
        ]
        g_score: dict[Cell, int] = {start: 0}
        parents: dict[Cell, Cell | None] = {start: None}
        visited: set[Cell] = set()

        while heap:
            _, _, _, current = heapq.heappop(heap)
            if current in visited:
                continue
            visited.add(current)

            if current.isExit:
                return Path(restorePath(parents, exit), len(visited))

            for neighbor in maze.getNeighbors(current):
                tentative_score = g_score[current]
                if tentative_score < g_score.get(neighbor, 10**12):
                    g_score[neighbor] = tentative_score
                    parents[neighbor] = current
                    heuristic = self.heuristic(neighbor, exit)
                    priority = tentative_score + heuristic
                    heapq.heappush(
                        heap,
                        (priority, heuristic, next(tie_breaker), neighbor),
                    )
        return Path(None, len(visited))