from __future__ import annotations

import tempfile
import unittest
from pathlib import Path

from maze_solver import (
    AStarStrategy,
    BFSStrategy,
    Direction,
    MazeSolver,
    MoveCommand,
    Player,
    TextFileMazeBuilder,
)


SIMPLE_MAZE = """\
#######
#S   E#
# ### #
#     #
#######"""


class MazeSolverTest(unittest.TestCase):
    def build_maze(self):
        with tempfile.TemporaryDirectory() as directory:
            path = Path(directory) / "maze.txt"
            path.write_text(SIMPLE_MAZE, encoding="utf-8")
            return TextFileMazeBuilder().build_from_file(path)

    def test_builder_reads_start_exit_and_neighbors(self) -> None:
        maze = self.build_maze()

        self.assertEqual((maze.start.x, maze.start.y), (1, 1))
        self.assertEqual((maze.exit.x, maze.exit.y), (5, 1))
        self.assertTrue(maze.get_cell(2, 1).is_passable())
        self.assertFalse(maze.get_cell(0, 0).is_passable())

    def test_bfs_and_astar_find_shortest_path(self) -> None:
        maze = self.build_maze()

        bfs_stats = MazeSolver(maze, BFSStrategy()).solve()
        astar_stats = MazeSolver(maze, AStarStrategy()).solve()

        self.assertEqual(bfs_stats.path_length, 5)
        self.assertEqual(astar_stats.path_length, 5)
        self.assertEqual(bfs_stats.path[0], maze.start)
        self.assertEqual(bfs_stats.path[-1], maze.exit)

    def test_move_command_can_execute_and_undo(self) -> None:
        maze = self.build_maze()
        player = Player.at_start(maze)
        command = MoveCommand(player, Direction.RIGHT)

        self.assertTrue(command.execute())
        self.assertEqual((player.current_cell.x, player.current_cell.y), (2, 1))
        self.assertTrue(command.undo())
        self.assertEqual(player.current_cell, maze.start)


if __name__ == "__main__":
    unittest.main()
