"use strict";
/**
 * YAML World Model Parser
 * Loads and validates world definitions from YAML files
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.YamlWorldParser = void 0;
const fs = __importStar(require("fs/promises"));
const yaml = __importStar(require("js-yaml"));
class YamlWorldParser {
    /**
     * Load a world model from a YAML file
     */
    static async loadFromFile(filePath) {
        try {
            const fileContents = await fs.readFile(filePath, 'utf8');
            const worldData = yaml.load(fileContents);
            return this.validateAndTransform(worldData);
        }
        catch (error) {
            console.error(`Error loading world from ${filePath}:`, error);
            throw new Error(`Failed to load world from ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    /**
     * Validate the loaded YAML data and transform it into a WorldModel
     */
    static validateAndTransform(data) {
        if (!data || typeof data !== 'object') {
            throw new Error('Invalid world data: must be an object');
        }
        const worldData = data;
        // Validate required top-level fields
        this.validateRequiredFields(worldData, ['title', 'author', 'version', 'introduction', 'rooms', 'initialState']);
        // Transform and validate the world model
        const worldModel = {
            title: this.validateString(worldData.title, 'title'),
            author: this.validateString(worldData.author, 'author'),
            version: this.validateString(worldData.version, 'version'),
            introduction: this.validateString(worldData.introduction, 'introduction'),
            rooms: this.validateRooms(worldData.rooms),
            objects: this.validateObjects(worldData.objects),
            characters: this.validateCharacters(worldData.characters),
            actions: this.validateActions(worldData.actions),
            initialState: this.validateInitialState(worldData.initialState)
        };
        // Validate references between entities
        this.validateReferences(worldModel);
        return worldModel;
    }
    /**
     * Validate that an object has all required fields
     */
    static validateRequiredFields(data, requiredFields) {
        for (const field of requiredFields) {
            if (!(field in data)) {
                throw new Error(`Missing required field: ${field}`);
            }
        }
    }
    /**
     * Validate that a value is a string
     */
    static validateString(value, fieldName) {
        if (typeof value !== 'string') {
            throw new Error(`Field ${fieldName} must be a string`);
        }
        return value;
    }
    /**
     * Validate room definitions
     */
    static validateRooms(rooms) {
        if (!rooms || typeof rooms !== 'object') {
            throw new Error('Rooms must be an object mapping room IDs to room definitions');
        }
        const roomsData = rooms;
        const validatedRooms = {};
        for (const [roomId, roomData] of Object.entries(roomsData)) {
            if (!roomData || typeof roomData !== 'object') {
                throw new Error(`Room ${roomId} must be an object`);
            }
            const room = roomData;
            this.validateRequiredFields(room, ['name', 'description', 'exits']);
            validatedRooms[roomId] = {
                id: roomId,
                name: this.validateString(room.name, `rooms.${roomId}.name`),
                description: this.validateString(room.description, `rooms.${roomId}.description`),
                exits: this.validateExits(room.exits, roomId),
                objects: this.validateStringArray(room.objects || [], `rooms.${roomId}.objects`),
                characters: this.validateStringArray(room.characters || [], `rooms.${roomId}.characters`)
            };
        }
        return validatedRooms;
    }
    /**
     * Validate exit definitions
     */
    static validateExits(exits, roomId) {
        if (!Array.isArray(exits)) {
            throw new Error(`Exits for room ${roomId} must be an array`);
        }
        return exits.map((exit, index) => {
            if (!exit || typeof exit !== 'object') {
                throw new Error(`Exit ${index} in room ${roomId} must be an object`);
            }
            const exitData = exit;
            this.validateRequiredFields(exitData, ['direction', 'targetRoomId']);
            return {
                direction: this.validateString(exitData.direction, `rooms.${roomId}.exits[${index}].direction`),
                targetRoomId: this.validateString(exitData.targetRoomId, `rooms.${roomId}.exits[${index}].targetRoomId`),
                description: exitData.description ? this.validateString(exitData.description, `rooms.${roomId}.exits[${index}].description`) : undefined,
                isLocked: typeof exitData.isLocked === 'boolean' ? exitData.isLocked : false,
                keyId: exitData.keyId ? this.validateString(exitData.keyId, `rooms.${roomId}.exits[${index}].keyId`) : undefined
            };
        });
    }
    /**
     * Validate object definitions
     */
    static validateObjects(objects) {
        if (!objects)
            return {}; // Objects are optional
        if (typeof objects !== 'object') {
            throw new Error('Objects must be an object mapping object IDs to object definitions');
        }
        const objectsData = objects;
        const validatedObjects = {};
        for (const [objectId, objectData] of Object.entries(objectsData)) {
            if (!objectData || typeof objectData !== 'object') {
                throw new Error(`Object ${objectId} must be an object`);
            }
            const obj = objectData;
            this.validateRequiredFields(obj, ['name', 'description', 'traits', 'allowedActions']);
            validatedObjects[objectId] = {
                id: objectId,
                name: this.validateString(obj.name, `objects.${objectId}.name`),
                description: this.validateString(obj.description, `objects.${objectId}.description`),
                traits: this.validateStringArray(obj.traits, `objects.${objectId}.traits`),
                states: this.validateObjectStates(obj.states, objectId),
                allowedActions: this.validateStringArray(obj.allowedActions, `objects.${objectId}.allowedActions`),
                containedObjects: obj.containedObjects ? this.validateStringArray(obj.containedObjects, `objects.${objectId}.containedObjects`) : []
            };
        }
        return validatedObjects;
    }
    /**
     * Validate character definitions
     */
    static validateCharacters(characters) {
        if (!characters)
            return {}; // Characters are optional
        if (typeof characters !== 'object') {
            throw new Error('Characters must be an object mapping character IDs to character definitions');
        }
        const charactersData = characters;
        const validatedCharacters = {};
        for (const [characterId, characterData] of Object.entries(charactersData)) {
            if (!characterData || typeof characterData !== 'object') {
                throw new Error(`Character ${characterId} must be an object`);
            }
            const character = characterData;
            this.validateRequiredFields(character, ['name', 'description', 'dialogue', 'defaultResponse']);
            validatedCharacters[characterId] = {
                id: characterId,
                name: this.validateString(character.name, `characters.${characterId}.name`),
                description: this.validateString(character.description, `characters.${characterId}.description`),
                dialogue: this.validateDialogue(character.dialogue, characterId),
                inventory: this.validateStringArray(character.inventory || [], `characters.${characterId}.inventory`),
                defaultResponse: this.validateString(character.defaultResponse, `characters.${characterId}.defaultResponse`),
                mood: character.mood ? this.validateString(character.mood, `characters.${characterId}.mood`) : undefined
            };
        }
        return validatedCharacters;
    }
    /**
     * Validate action definitions
     */
    static validateActions(actions) {
        if (!actions)
            return {}; // Actions are optional
        if (typeof actions !== 'object') {
            throw new Error('Actions must be an object mapping action names to action definitions');
        }
        const actionsData = actions;
        const validatedActions = {};
        for (const [actionName, actionData] of Object.entries(actionsData)) {
            if (!actionData || typeof actionData !== 'object') {
                throw new Error(`Action ${actionName} must be an object`);
            }
            const action = actionData;
            this.validateRequiredFields(action, ['patterns', 'handler']);
            validatedActions[actionName] = {
                name: actionName,
                patterns: this.validateStringArray(action.patterns, `actions.${actionName}.patterns`),
                requiresObject: typeof action.requiresObject === 'boolean' ? action.requiresObject : false,
                requiresTarget: typeof action.requiresTarget === 'boolean' ? action.requiresTarget : false,
                handler: this.validateString(action.handler, `actions.${actionName}.handler`)
            };
        }
        return validatedActions;
    }
    /**
     * Validate initial game state
     */
    static validateInitialState(initialState) {
        if (!initialState || typeof initialState !== 'object') {
            throw new Error('Initial state must be an object');
        }
        const stateData = initialState;
        this.validateRequiredFields(stateData, ['currentRoomId']);
        return {
            currentRoomId: this.validateString(stateData.currentRoomId, 'initialState.currentRoomId'),
            inventory: this.validateStringArray(stateData.inventory || [], 'initialState.inventory'),
            visitedRooms: this.validateStringArray(stateData.visitedRooms || [], 'initialState.visitedRooms'),
            flags: this.validateFlags(stateData.flags),
            counters: this.validateCounters(stateData.counters)
        };
    }
    /**
     * Validate object states (record of boolean values)
     */
    static validateObjectStates(states, objectId) {
        if (!states)
            return {};
        if (typeof states !== 'object') {
            throw new Error(`States for object ${objectId} must be an object`);
        }
        const statesData = states;
        const validatedStates = {};
        for (const [stateName, stateValue] of Object.entries(statesData)) {
            if (typeof stateValue !== 'boolean') {
                throw new Error(`State ${stateName} for object ${objectId} must be a boolean value`);
            }
            validatedStates[stateName] = stateValue;
        }
        return validatedStates;
    }
    /**
     * Validate dialogue (record of string values)
     */
    static validateDialogue(dialogue, characterId) {
        if (!dialogue || typeof dialogue !== 'object') {
            throw new Error(`Dialogue for character ${characterId} must be an object`);
        }
        const dialogueData = dialogue;
        const validatedDialogue = {};
        for (const [topic, response] of Object.entries(dialogueData)) {
            validatedDialogue[topic] = this.validateString(response, `characters.${characterId}.dialogue.${topic}`);
        }
        return validatedDialogue;
    }
    /**
     * Validate flags (record of boolean values)
     */
    static validateFlags(flags) {
        if (!flags)
            return {};
        if (typeof flags !== 'object') {
            throw new Error('Flags must be an object');
        }
        const flagsData = flags;
        const validatedFlags = {};
        for (const [flagName, flagValue] of Object.entries(flagsData)) {
            if (typeof flagValue !== 'boolean') {
                throw new Error(`Flag ${flagName} must be a boolean value`);
            }
            validatedFlags[flagName] = flagValue;
        }
        return validatedFlags;
    }
    /**
     * Validate counters (record of number values)
     */
    static validateCounters(counters) {
        if (!counters)
            return {};
        if (typeof counters !== 'object') {
            throw new Error('Counters must be an object');
        }
        const countersData = counters;
        const validatedCounters = {};
        for (const [counterName, counterValue] of Object.entries(countersData)) {
            if (typeof counterValue !== 'number') {
                throw new Error(`Counter ${counterName} must be a numeric value`);
            }
            validatedCounters[counterName] = counterValue;
        }
        return validatedCounters;
    }
    /**
     * Validate that an array of strings is valid
     */
    static validateStringArray(arr, fieldName) {
        if (!arr)
            return [];
        if (!Array.isArray(arr)) {
            throw new Error(`Field ${fieldName} must be an array`);
        }
        return arr.map((item, index) => {
            if (typeof item !== 'string') {
                throw new Error(`Item at index ${index} in ${fieldName} must be a string`);
            }
            return item;
        });
    }
    /**
     * Validate references between entities
     */
    static validateReferences(worldModel) {
        const { rooms, objects, characters, initialState } = worldModel;
        // Check that the initial room exists
        if (!rooms[initialState.currentRoomId]) {
            throw new Error(`Initial room ${initialState.currentRoomId} does not exist`);
        }
        // Check room exits
        for (const [roomId, room] of Object.entries(rooms)) {
            for (const exit of room.exits) {
                if (!rooms[exit.targetRoomId]) {
                    throw new Error(`Room ${roomId} has an exit to non-existent room ${exit.targetRoomId}`);
                }
                if (exit.keyId && !objects[exit.keyId]) {
                    throw new Error(`Room ${roomId} has an exit requiring non-existent key ${exit.keyId}`);
                }
            }
            // Check room objects
            for (const objectId of room.objects) {
                if (!objects[objectId]) {
                    throw new Error(`Room ${roomId} contains non-existent object ${objectId}`);
                }
            }
            // Check room characters
            for (const characterId of room.characters) {
                if (!characters[characterId]) {
                    throw new Error(`Room ${roomId} contains non-existent character ${characterId}`);
                }
            }
        }
        // Check object containment
        for (const [objectId, object] of Object.entries(objects)) {
            if (object.containedObjects) {
                for (const containedId of object.containedObjects) {
                    if (!objects[containedId]) {
                        throw new Error(`Object ${objectId} contains non-existent object ${containedId}`);
                    }
                }
            }
        }
        // Check character inventory
        for (const [characterId, character] of Object.entries(characters)) {
            for (const objectId of character.inventory) {
                if (!objects[objectId]) {
                    throw new Error(`Character ${characterId} has non-existent object ${objectId} in inventory`);
                }
            }
        }
        // Check player inventory
        for (const objectId of initialState.inventory) {
            if (!objects[objectId]) {
                throw new Error(`Initial inventory contains non-existent object ${objectId}`);
            }
        }
    }
}
exports.YamlWorldParser = YamlWorldParser;
//# sourceMappingURL=yaml-parser.js.map