package thb.jeanluc.adventure.game;

import thb.jeanluc.adventure.model.Direction;
import thb.jeanluc.adventure.model.ExitLock;
import thb.jeanluc.adventure.model.Room;

import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

/**
 * Breadth-first shortest-path search over the rooms the player has already
 * visited, honouring the same gates as a manual move: locked exits and
 * darkness. Stateless utility, alongside {@link Conditions}/{@link Effects}/
 * {@link Light}.
 */
public final class Pathfinder {

    private Pathfinder() {
    }

    /**
     * Finds the shortest path from the player's current room to {@code target}.
     *
     * @param ctx    active game context
     * @param target destination room (expected to be a visited room)
     * @return the rooms to step through, excluding the start and ending at
     *         {@code target}; empty if there is no currently-passable path
     *         (or {@code target} is the current room)
     */
    public static List<Room> findPath(GameContext ctx, Room target) {
        Room start = ctx.getPlayer().getCurrentRoom();
        Set<String> visited = ctx.getPlayer().getVisitedRoomIds();

        Queue<Room> queue = new ArrayDeque<>();
        Map<Room, Room> cameFrom = new HashMap<>();
        queue.add(start);
        cameFrom.put(start, null);

        while (!queue.isEmpty()) {
            Room current = queue.poll();
            if (current == target) {
                return reconstruct(cameFrom, target);
            }
            for (Map.Entry<Direction, Room> exit : current.getExits().entrySet()) {
                Room next = exit.getValue();
                if (cameFrom.containsKey(next) || !visited.contains(next.getId())) {
                    continue;
                }
                ExitLock lock = current.getExitLocks().get(exit.getKey());
                if (lock != null && !Conditions.all(lock.requires(), ctx)) {
                    continue;
                }
                if (!Light.isLit(ctx, next)) {
                    continue;
                }
                cameFrom.put(next, current);
                queue.add(next);
            }
        }
        return List.of();
    }

    /** Walks predecessors back from target to start; returns steps after start. */
    private static List<Room> reconstruct(Map<Room, Room> cameFrom, Room target) {
        LinkedList<Room> path = new LinkedList<>();
        for (Room r = target; r != null && cameFrom.get(r) != null; r = cameFrom.get(r)) {
            path.addFirst(r);
        }
        return path;
    }
}
