package thb.jeanluc.adventure.loader;

import lombok.RequiredArgsConstructor;
import thb.jeanluc.adventure.loader.dto.GameDto;
import thb.jeanluc.adventure.model.Npc;
import thb.jeanluc.adventure.model.Room;
import thb.jeanluc.adventure.model.item.Item;

import java.util.Map;
import java.util.regex.Pattern;

/**
 * Phase 4 of the loading pipeline. Walks the fully wired world and
 * checks the invariants documented in {@code yaml-schemas.md}.
 */
@RequiredArgsConstructor
public class WorldValidator {

    /** Regex every entity id must match. */
    private static final Pattern ID_PATTERN = Pattern.compile("^[a-z][a-z0-9_]*$");

    /** Item registry to validate. */
    private final Map<String, Item> items;

    /** NPC registry to validate. */
    private final Map<String, Npc> npcs;

    /** Room registry to validate. */
    private final Map<String, Room> rooms;

    /** Loaded game.yaml whose {@code startRoom} must exist. */
    private final GameDto gameDto;

    /**
     * Runs every validation rule.
     *
     * @throws WorldLoadException on the first failure
     */
    public void validate() {
        validateIds();
        validateStartRoom();
        validateGreetings();
    }

    private void validateIds() {
        items.keySet().forEach(this::requireValidId);
        npcs.keySet().forEach(this::requireValidId);
        rooms.keySet().forEach(this::requireValidId);
    }

    private void requireValidId(String id) {
        if (id == null || !ID_PATTERN.matcher(id).matches()) {
            throw new WorldLoadException("Invalid id '" + id + "': must match " + ID_PATTERN);
        }
    }

    private void validateStartRoom() {
        if (gameDto == null || gameDto.startRoom() == null) {
            throw new WorldLoadException("game.yaml is missing 'startRoom'");
        }
        if (!rooms.containsKey(gameDto.startRoom())) {
            throw new WorldLoadException(
                    "game.yaml.startRoom '" + gameDto.startRoom() + "' does not match any room");
        }
    }

    private void validateGreetings() {
        for (Npc npc : npcs.values()) {
            if (npc.getGreeting() == null || npc.getGreeting().isBlank()) {
                throw new WorldLoadException("NPC '" + npc.getId() + "' has an empty greeting");
            }
        }
    }
}
