/**
 * ROOM MANAGEMENT SYSTEM - SIMPLIFIED DEPTH LAYERING APPROACH
 * ===========================================================
 * 
 * This system implements a simplified depth-based layering approach where all elements
 * use their world Y position + layer offset for depth calculation.
 * 
 * DEPTH CALCULATION PHILOSOPHY:
 * -----------------------------
 * 1. **World Y Position**: All depth calculations are based on the world Y position
 *    of the element (bottom of sprites, room ground level).
 * 
 * 2. **Layer Offsets**: Each element type has a fixed layer offset added to its Y position
 *    to create proper layering hierarchy.
 * 
 * 3. **Room Y Offset**: Room Y position is considered to be 2 tiles south of the actual
 *    room position (where door sprites are positioned).
 * 
 * DEPTH HIERARCHY:
 * ----------------
 * Room Layers (world Y + layer offset):
 * - Floor:     roomWorldY + 0.1
 * - Collision: roomWorldY + 0.15  
 * - Walls:     roomWorldY + 0.2
 * - Props:     roomWorldY + 0.3
 * - Other:     roomWorldY + 0.4
 * 
 * Interactive Elements (world Y + layer offset):
 * - Doors:     doorY + 0.45 (between room tiles and sprites)
 * - Door Tops: doorY + 0.55 (above doors, below sprites)
 * - Animated Doors: doorBottomY + 0.45 (bottom Y + door layer offset)
 * - Animated Door Tops: doorBottomY + 0.55 (bottom Y + door top layer offset)
 * - Player:    playerBottomY + 0.5 (dynamic based on Y position)
 * - Objects:   objectBottomY + 0.5 (dynamic based on Y position)
 * 
 * DEPTH CALCULATION CONSISTENCY:
 * ------------------------------
 * ✅ All elements use world Y position + layer offset
 * ✅ Room Y is 2 tiles south of room position
 * ✅ Player and objects use bottom Y position
 * ✅ Simple and consistent across all elements
 */

// Room management system
import {
    TILE_SIZE,
    DOOR_ALIGN_OVERLAP,
    GRID_SIZE,
    INTERACTION_RANGE_SQ,
    INTERACTION_CHECK_INTERVAL,
    GRID_UNIT_WIDTH_TILES,
    GRID_UNIT_HEIGHT_TILES,
    VISUAL_TOP_TILES,
    GRID_UNIT_WIDTH_PX,
    GRID_UNIT_HEIGHT_PX
} from '../utils/constants.js?v=8';

// Import the new system modules
import { initializeDoors, createDoorSpritesForRoom, checkDoorTransitions, updateDoorSpritesVisibility } from '../systems/doors.js';
import { initializeObjectPhysics, setupChairCollisions, setupExistingChairsWithNewRoom, calculateChairSpinDirection, updateSwivelChairRotation, updateSpriteDepth } from '../systems/object-physics.js';
import { initializePlayerEffects, createPlayerBumpEffect, createPlantBumpEffect } from '../systems/player-effects.js';
import { initializeCollision, createWallCollisionBoxes, removeTilesUnderDoor, removeWallTilesForDoorInRoom, removeWallTilesAtWorldPosition } from '../systems/collision.js';
import { NPCPathfindingManager } from '../systems/npc-pathfinding.js?v=2';
import NPCSpriteManager from '../systems/npc-sprites.js?v=3';

export let rooms = {};
export let currentRoom = '';
export let currentPlayerRoom = '';
// Track which rooms have been DISCOVERED by the player
// NOTE: "Discovered" means the player has ENTERED the room via door transition.
// This is separate from "revealed" (graphics visible). Rooms can be revealed
// (loaded for graphics/performance) without being discovered (player hasn't entered yet).
// This distinction is important for NPC event triggers like "room_discovered".
export let discoveredRooms = new Set();

// Pathfinding manager for NPC patrol routes
export let pathfindingManager = null;

// Helper function to check if a position overlaps with existing items
function isPositionOverlapping(x, y, roomId, itemSize = TILE_SIZE) {
    const room = rooms[roomId];
    if (!room || !room.objects) return false;
    
    // Check against all existing objects in the room
    for (const obj of Object.values(room.objects)) {
        if (!obj || !obj.active) continue;
        
        // Calculate overlap with some padding
        const padding = TILE_SIZE * 0.5; // Half tile padding
        const objLeft = obj.x - padding;
        const objRight = obj.x + obj.width + padding;
        const objTop = obj.y - padding;
        const objBottom = obj.y + obj.height + padding;
        
        const newLeft = x;
        const newRight = x + itemSize;
        const newTop = y;
        const newBottom = y + itemSize;
        
        // Check for overlap
        if (newLeft < objRight && newRight > objLeft && 
            newTop < objBottom && newBottom > objTop) {
            return true; // Overlap detected
        }
    }
    
    return false; // No overlap
}

// Make discoveredRooms available globally
window.discoveredRooms = discoveredRooms;
let gameRef = null;

// ===== ITEM POOL MANAGEMENT (PHASE 2 IMPROVEMENTS) =====
// Moved to module level to avoid temporal dead zone errors
// and improve performance (defined once, not on every room load)

/**
 * Manages the collection of available Tiled items organized by type
 * Provides unified interface for finding, reserving, and tracking items
 * 
 * This class centralizes item pool logic to improve maintainability
 * while preserving all existing matching behavior
 */
class TiledItemPool {
    constructor(objectsByLayer, map) {
        this.itemsByType = {};              // Regular items (non-table) indexed by type
        this.tableItemsByType = {};         // Regular table items indexed by type
        this.conditionalItemsByType = {};   // Conditional items indexed by type
        this.conditionalTableItemsByType = {}; // Conditional table items indexed by type
        this.reserved = new Set();          // Track reserved items to prevent reuse
        this.map = map;                     // Store map for tileset lookups
        
        this.populateFromLayers(objectsByLayer);
    }
    
    /**
     * Get image name from Tiled object by looking up its GID in tilesets
     */
    getImageNameFromObject(obj) {
        return getImageNameFromObjectWithMap(obj, this.map);
    }
    
    /**
     * Extract base type from image name (e.g., "phone1" -> "phone")
     */
    extractBaseTypeFromImageName(imageName) {
        if (!imageName) {
            return 'unknown';
        }
        
        // Remove numbers and common suffixes to get base type
        let baseType = imageName.replace(/\d+$/, ''); // Remove trailing numbers
        baseType = baseType.replace(/\.png$/, ''); // Remove .png extension
        
        // Handle special cases where scenario uses plural but items use singular
        if (baseType === 'note') {
            const number = imageName.match(/\d+/);
            if (number) {
                baseType = 'notes' + number[0];
            } else {
                baseType = 'notes';
            }
        }
        
        return baseType;
    }
    
    /**
     * Populate pool from Tiled object layers
     * Indexes items by their base type for efficient lookup
     * 
     * Priority order for matching:
     * 1. Regular items (non-table)
     * 2. Regular table items
     * 3. Conditional items (non-table)
     * 4. Conditional table items
     */
    populateFromLayers(objectsByLayer) {
        this.itemsByType = this.indexByType(objectsByLayer.items || []);
        this.tableItemsByType = this.indexByType(objectsByLayer.table_items || []);
        this.conditionalItemsByType = this.indexByType(objectsByLayer.conditional_items || []);
        this.conditionalTableItemsByType = this.indexByType(objectsByLayer.conditional_table_items || []);
    }
    
    /**
     * Index an array of items by their base type
     * Returns object with baseType as keys and arrays of items as values
     */
    indexByType(items) {
        const indexed = {};
        items.forEach(item => {
            const imageName = this.getImageNameFromObject(item);
            if (imageName && imageName !== 'unknown') {
                const baseType = this.extractBaseTypeFromImageName(imageName);
                if (!indexed[baseType]) {
                    indexed[baseType] = [];
                }
                indexed[baseType].push(item);
            }
        });
        return indexed;
    }
    
    /**
     * Find best matching item for a scenario object
     * Searches in strict priority order:
     * 1. Regular items (items layer)
     * 2. Regular table items (table_items layer)
     * 3. Conditional items (conditional_items layer)
     * 4. Conditional table items (conditional_table_items layer)
     * 
     * This ensures unconditional items are ALWAYS used before conditional items.
     * For multiple requests of the same type, exhausts each layer before moving to next.
     * 
     * Skips reserved items to prevent reuse.
     * Returns the matched item or null if no match found
     */
    findMatchFor(scenarioObj) {
        const searchType = scenarioObj.type;
        
        // Search priority: unconditional layers first, then conditional layers
        const searchOrder = [
            this.itemsByType,              // Regular items
            this.tableItemsByType,         // Regular table items (NEW - CRITICAL FIX)
            this.conditionalItemsByType,   // Conditional items
            this.conditionalTableItemsByType // Conditional table items
        ];
        
        for (const indexedItems of searchOrder) {
            const candidates = indexedItems[searchType] || [];
            
            // Find first unreserved item of matching type
            for (const item of candidates) {
                if (!this.isReserved(item)) {
                    return item;
                }
            }
        }
        
        return null;
    }
    
    /**
     * Reserve an item to prevent it from being used again
     * Creates unique identifier from GID and coordinates
     */
    reserve(tiledItem) {
        const itemId = this.getItemId(tiledItem);
        this.reserved.add(itemId);
    }
    
    /**
     * Check if an item has been reserved
     */
    isReserved(tiledItem) {
        const itemId = this.getItemId(tiledItem);
        return this.reserved.has(itemId);
    }
    
    /**
     * Get all unreserved items across all regular (unconditional) layers
     * Used to process background decoration items that weren't used by scenario
     * 
     * NOTE: Returns BOTH regular items AND regular table items, but NOT conditional items.
     * Conditional items should ONLY be created when explicitly requested by scenario.
     * This ensures conditional items stay hidden until the scenario needs them.
     */
    getUnreservedItems() {
        const unreserved = [];
        
        const collectUnreserved = (indexed) => {
            Object.values(indexed).forEach(items => {
                items.forEach(item => {
                    if (!this.isReserved(item)) {
                        unreserved.push(item);
                    }
                });
            });
        };
        
        // Process both regular items and regular table items
        // Exclude conditional items - they should only appear when scenario explicitly requests them
        collectUnreserved(this.itemsByType);
        collectUnreserved(this.tableItemsByType);
        
        return unreserved;
    }
    
    /**
     * Get a unique identifier for an item based on GID and position
     * @private
     */
    getItemId(tiledItem) {
        return `gid_${tiledItem.gid}_x${tiledItem.x}_y${tiledItem.y}`;
    }
}

/**
 * Helper: Apply visual/transform properties from Tiled item to sprite
 * Handles rotation, flipping, and other Tiled-specific properties
 */
function applyTiledProperties(sprite, tiledItem) {
    sprite.setOrigin(0, 0);
    
    // Apply rotation if present
    if (tiledItem.rotation) {
        sprite.setRotation(Phaser.Math.DegToRad(tiledItem.rotation));
    }
    
    // Apply flipping if present
    if (tiledItem.flipX) {
        sprite.setFlipX(true);
    }
    if (tiledItem.flipY) {
        sprite.setFlipY(true);
    }
}

/**
 * Helper: Apply game logic properties from scenario to sprite
 * Stores scenario data and makes sprite interactive
 * 
 * IMPORTANT - KeyPins Normalization:
 * ===================================
 * KeyPins are already normalized by normalizeScenarioKeyPins() in game.js during scenario load.
 * This happens BEFORE any sprites are created, converting 0-100 scale to 25-65 pixel range.
 * 
 * Do NOT normalize keyPins here - it would cause double normalization:
 * - Original: [100, 0, 100, 0]
 * - After 1st normalization (game.js): [65, 25, 65, 25] ✓
 * - After 2nd normalization (here): [51, 35, 51, 35] ✗ WRONG!
 * 
 * The sprite simply receives the already-normalized values from scenarioObj.
 */
function applyScenarioProperties(sprite, scenarioObj, roomId, index) {
    
    sprite.scenarioData = scenarioObj;
    sprite.interactable = true; // Mark scenario items as interactable
    sprite.name = scenarioObj.name;
    sprite.objectId = `${roomId}_${scenarioObj.type}_${index}`;
    sprite.setInteractive({ useHandCursor: true });
    
    // Store all scenario properties for interaction system
    Object.keys(scenarioObj).forEach(key => {
        sprite[key] = scenarioObj[key];
    });
    
    // Ensure phones are always interactable (override scenario data if needed)
    if (scenarioObj.type === 'phone') {
        sprite.interactable = true;
    }
    
    // Log applied data for debugging
    console.log(`Applied scenario data to ${scenarioObj.type}:`, {
        name: scenarioObj.name,
        type: scenarioObj.type,
        takeable: scenarioObj.takeable,
        readable: scenarioObj.readable,
        text: scenarioObj.text,
        observations: scenarioObj.observations,
        keyPins: scenarioObj.keyPins,  // Include keyPins in log
        locked: scenarioObj.locked,
        lockType: scenarioObj.lockType
    });
    
    // Verify keyPins are stored on the sprite
    if (scenarioObj.keyPins) {
        console.log(`✓ keyPins stored on ${roomId}_${scenarioObj.type}: [${sprite.keyPins.join(', ')}]`);
    }
}

/**
 * Helper: Calculate and set depth for sprite based on room position
 * Handles elevation for back-wall items and table items
 */
function setDepthAndStore(sprite, position, roomId, isTableItem = false) {
    // Skip depth calculation for table items - already set in table grouping
    if (!isTableItem) {
        const objectBottomY = sprite.y + sprite.height;
        
        // Calculate elevation for items on the back wall (top 2 tiles of room)
        const roomTopY = position.y;
        const backWallThreshold = roomTopY + (2 * TILE_SIZE);
        const itemBottomY = sprite.y + sprite.height;
        const elevation = itemBottomY < backWallThreshold ? (backWallThreshold - itemBottomY) : 0;
        
        const objectDepth = objectBottomY + 0.5 + elevation;
        sprite.setDepth(objectDepth);
        sprite.elevation = elevation;
    }
    
    // Initially hide the object
    sprite.setVisible(false);
    
    // Store the object
    rooms[roomId].objects[sprite.objectId] = sprite;
}

/**
 * Module-level helper: Get image name from Tiled object
 * Used by both TiledItemPool and other helper functions
 * Note: This function needs to be called with map in context where it's available
 */
function getImageNameFromObjectWithMap(obj, map) {
    if (!map || !map.tilesets) {
        return null;
    }
    
    // Find the tileset that contains this GID
    let tileset = null;
    let localTileId = 0;
    let bestMatch = null;
    let bestMatchIndex = -1;
    
    for (let i = 0; i < map.tilesets.length; i++) {
        const ts = map.tilesets[i];
        const maxGid = ts.tilecount ? ts.firstgid + ts.tilecount : ts.firstgid + 1;
        if (obj.gid >= ts.firstgid && obj.gid < maxGid) {
            // Prefer objects tilesets, and among those, prefer the most recent (highest index)
            if (ts.name === 'objects' || ts.name.includes('objects/') || ts.name.includes('tables/')) {
                if (bestMatchIndex < i) {
                    bestMatch = ts;
                    bestMatchIndex = i;
                    tileset = ts;
                    localTileId = obj.gid - ts.firstgid;
                }
            } else if (!bestMatch) {
                // Fallback to any matching tileset if no objects tileset found
                tileset = ts;
                localTileId = obj.gid - ts.firstgid;
            }
        }
    }
    
    if (tileset && (tileset.name === 'objects' || tileset.name.includes('objects/') || tileset.name.includes('tables/'))) {
        let imageName = null;
        
        if (tileset.images && tileset.images[localTileId]) {
            const imageData = tileset.images[localTileId];
            if (imageData && imageData.name) {
                imageName = imageData.name;
            }
        } else if (tileset.tileData && tileset.tileData[localTileId]) {
            const tileData = tileset.tileData[localTileId];
            if (tileData && tileData.image) {
                const imagePath = tileData.image;
                imageName = imagePath.split('/').pop().replace('.png', '');
            }
        } else if (tileset.name.includes('objects/') || tileset.name.includes('tables/')) {
            imageName = tileset.name.split('/').pop().replace('.png', '');
        }
        
        return imageName;
    }
    
    return null;
}

/**
 * Helper: Create sprite from a matched Tiled item and scenario object
 * Combines visual data (position, image) with game logic (properties)
 */
function createSpriteFromMatch(tiledItem, scenarioObj, position, roomId, index, map) {
    // Use the map-aware version of getImageNameFromObject
    const imageName = getImageNameFromObjectWithMap(tiledItem, map);
    
    // Create sprite at Tiled position with proper coordinate conversion
    // (Tiled Y is top-left, we use bottom-left for isometric perspective)
    const sprite = gameRef.add.sprite(
        Math.round(position.x + tiledItem.x),
        Math.round(position.y + tiledItem.y - tiledItem.height),
        imageName
    );
    
    // Apply Tiled visual properties (rotation, flipping, etc.)
    applyTiledProperties(sprite, tiledItem);
    
    // Apply scenario properties (name, type, interactive data)
    applyScenarioProperties(sprite, scenarioObj, roomId, index);
    
    return sprite;
}

/**
 * Helper: Create sprite at random position when no matching Tiled item found
 * Ensures position doesn't overlap with existing items
 * Items are placed within room GU boundaries with proper padding:
 * - 1 tile (32px) from left and right sides
 * - 2 tiles (64px) from top and bottom
 */
function createSpriteAtRandomPosition(scenarioObj, position, roomId, index, map) {
    // Get actual room dimensions from the tilemap
    let roomWidth = 10 * TILE_SIZE;   // fallback
    let roomHeight = 10 * TILE_SIZE;  // fallback

    if (map) {
        let width, height;
        if (map.json) {
            width = map.json.width;
            height = map.json.height;
        } else if (map.data) {
            width = map.data.width;
            height = map.data.height;
        } else {
            width = map.width;
            height = map.height;
        }

        if (width && height) {
            roomWidth = width * TILE_SIZE;
            roomHeight = height * TILE_SIZE;
        }
    }

    // Apply proper padding based on requirements:
    // - 1 tile (32px) from left and right sides
    // - 2 tiles (64px) from top and bottom
    const paddingX = TILE_SIZE * 1;  // 32px from sides
    const paddingY = TILE_SIZE * 2;  // 64px from top/bottom

    // Find a valid position that doesn't overlap with existing items
    let randomX, randomY;
    let attempts = 0;
    const maxAttempts = 50;

    do {
        randomX = position.x + paddingX + Math.random() * (roomWidth - paddingX * 2);
        randomY = position.y + paddingY + Math.random() * (roomHeight - paddingY * 2);
        attempts++;
    } while (attempts < maxAttempts && isPositionOverlapping(randomX, randomY, roomId, TILE_SIZE));

    const sprite = gameRef.add.sprite(Math.round(randomX), Math.round(randomY), scenarioObj.type);

    console.log(`Created ${scenarioObj.type} at random position - no matching item found (attempts: ${attempts})`);

    // Apply properties
    sprite.setOrigin(0, 0);
    applyScenarioProperties(sprite, scenarioObj, roomId, index);

    return sprite;
}

// ===== END: ITEM POOL MANAGEMENT (PHASE 2 IMPROVEMENTS) =====

// Define scale factors for different object types
const OBJECT_SCALES = {
    'notes': 0.75,
    'key': 0.75,
    'phone': 1,
    'tablet': 0.75,
    'bluetooth_scanner': 0.7
};

// Function to load a room lazily
async function loadRoom(roomId) {
    const gameScenario = window.gameScenario;
    const roomData = gameScenario.rooms[roomId];
    const position = window.roomPositions[roomId];
    
    if (!roomData || !position) {
        console.error(`Cannot load room ${roomId}: missing data or position`);
        return;
    }
    
    console.log(`Lazy loading room: ${roomId}`);
    
    // Load NPCs BEFORE creating room visuals
    // This ensures NPCs are registered before room objects/sprites are created
    if (window.npcLazyLoader && roomData) {
        try {
            await window.npcLazyLoader.loadNPCsForRoom(roomId, roomData);
        } catch (error) {
            console.error(`Failed to load NPCs for room ${roomId}:`, error);
            // Continue with room creation even if NPC loading fails
        }
    }
    
    createRoom(roomId, roomData, position);
    
    // Reveal (make visible) but do NOT mark as discovered
    // The room will only be marked as "discovered" when the player
    // actually enters it via door transition
    revealRoom(roomId);
}

export function initializeRooms(gameInstance) {
    gameRef = gameInstance;
    console.log('Initializing rooms');
    rooms = {};
    window.rooms = rooms; // Ensure window.rooms references the same object
    currentRoom = '';
    currentPlayerRoom = '';
    window.currentPlayerRoom = '';
    
    // Clear discovered rooms on scenario load
    // This ensures "first visit" detection works correctly for NPC events
    discoveredRooms = new Set();
    // Update global reference
    window.discoveredRooms = discoveredRooms;
    
    // Calculate room positions for lazy loading
    window.roomPositions = calculateRoomPositions(gameInstance);
    console.log('Room positions calculated for lazy loading');
    
    // Initialize the new system modules
    initializeDoors(gameInstance, rooms);
    initializeObjectPhysics(gameInstance, rooms);
    initializePlayerEffects(gameInstance, rooms);
    initializeCollision(gameInstance, rooms);
    
    // Initialize pathfinding manager for NPC patrol routes
    pathfindingManager = new NPCPathfindingManager(gameInstance);
    window.pathfindingManager = pathfindingManager;
}

// Door validation is now handled by the sprite-based door system
export function validateDoorsByRoomOverlap() {
    console.log('Door validation is now handled by the sprite-based door system');
}

// Calculate world bounds
export function calculateWorldBounds(gameInstance) {
    console.log('Calculating world bounds');
    const gameScenario = window.gameScenario;
    if (!gameScenario || !gameScenario.rooms) {
        console.error('Game scenario not loaded properly');
        return {
            x: -1800,
            y: -1800,
            width: 3600,
            height: 3600
        };
    }

    let minX = -1800, minY = -1800, maxX = 1800, maxY = 1800;
    
    // Check all room positions to determine world bounds
    const roomPositions = calculateRoomPositions(gameInstance);
    Object.entries(gameScenario.rooms).forEach(([roomId, room]) => {
        const position = roomPositions[roomId];
        if (position) {
            // Get actual room dimensions
            const map = gameInstance.cache.tilemap.get(room.type);
            let roomWidth = 800, roomHeight = 600; // fallback
            
            if (map) {
                let width, height;
                if (map.json) {
                    width = map.json.width;
                    height = map.json.height;
                } else if (map.data) {
                    width = map.data.width;
                    height = map.data.height;
                } else {
                    width = map.width;
                    height = map.height;
                }
                
                if (width && height) {
                    roomWidth = width * TILE_SIZE;   // tile width is TILE_SIZE
                    roomHeight = height * TILE_SIZE; // tile height is TILE_SIZE
                }
            }
            
            minX = Math.min(minX, position.x);
            minY = Math.min(minY, position.y);
            maxX = Math.max(maxX, position.x + roomWidth);
            maxY = Math.max(maxY, position.y + roomHeight);
        }
    });

    // Add some padding
    const padding = 200;
    return {
        x: minX - padding,
        y: minY - padding,
        width: (maxX - minX) + (padding * 2),
        height: (maxY - minY) + (padding * 2)
    };
}

// ============================================================================
// GRID UNIT CONVERSION FUNCTIONS
// ============================================================================

/**
 * Convert tile dimensions to grid units
 *
 * Grid units are the base stacking size: 5 tiles wide × 4 tiles tall
 * (excluding top 2 visual wall tiles)
 *
 * @param {number} widthTiles - Room width in tiles
 * @param {number} heightTiles - Room height in tiles (including visual wall)
 * @returns {{gridWidth: number, gridHeight: number}}
 */
function tilesToGridUnits(widthTiles, heightTiles) {
    const gridWidth = Math.floor(widthTiles / GRID_UNIT_WIDTH_TILES);

    // Subtract visual top wall tiles before calculating grid height
    const stackingHeightTiles = heightTiles - VISUAL_TOP_TILES;
    const gridHeight = Math.floor(stackingHeightTiles / GRID_UNIT_HEIGHT_TILES);

    return { gridWidth, gridHeight };
}

/**
 * Convert grid coordinates to world position
 *
 * Grid coordinates are positions in grid unit space.
 * This converts them to pixel world coordinates.
 *
 * @param {number} gridX - Grid X coordinate
 * @param {number} gridY - Grid Y coordinate
 * @returns {{x: number, y: number}}
 */
function gridToWorld(gridX, gridY) {
    return {
        x: gridX * GRID_UNIT_WIDTH_PX,
        y: gridY * GRID_UNIT_HEIGHT_PX
    };
}

/**
 * Convert world position to grid coordinates
 *
 * @param {number} worldX - World X position in pixels
 * @param {number} worldY - World Y position in pixels
 * @returns {{gridX: number, gridY: number}}
 */
function worldToGrid(worldX, worldY) {
    return {
        gridX: Math.floor(worldX / GRID_UNIT_WIDTH_PX),
        gridY: Math.floor(worldY / GRID_UNIT_HEIGHT_PX)
    };
}

/**
 * Align a world position to the nearest grid boundary
 *
 * Uses Math.floor for consistent rounding of negative numbers
 * (always rounds toward negative infinity)
 *
 * @param {number} worldX - World X position
 * @param {number} worldY - World Y position
 * @returns {{x: number, y: number}}
 */
function alignToGrid(worldX, worldY) {
    // Use floor for consistent rounding of negative numbers
    const gridX = Math.floor(worldX / GRID_UNIT_WIDTH_PX);
    const gridY = Math.floor(worldY / GRID_UNIT_HEIGHT_PX);

    return {
        x: gridX * GRID_UNIT_WIDTH_PX,
        y: gridY * GRID_UNIT_HEIGHT_PX
    };
}

/**
 * Extract room dimensions from Tiled JSON data
 *
 * Reads the tilemap to get room size and calculates:
 * - Tile dimensions
 * - Pixel dimensions
 * - Grid units
 * - Stacking height (for positioning calculations)
 *
 * @param {string} roomId - Room identifier
 * @param {Object} roomData - Room data from scenario
 * @param {Phaser.Game} gameInstance - Game instance for accessing tilemaps
 * @returns {Object} Dimension data
 */
function getRoomDimensions(roomId, roomData, gameInstance) {
    const map = gameInstance.cache.tilemap.get(roomData.type);

    let widthTiles, heightTiles;

    // Try different ways to access tilemap data
    if (map && map.json) {
        widthTiles = map.json.width;
        heightTiles = map.json.height;
    } else if (map && map.data) {
        widthTiles = map.data.width;
        heightTiles = map.data.height;
    } else {
        // Fallback to standard room size
        console.warn(`Could not read dimensions for ${roomId}, using default 10×10`);
        widthTiles = 10;
        heightTiles = 10;
    }

    // Calculate grid units
    const { gridWidth, gridHeight } = tilesToGridUnits(widthTiles, heightTiles);

    // Calculate pixel dimensions
    const widthPx = widthTiles * TILE_SIZE;
    const heightPx = heightTiles * TILE_SIZE;
    const stackingHeightPx = (heightTiles - VISUAL_TOP_TILES) * TILE_SIZE;

    return {
        widthTiles,
        heightTiles,
        widthPx,
        heightPx,
        stackingHeightPx,
        gridWidth,
        gridHeight
    };
}

// ============================================================================
// VALIDATION FUNCTIONS
// ============================================================================

/**
 * Validate that a room's dimensions are multiples of grid units
 *
 * @param {string} roomId - Room identifier
 * @param {Object} dimensions - Room dimensions from getRoomDimensions
 * @returns {{valid: boolean, errors: string[]}}
 */
function validateRoomSize(roomId, dimensions) {
    const errors = [];

    // Check if width is multiple of grid unit width
    const widthRemainder = dimensions.widthTiles % GRID_UNIT_WIDTH_TILES;
    if (widthRemainder !== 0) {
        errors.push(`Room ${roomId} width ${dimensions.widthTiles} tiles is not a multiple of ${GRID_UNIT_WIDTH_TILES} (grid unit width). Remainder: ${widthRemainder} tiles`);
    }

    // Check if stacking height is multiple of grid unit height
    const stackingHeightTiles = dimensions.heightTiles - VISUAL_TOP_TILES;
    const heightRemainder = stackingHeightTiles % GRID_UNIT_HEIGHT_TILES;
    if (heightRemainder !== 0) {
        errors.push(`Room ${roomId} stacking height ${stackingHeightTiles} tiles is not a multiple of ${GRID_UNIT_HEIGHT_TILES} (grid unit height). Remainder: ${heightRemainder} tiles`);
    }

    return {
        valid: errors.length === 0,
        errors
    };
}

/**
 * Validate that all room positions are grid-aligned
 *
 * @param {Object} positions - Map of roomId -> {x, y}
 * @returns {{valid: boolean, errors: string[]}}
 */
function validateGridAlignment(positions) {
    const errors = [];

    Object.entries(positions).forEach(([roomId, pos]) => {
        // Check X alignment
        const xRemainder = pos.x % GRID_UNIT_WIDTH_PX;
        if (xRemainder !== 0) {
            errors.push(`Room ${roomId} X position ${pos.x} is not grid-aligned (remainder: ${xRemainder}px, should be multiple of ${GRID_UNIT_WIDTH_PX}px)`);
        }

        // Check Y alignment
        const yRemainder = pos.y % GRID_UNIT_HEIGHT_PX;
        if (yRemainder !== 0) {
            errors.push(`Room ${roomId} Y position ${pos.y} is not grid-aligned (remainder: ${yRemainder}px, should be multiple of ${GRID_UNIT_HEIGHT_PX}px)`);
        }
    });

    return {
        valid: errors.length === 0,
        errors
    };
}

/**
 * Check if two rooms overlap
 *
 * @param {string} roomId1 - First room ID
 * @param {string} roomId2 - Second room ID
 * @param {Object} positions - Map of roomId -> {x, y}
 * @param {Object} dimensions - Map of roomId -> dimensions
 * @returns {boolean} True if rooms overlap
 */
function roomsOverlap(roomId1, roomId2, positions, dimensions) {
    const pos1 = positions[roomId1];
    const dim1 = dimensions[roomId1];
    const pos2 = positions[roomId2];
    const dim2 = dimensions[roomId2];

    // Check for overlap using AABB (Axis-Aligned Bounding Box) collision
    const overlap = !(
        pos1.x + dim1.widthPx <= pos2.x ||
        pos2.x + dim2.widthPx <= pos1.x ||
        pos1.y + dim1.stackingHeightPx <= pos2.y ||
        pos2.y + dim2.stackingHeightPx <= pos1.y
    );

    return overlap;
}

/**
 * Validate that no rooms overlap
 *
 * @param {Object} positions - Map of roomId -> {x, y}
 * @param {Object} dimensions - Map of roomId -> dimensions
 * @returns {{valid: boolean, errors: string[]}}
 */
function validateNoOverlaps(positions, dimensions) {
    const errors = [];
    const roomIds = Object.keys(positions);

    // Check each pair of rooms
    for (let i = 0; i < roomIds.length; i++) {
        for (let j = i + 1; j < roomIds.length; j++) {
            const roomId1 = roomIds[i];
            const roomId2 = roomIds[j];

            if (roomsOverlap(roomId1, roomId2, positions, dimensions)) {
                const pos1 = positions[roomId1];
                const pos2 = positions[roomId2];
                errors.push(`Rooms ${roomId1} and ${roomId2} overlap! ${roomId1} at (${pos1.x}, ${pos1.y}), ${roomId2} at (${pos2.x}, ${pos2.y})`);
            }
        }
    }

    return {
        valid: errors.length === 0,
        errors
    };
}

/**
 * Validate all room layout constraints
 *
 * @param {Object} dimensions - Map of roomId -> dimensions
 * @param {Object} positions - Map of roomId -> {x, y}
 * @returns {{valid: boolean, errors: string[], warnings: string[]}}
 */
function validateRoomLayout(dimensions, positions) {
    const errors = [];
    const warnings = [];

    console.log('\n=== Validating Room Layout ===');

    // Validate room sizes
    console.log('Validating room sizes...');
    Object.entries(dimensions).forEach(([roomId, dim]) => {
        const result = validateRoomSize(roomId, dim);
        if (!result.valid) {
            warnings.push(...result.errors); // Size issues are warnings, not errors
        }
    });

    // Validate grid alignment
    console.log('Validating grid alignment...');
    const alignmentResult = validateGridAlignment(positions);
    if (!alignmentResult.valid) {
        errors.push(...alignmentResult.errors);
    }

    // Validate no overlaps
    console.log('Validating room overlaps...');
    const overlapResult = validateNoOverlaps(positions, dimensions);
    if (!overlapResult.valid) {
        errors.push(...overlapResult.errors);
    }

    const valid = errors.length === 0;

    console.log(`Validation ${valid ? 'PASSED' : 'FAILED'}`);
    if (warnings.length > 0) {
        console.log(`${warnings.length} warnings:`);
        warnings.forEach(w => console.warn(`  ⚠️  ${w}`));
    }
    if (errors.length > 0) {
        console.log(`${errors.length} errors:`);
        errors.forEach(e => console.error(`  ❌ ${e}`));
    }

    return { valid, errors, warnings };
}

// ============================================================================
// ROOM POSITIONING FUNCTIONS
// ============================================================================

/**
 * Position a single room to the north of current room
 */
function positionNorthSingle(currentRoom, connectedRoom, currentPos, dimensions) {
    const currentDim = dimensions[currentRoom];
    const connectedDim = dimensions[connectedRoom];

    // Center the connected room above current room
    const x = currentPos.x + (currentDim.widthPx - connectedDim.widthPx) / 2;
    const y = currentPos.y - connectedDim.stackingHeightPx;

    // Align to grid using floor (consistent rounding for negatives)
    return alignToGrid(x, y);
}

/**
 * Validate if multiple connections can fit in the given direction
 * Returns true if valid, false with console error if invalid
 */
function validateMultipleConnections(direction, currentRoom, connectedRooms, currentDim, dimensions) {
    if (direction === 'north' || direction === 'south') {
        // Check if rooms can fit side-by-side when centered on door positions
        const edgeInset = TILE_SIZE * 1.5; // 48px
        const availableWidth = currentDim.widthPx - (edgeInset * 2);
        const doorCount = connectedRooms.length;
        const doorSpacing = availableWidth / (doorCount - 1);

        // Calculate total span of rooms when centered on doors
        let minX = Infinity;
        let maxX = -Infinity;

        connectedRooms.forEach((roomId, index) => {
            const connectedDim = dimensions[roomId];
            const doorX = edgeInset + (doorSpacing * index);
            const roomLeft = doorX - (connectedDim.widthPx / 2);
            const roomRight = doorX + (connectedDim.widthPx / 2);

            minX = Math.min(minX, roomLeft);
            maxX = Math.max(maxX, roomRight);
        });

        const totalSpan = maxX - minX;
        const overhang = Math.max(0, totalSpan - currentDim.widthPx);

        if (overhang > GRID_UNIT_WIDTH_PX / 2) { // Allow some small overhang (half grid unit)
            console.error(`❌ VALIDATION ERROR: Room "${currentRoom}" (${currentDim.gridWidth}×${currentDim.gridHeight} GU, ${currentDim.widthPx}px wide) has ${doorCount} ${direction} connections, but they don't fit!`);
            console.error(`   Connected rooms total span: ${totalSpan.toFixed(0)}px, overhang: ${overhang.toFixed(0)}px`);
            console.error(`   Recommendation: Reduce number of connections to ${Math.floor(doorCount * currentDim.widthPx / totalSpan)} or use a wider room (${Math.ceil(totalSpan / GRID_UNIT_WIDTH_PX)}+ GU)`);
            connectedRooms.forEach((roomId, index) => {
                const dim = dimensions[roomId];
                console.error(`   - ${roomId}: ${dim.gridWidth}×${dim.gridHeight} GU (${dim.widthPx}px wide)`);
            });
            return false;
        }
    } else if (direction === 'east' || direction === 'west') {
        // Check if rooms can fit stacked vertically when centered on door positions
        const topY = TILE_SIZE * 2;
        const bottomY = currentDim.heightPx - (TILE_SIZE * 3);
        const doorSpacing = (bottomY - topY) / (connectedRooms.length - 1);

        // Calculate total span of rooms when centered on doors
        let minY = Infinity;
        let maxY = -Infinity;

        connectedRooms.forEach((roomId, index) => {
            const connectedDim = dimensions[roomId];
            const doorY = topY + (doorSpacing * index);
            // Door is positioned at 2 tiles from top of room
            const roomTop = doorY - (TILE_SIZE * 2);
            const roomBottom = roomTop + connectedDim.heightPx;

            minY = Math.min(minY, roomTop);
            maxY = Math.max(maxY, roomBottom);
        });

        const totalSpan = maxY - minY;
        const overhang = Math.max(0, totalSpan - currentDim.heightPx);

        if (overhang > GRID_UNIT_HEIGHT_PX / 2) { // Allow some small overhang (half grid unit)
            console.error(`❌ VALIDATION ERROR: Room "${currentRoom}" (${currentDim.gridWidth}×${currentDim.gridHeight} GU, ${currentDim.heightPx}px tall) has ${connectedRooms.length} ${direction} connections, but they don't fit!`);
            console.error(`   Connected rooms total span: ${totalSpan.toFixed(0)}px, overhang: ${overhang.toFixed(0)}px`);
            console.error(`   Recommendation: Reduce number of connections or use a taller room`);
            connectedRooms.forEach((roomId, index) => {
                const dim = dimensions[roomId];
                console.error(`   - ${roomId}: ${dim.gridWidth}×${dim.gridHeight} GU (${dim.heightPx}px tall)`);
            });
            return false;
        }
    }

    return true;
}

/**
 * Position multiple rooms to the north of current room
 * CRITICAL: Ensures all connected rooms have at least 1 GU overlap with current room's edge
 */
function positionNorthMultiple(currentRoom, connectedRooms, currentPos, dimensions) {
    const currentDim = dimensions[currentRoom];
    const positions = {};

    // Calculate total width of all connected rooms
    const totalWidth = connectedRooms.reduce((sum, roomId) => {
        return sum + dimensions[roomId].widthPx;
    }, 0);

    // Calculate starting X position to center the group over current room
    const groupStartX = currentPos.x + (currentDim.widthPx - totalWidth) / 2;

    // Align the starting position to grid
    let alignedStartX = alignToGrid(groupStartX, 0).x;

    // CRITICAL: Ensure each room has at least 1 GU overlap with parent room
    // After grid alignment, check if all rooms will have minimum overlap
    const minOverlap = GRID_UNIT_WIDTH_PX;

    // Check first room overlap
    const firstDim = dimensions[connectedRooms[0]];
    const firstRoomEnd = alignedStartX + firstDim.widthPx;
    const firstOverlap = Math.min(firstRoomEnd, currentPos.x + currentDim.widthPx) - Math.max(alignedStartX, currentPos.x);

    if (firstOverlap < minOverlap) {
        // First room doesn't have enough overlap, shift group to the right
        alignedStartX = currentPos.x - firstDim.widthPx + minOverlap;
        alignedStartX = alignToGrid(alignedStartX, 0).x;
    }

    // Check last room overlap (after potential adjustment for first room)
    const lastRoomStartX = alignedStartX + totalWidth - dimensions[connectedRooms[connectedRooms.length - 1]].widthPx;
    const lastRoomEndX = alignedStartX + totalWidth;
    const lastOverlap = Math.min(lastRoomEndX, currentPos.x + currentDim.widthPx) - Math.max(lastRoomStartX, currentPos.x);

    if (lastOverlap < minOverlap) {
        // Last room doesn't have enough overlap, shift group to the left
        const lastDim = dimensions[connectedRooms[connectedRooms.length - 1]];
        alignedStartX = currentPos.x + currentDim.widthPx - totalWidth - lastDim.widthPx + minOverlap;
        alignedStartX = alignToGrid(alignedStartX, 0).x;
    }

    // Position each room side-by-side starting from adjusted aligned position
    let currentX = alignedStartX;

    connectedRooms.forEach(roomId => {
        const connectedDim = dimensions[roomId];

        // Calculate Y position based on room's stacking height
        const roomY = currentPos.y - connectedDim.stackingHeightPx;
        const alignedY = alignToGrid(0, roomY).y;

        positions[roomId] = { x: currentX, y: alignedY };

        // Move X position for next room
        currentX += connectedDim.widthPx;
    });

    return positions;
}

/**
 * Position a single room to the south of current room
 */
function positionSouthSingle(currentRoom, connectedRoom, currentPos, dimensions) {
    const currentDim = dimensions[currentRoom];
    const connectedDim = dimensions[connectedRoom];

    // Center the connected room below current room
    const x = currentPos.x + (currentDim.widthPx - connectedDim.widthPx) / 2;
    const y = currentPos.y + currentDim.stackingHeightPx;

    // Align to grid
    return alignToGrid(x, y);
}

/**
 * Position multiple rooms to the south of current room
 * CRITICAL: Ensures all connected rooms have at least 1 GU overlap with current room's edge
 */
function positionSouthMultiple(currentRoom, connectedRooms, currentPos, dimensions) {
    const currentDim = dimensions[currentRoom];
    const positions = {};

    // Calculate total width of all connected rooms
    const totalWidth = connectedRooms.reduce((sum, roomId) => {
        return sum + dimensions[roomId].widthPx;
    }, 0);

    // Calculate starting X position to center the group over current room
    const groupStartX = currentPos.x + (currentDim.widthPx - totalWidth) / 2;

    // Align the starting position to grid
    let alignedStartX = alignToGrid(groupStartX, 0).x;

    // CRITICAL: Ensure each room has at least 1 GU overlap with parent room
    const minOverlap = GRID_UNIT_WIDTH_PX;

    // Check first room overlap
    const firstDim = dimensions[connectedRooms[0]];
    const firstRoomEnd = alignedStartX + firstDim.widthPx;
    const firstOverlap = Math.min(firstRoomEnd, currentPos.x + currentDim.widthPx) - Math.max(alignedStartX, currentPos.x);

    if (firstOverlap < minOverlap) {
        // First room doesn't have enough overlap, shift group to the right
        alignedStartX = currentPos.x - firstDim.widthPx + minOverlap;
        alignedStartX = alignToGrid(alignedStartX, 0).x;
    }

    // Check last room overlap
    const lastRoomStartX = alignedStartX + totalWidth - dimensions[connectedRooms[connectedRooms.length - 1]].widthPx;
    const lastRoomEndX = alignedStartX + totalWidth;
    const lastOverlap = Math.min(lastRoomEndX, currentPos.x + currentDim.widthPx) - Math.max(lastRoomStartX, currentPos.x);

    if (lastOverlap < minOverlap) {
        // Last room doesn't have enough overlap, shift group to the left
        const lastDim = dimensions[connectedRooms[connectedRooms.length - 1]];
        alignedStartX = currentPos.x + currentDim.widthPx - totalWidth - lastDim.widthPx + minOverlap;
        alignedStartX = alignToGrid(alignedStartX, 0).x;
    }

    // Position each room side-by-side
    let currentX = alignedStartX;
    const y = currentPos.y + currentDim.stackingHeightPx;
    const alignedY = alignToGrid(0, y).y;

    connectedRooms.forEach(roomId => {
        const connectedDim = dimensions[roomId];

        positions[roomId] = { x: currentX, y: alignedY };

        // Move X position for next room
        currentX += connectedDim.widthPx;
    });

    return positions;
}

/**
 * Position a single room to the east of current room
 */
function positionEastSingle(currentRoom, connectedRoom, currentPos, dimensions) {
    const currentDim = dimensions[currentRoom];
    const connectedDim = dimensions[connectedRoom];

    // Position to the right, aligned at north edge
    const x = currentPos.x + currentDim.widthPx;
    const y = currentPos.y;

    // Align to grid
    return alignToGrid(x, y);
}

/**
 * Position multiple rooms to the east of current room
 * CRITICAL: Ensures all connected rooms have at least 1 GU overlap with current room's edge
 */
function positionEastMultiple(currentRoom, connectedRooms, currentPos, dimensions) {
    const currentDim = dimensions[currentRoom];
    const positions = {};

    // Calculate total height of all connected rooms (using stacking height)
    const totalHeight = connectedRooms.reduce((sum, roomId) => {
        return sum + dimensions[roomId].stackingHeightPx;
    }, 0);

    // Calculate starting Y position to center the group along current room's edge
    const groupStartY = currentPos.y + (currentDim.stackingHeightPx - totalHeight) / 2;

    // Align the starting position to grid
    let alignedStartY = alignToGrid(0, groupStartY).y;

    // CRITICAL: Ensure each room has at least 1 GU overlap with parent room
    const minOverlap = GRID_UNIT_HEIGHT_PX;

    // Check first room overlap
    const firstDim = dimensions[connectedRooms[0]];
    const firstRoomEnd = alignedStartY + firstDim.stackingHeightPx;
    const firstOverlap = Math.min(firstRoomEnd, currentPos.y + currentDim.stackingHeightPx) - Math.max(alignedStartY, currentPos.y);

    if (firstOverlap < minOverlap) {
        // First room doesn't have enough overlap, shift group down
        alignedStartY = currentPos.y - firstDim.stackingHeightPx + minOverlap;
        alignedStartY = alignToGrid(0, alignedStartY).y;
    }

    // Check last room overlap
    const lastRoomStartY = alignedStartY + totalHeight - dimensions[connectedRooms[connectedRooms.length - 1]].stackingHeightPx;
    const lastRoomEndY = alignedStartY + totalHeight;
    const lastOverlap = Math.min(lastRoomEndY, currentPos.y + currentDim.stackingHeightPx) - Math.max(lastRoomStartY, currentPos.y);

    if (lastOverlap < minOverlap) {
        // Last room doesn't have enough overlap, shift group up
        const lastDim = dimensions[connectedRooms[connectedRooms.length - 1]];
        alignedStartY = currentPos.y + currentDim.stackingHeightPx - totalHeight - lastDim.stackingHeightPx + minOverlap;
        alignedStartY = alignToGrid(0, alignedStartY).y;
    }

    // Position each room stacked vertically
    const x = currentPos.x + currentDim.widthPx;
    const alignedX = alignToGrid(x, 0).x;
    let currentY = alignedStartY;

    connectedRooms.forEach(roomId => {
        const connectedDim = dimensions[roomId];

        positions[roomId] = { x: alignedX, y: currentY };

        // Move Y position for next room
        currentY += connectedDim.stackingHeightPx;
    });

    return positions;
}

/**
 * Position a single room to the west of current room
 */
function positionWestSingle(currentRoom, connectedRoom, currentPos, dimensions) {
    const connectedDim = dimensions[connectedRoom];

    // Position to the left, aligned at north edge
    const x = currentPos.x - connectedDim.widthPx;
    const y = currentPos.y;

    // Align to grid
    return alignToGrid(x, y);
}

/**
 * Position multiple rooms to the west of current room
 * CRITICAL: Ensures all connected rooms have at least 1 GU overlap with current room's edge
 */
function positionWestMultiple(currentRoom, connectedRooms, currentPos, dimensions) {
    const currentDim = dimensions[currentRoom];
    const positions = {};

    // Calculate total height of all connected rooms (using stacking height)
    const totalHeight = connectedRooms.reduce((sum, roomId) => {
        return sum + dimensions[roomId].stackingHeightPx;
    }, 0);

    // Calculate starting Y position to center the group along current room's edge
    const groupStartY = currentPos.y + (currentDim.stackingHeightPx - totalHeight) / 2;

    // Align the starting position to grid
    let alignedStartY = alignToGrid(0, groupStartY).y;

    // CRITICAL: Ensure each room has at least 1 GU overlap with parent room
    const minOverlap = GRID_UNIT_HEIGHT_PX;

    // Check first room overlap
    const firstDim = dimensions[connectedRooms[0]];
    const firstRoomEnd = alignedStartY + firstDim.stackingHeightPx;
    const firstOverlap = Math.min(firstRoomEnd, currentPos.y + currentDim.stackingHeightPx) - Math.max(alignedStartY, currentPos.y);

    if (firstOverlap < minOverlap) {
        // First room doesn't have enough overlap, shift group down
        alignedStartY = currentPos.y - firstDim.stackingHeightPx + minOverlap;
        alignedStartY = alignToGrid(0, alignedStartY).y;
    }

    // Check last room overlap
    const lastRoomStartY = alignedStartY + totalHeight - dimensions[connectedRooms[connectedRooms.length - 1]].stackingHeightPx;
    const lastRoomEndY = alignedStartY + totalHeight;
    const lastOverlap = Math.min(lastRoomEndY, currentPos.y + currentDim.stackingHeightPx) - Math.max(lastRoomStartY, currentPos.y);

    if (lastOverlap < minOverlap) {
        // Last room doesn't have enough overlap, shift group up
        const lastDim = dimensions[connectedRooms[connectedRooms.length - 1]];
        alignedStartY = currentPos.y + currentDim.stackingHeightPx - totalHeight - lastDim.stackingHeightPx + minOverlap;
        alignedStartY = alignToGrid(0, alignedStartY).y;
    }

    // Position each room stacked vertically
    let currentY = alignedStartY;

    connectedRooms.forEach(roomId => {
        const connectedDim = dimensions[roomId];

        // Position to the left
        const x = currentPos.x - connectedDim.widthPx;
        const alignedX = alignToGrid(x, 0).x;

        positions[roomId] = { x: alignedX, y: currentY };

        // Move Y position for next room
        currentY += connectedDim.stackingHeightPx;
    });

    return positions;
}

/**
 * Route single room positioning to appropriate direction function
 */
function positionSingleRoom(direction, currentRoom, connectedRoom, currentPos, dimensions) {
    switch (direction) {
        case 'north': return positionNorthSingle(currentRoom, connectedRoom, currentPos, dimensions);
        case 'south': return positionSouthSingle(currentRoom, connectedRoom, currentPos, dimensions);
        case 'east': return positionEastSingle(currentRoom, connectedRoom, currentPos, dimensions);
        case 'west': return positionWestSingle(currentRoom, connectedRoom, currentPos, dimensions);
        default:
            console.error(`Unknown direction: ${direction}`);
            return currentPos;
    }
}

/**
 * Route multiple room positioning to appropriate direction function
 */
function positionMultipleRooms(direction, currentRoom, connectedRooms, currentPos, dimensions) {
    switch (direction) {
        case 'north': return positionNorthMultiple(currentRoom, connectedRooms, currentPos, dimensions);
        case 'south': return positionSouthMultiple(currentRoom, connectedRooms, currentPos, dimensions);
        case 'east': return positionEastMultiple(currentRoom, connectedRooms, currentPos, dimensions);
        case 'west': return positionWestMultiple(currentRoom, connectedRooms, currentPos, dimensions);
        default:
            console.error(`Unknown direction: ${direction}`);
            return {};
    }
}

export function calculateRoomPositions(gameInstance) {
    const positions = {};
    const dimensions = {};
    const processed = new Set();
    const queue = [];
    const gameScenario = window.gameScenario;

    console.log('=== NEW ROOM LAYOUT SYSTEM: Starting Room Position Calculations ===');

    // Phase 1: Extract all room dimensions
    console.log('\n--- Phase 1: Extracting Room Dimensions ---');
    Object.entries(gameScenario.rooms).forEach(([roomId, roomData]) => {
        dimensions[roomId] = getRoomDimensions(roomId, roomData, gameInstance);
        console.log(`Room ${roomId} (${roomData.type}): ${dimensions[roomId].widthTiles}×${dimensions[roomId].heightTiles} tiles = ${dimensions[roomId].gridWidth}×${dimensions[roomId].gridHeight} grid units`);
    });

    // Phase 2: Place starting room at origin
    console.log('\n--- Phase 2: Placing Starting Room ---');
    const startRoomId = gameScenario.startRoom;
    positions[startRoomId] = { x: 0, y: 0 };
    processed.add(startRoomId);
    queue.push(startRoomId);
    console.log(`Starting room "${startRoomId}" positioned at (0, 0)`);

    // Phase 3: Process rooms breadth-first
    console.log('\n--- Phase 3: Processing Rooms Breadth-First ---');
    while (queue.length > 0) {
        const currentRoomId = queue.shift();
        const currentRoom = gameScenario.rooms[currentRoomId];
        const currentPos = positions[currentRoomId];

        console.log(`\nProcessing room: ${currentRoomId}`);
        console.log(`  Position: (${currentPos.x}, ${currentPos.y})`);

        // Skip rooms without connections
        if (!currentRoom.connections) {
            console.log(`  No connections for ${currentRoomId}`);
            continue;
        }

        // Process each direction
        ['north', 'south', 'east', 'west'].forEach(direction => {
            const connected = currentRoom.connections[direction];
            if (!connected) return;

            // Convert to array if single connection
            const connectedRooms = Array.isArray(connected) ? connected : [connected];

            // Filter out already processed rooms
            const unprocessed = connectedRooms.filter(roomId => !processed.has(roomId));
            if (unprocessed.length === 0) {
                console.log(`  ${direction}: all rooms already processed`);
                return;
            }

            console.log(`  ${direction}: positioning ${unprocessed.length} room(s) - ${unprocessed.join(', ')}`);

            // Position rooms based on count
            if (unprocessed.length === 1) {
                // Single room connection
                const roomId = unprocessed[0];
                const position = positionSingleRoom(direction, currentRoomId, roomId, currentPos, dimensions);
                positions[roomId] = position;
                processed.add(roomId);
                queue.push(roomId);

                const gridCoords = worldToGrid(position.x, position.y);
                console.log(`    ${roomId}: positioned at world(${position.x}, ${position.y}) = grid(${gridCoords.gridX}, ${gridCoords.gridY})`);
            } else {
                // Multiple room connections - validate first
                const currentDim = dimensions[currentRoomId];
                const isValid = validateMultipleConnections(direction, currentRoomId, unprocessed, currentDim, dimensions);

                if (!isValid) {
                    console.warn(`⚠️  Skipping invalid connections for ${currentRoomId} ${direction}. Layout may be broken.`);
                    // Still process them to avoid breaking the game, but with a warning
                }

                const newPositions = positionMultipleRooms(direction, currentRoomId, unprocessed, currentPos, dimensions);

                unprocessed.forEach(roomId => {
                    positions[roomId] = newPositions[roomId];
                    processed.add(roomId);
                    queue.push(roomId);

                    const gridCoords = worldToGrid(newPositions[roomId].x, newPositions[roomId].y);
                    console.log(`    ${roomId}: positioned at world(${newPositions[roomId].x}, ${newPositions[roomId].y}) = grid(${gridCoords.gridX}, ${gridCoords.gridY})`);
                });
            }
        });
    }

    // Phase 4: Log final positions
    console.log('\n--- Phase 4: Final Room Positions ---');
    Object.entries(positions).forEach(([roomId, pos]) => {
        const gridCoords = worldToGrid(pos.x, pos.y);
        console.log(`${roomId}: world(${pos.x}, ${pos.y}) = grid(${gridCoords.gridX}, ${gridCoords.gridY})`);
    });

    // Phase 5: Validate room layout
    const validation = validateRoomLayout(dimensions, positions);

    // Store dimensions and positions globally for use by door placement and validation
    window.roomDimensions = dimensions;
    window.roomPositions = positions;

    console.log('\n=== Room Position Calculations Complete ===\n');

    // If validation failed, log but don't block (existing scenarios may have issues)
    if (!validation.valid) {
        console.error('⚠️  Room layout validation found errors. The game may not work correctly.');
    }

    return positions;
}

export function createRoom(roomId, roomData, position) {
    try {
        console.log(`Creating room ${roomId} of type ${roomData.type}`);
        const gameScenario = window.gameScenario;
        
        // Build a set of item types that are in startItemsInInventory
        // These should NOT be created as sprites in rooms
        const startInventoryTypes = new Set();
        if (gameScenario && gameScenario.startItemsInInventory && Array.isArray(gameScenario.startItemsInInventory)) {
            gameScenario.startItemsInInventory.forEach(item => {
                startInventoryTypes.add(item.type);
                console.log(`Marking item type "${item.type}" as starting inventory (will not create sprite in rooms)`);
            });
        }
        
        // Safety check: if gameRef is null, use window.game as fallback
        if (!gameRef && window.game) {
            console.log('gameRef was null, using window.game as fallback');
            gameRef = window.game;
        }
        
        if (!gameRef) {
            throw new Error('Game reference is null - cannot create room. This should not happen if called after game initialization.');
        }
        
        const map = gameRef.make.tilemap({ key: roomData.type });
        const tilesets = [];
        
        // Add tilesets
        console.log('Available tilesets:', map.tilesets.map(t => ({
            name: t.name,
            columns: t.columns,
            firstgid: t.firstgid,
            tilecount: t.tilecount
        })));
        
        const regularTilesets = map.tilesets.filter(t => 
            !t.name.includes('Interiors_48x48') && 
            t.name !== 'objects' && // Skip the objects tileset as it's handled separately
            t.name !== 'tables' && // Skip the tables tileset as it's also an ImageCollection
            !t.name.includes('../objects/') && // Skip individual object tilesets
            !t.name.includes('../tables/') && // Skip individual table tilesets
            t.columns > 0 // Only process tilesets with columns (regular tilesets)
        );
        
        console.log('Filtered tilesets to process:', regularTilesets.map(t => t.name));
        
        regularTilesets.forEach(tileset => {
            console.log(`Attempting to add tileset: ${tileset.name}`);
            const loadedTileset = map.addTilesetImage(tileset.name, tileset.name);
            if (loadedTileset) {
                tilesets.push(loadedTileset);
                console.log(`Added regular tileset: ${tileset.name}`);
            } else {
                console.log(`Failed to add tileset: ${tileset.name}`);
            }
        });

        // Initialize room data structure first
        rooms[roomId] = {
            map,
            layers: {},
            wallsLayers: [],
            objects: {},
            position
        };
        
        // Ensure window.rooms is updated
        window.rooms = rooms;

        const layers = rooms[roomId].layers;
        const wallsLayers = rooms[roomId].wallsLayers;
        
        // IMPORTANT: This counter ensures unique layer IDs across ALL rooms and should not be removed
        if (!window.globalLayerCounter) window.globalLayerCounter = 0;

        // Calculate base depth for this room's layers
        // Use world Y position + layer offset (room Y is 2 tiles south of actual room position)
        const roomWorldY = position.y + TILE_SIZE * 2; // Room Y is 2 tiles south of room position
        
        // Create door sprites based on gameScenario connections
        const doorSprites = createDoorSpritesForRoom(roomId, position);
        rooms[roomId].doorSprites = doorSprites;
        console.log(`Stored ${doorSprites.length} door sprites in room ${roomId}`);
        
        // Store door positions for wall tile removal
        const doorPositions = doorSprites.map(doorSprite => ({
            x: doorSprite.x,
            y: doorSprite.y,
            width: doorSprite.body ? doorSprite.body.width : TILE_SIZE,
            height: doorSprite.body ? doorSprite.body.height : TILE_SIZE * 2
        }));

        // Create other layers with appropriate depths
        map.layers.forEach((layerData, index) => {
            // Skip the doors layer since we're using sprite-based doors
            if (layerData.name.toLowerCase().includes('doors')) {
                console.log(`Skipping doors layer: ${layerData.name} in room ${roomId}`);
                return;
            }

            window.globalLayerCounter++;
            const uniqueLayerId = `${roomId}_${layerData.name}_${window.globalLayerCounter}`;
            
            const layer = map.createLayer(index, tilesets, Math.round(position.x), Math.round(position.y));
            if (layer) {
                layer.name = uniqueLayerId;
                // remove tiles under doors
                removeTilesUnderDoor(layer, roomId, position);

                
                // Set depth based on layer type and room position
                if (layerData.name.toLowerCase().includes('floor')) {
                    layer.setDepth(roomWorldY + 0.1);
                    console.log(`Floor layer depth: ${roomWorldY + 0.1}`);
                } else if (layerData.name.toLowerCase().includes('walls')) {
                    layer.setDepth(roomWorldY + 0.2);
                    console.log(`Wall layer depth: ${roomWorldY + 0.2}`);
                    
                    // Remove wall tiles under doors
                    removeTilesUnderDoor(layer, roomId, position);
                    
                    // Set up wall layer (collision disabled - using custom collision boxes instead)
                    try {
                        // Disabled: layer.setCollisionByExclusion([-1]);
                        console.log(`Wall layer ${uniqueLayerId} - using custom collision boxes instead of tile collision`);
                        
                        wallsLayers.push(layer);
                        console.log(`Added wall layer: ${uniqueLayerId}`);
                        
                        // Disabled: Old collision system between player and wall layer
                        // const player = window.player;
                        // if (player && player.body) {
                        //     gameRef.physics.add.collider(player, layer);
                        //     console.log(`Added collision between player and wall layer: ${uniqueLayerId}`);
                        // }
                        
                        // Create thin collision boxes for wall tiles
                        createWallCollisionBoxes(layer, roomId, position);
                    } catch (e) {
                        console.warn(`Error setting up collisions for ${uniqueLayerId}:`, e);
                    }
                } else if (layerData.name.toLowerCase().includes('collision')) {
                    layer.setDepth(roomWorldY + 0.15);
                    console.log(`Collision layer depth: ${roomWorldY + 0.15}`);
                    
                    // Set up collision layer (collision disabled - using custom collision boxes instead)
                    try {
                        // Disabled: layer.setCollisionByExclusion([-1]);
                        console.log(`Collision layer ${uniqueLayerId} - using custom collision boxes instead of tile collision`);
                        
                        // Disabled: Old collision system between player and collision layer
                        // const player = window.player;
                        // if (player && player.body) {
                        //     gameRef.physics.add.collider(player, layer);
                        //     console.log(`Added collision between player and collision layer: ${uniqueLayerId}`);
                        // }
                    } catch (e) {
                        console.warn(`Error setting up collision layer ${uniqueLayerId}:`, e);
                    }
                } else if (layerData.name.toLowerCase().includes('props')) {
                    layer.setDepth(roomWorldY + 0.3);
                    console.log(`Props layer depth: ${roomWorldY + 0.3}`);
                } else {
                    // Other layers (decorations, etc.)
                    layer.setDepth(roomWorldY + 0.4);
                    console.log(`Other layer depth: ${roomWorldY + 0.4}`);
                }
                
                layers[uniqueLayerId] = layer;
                layer.setVisible(false);
                layer.setAlpha(0);
            }
        });

        // Handle new Tiled object layers with grouping logic
        const objectLayers = [
            'tables', 'table_items', 'conditional_table_items', 
            'items', 'conditional_items'
        ];
        
        // First, collect all objects by layer
        const objectsByLayer = {};
        objectLayers.forEach(layerName => {
            const objectLayer = map.getObjectLayer(layerName);
            if (objectLayer && objectLayer.objects.length > 0) {
                objectsByLayer[layerName] = objectLayer.objects;
                console.log(`Collected ${layerName} layer with ${objectLayer.objects.length} objects`);
            }
        });
        
        // Process tables first to establish base positions
        const tableObjects = [];
        if (objectsByLayer.tables) {
            objectsByLayer.tables.forEach(obj => {
                const processedObj = processObject(obj, position, roomId, 'table', map);
                if (processedObj) {
                    tableObjects.push(processedObj);
                }
            });
        }
        
        // Group table items with their closest tables
        const tableGroups = [];
        tableObjects.forEach(table => {
            const group = {
                table: table,
                items: [],
                baseDepth: table.sprite.depth
            };
            tableGroups.push(group);
        });
        
        // NOTE: Table items (both regular and conditional) are now processed through the item pool
        // in processScenarioObjectsWithConditionalMatching(). They will be handled there
        // with proper priority ordering (regular table items before conditional ones).
        
        // Process scenario objects with conditional item matching first
        const usedItems = processScenarioObjectsWithConditionalMatching(roomId, position, objectsByLayer, map);
        
        // Process all non-conditional items (chairs, plants, lamps, PCs, etc.)
        // These should ALWAYS be visible (not conditional)
        // They get default properties if not customized by scenario
        if (objectsByLayer.items) {
            objectsByLayer.items.forEach(obj => {
                const imageName = getImageNameFromObjectWithMap(obj, map);
                // Extract base type from image name
                let baseType = imageName ? imageName.replace(/\d+$/, '').replace(/\.png$/, '') : 'unknown';
                if (baseType === 'note') {
                    const number = imageName ? imageName.match(/\d+/) : null;
                    baseType = number ? 'notes' + number[0] : 'notes';
                }
                
                // Skip if this item type is in starting inventory
                if (startInventoryTypes.has(baseType)) {
                    console.log(`Skipping regular item ${imageName} (baseType: ${baseType}) - marked as starting inventory item`);
                    return;
                }
                
                // Skip if this exact item was used by scenario objects
                // BUT: Create it anyway if we haven't used ALL items of this type
                if (imageName && usedItems.has(imageName)) {
                    console.log(`Skipping regular item ${imageName} (exact item used by scenario)`);
                    return;
                }
                
                // Process the item and store it
                const result = processObject(obj, position, roomId, 'item', map);
                if (result && result.sprite) {
                    // Store unconditional items in the objects collection so they're revealed
                    rooms[roomId].objects[result.sprite.objectId] = result.sprite;
                }
            });
        }
        
        // ===== NEW: ITEM POOL MANAGEMENT (PHASE 2 IMPROVEMENTS) =====
        
        // Helper function to process scenario objects with conditional matching
        function processScenarioObjectsWithConditionalMatching(roomId, position, objectsByLayer, map) {
            const gameScenario = window.gameScenario;
            if (!gameScenario.rooms[roomId].objects) {
                return new Set();
            }
            
            // 1. Initialize item pool with all available Tiled items
            const itemPool = new TiledItemPool(objectsByLayer, map); // Pass map here
            const usedItems = new Set();
            
            console.log(`Processing ${gameScenario.rooms[roomId].objects.length} scenario objects for room ${roomId}`);
            
            // 2. Process each scenario object
            gameScenario.rooms[roomId].objects.forEach((scenarioObj, index) => {
                const objType = scenarioObj.type;
            
                let sprite = null;
                let usedItem = null;
                let isTableItem = false;
                
                console.log(`Looking for scenario object type: ${objType}`);
                console.log(`Available regular items for ${objType}: ${itemPool.itemsByType[objType] ? itemPool.itemsByType[objType].length : 0}`);
                console.log(`Available conditional items for ${objType}: ${itemPool.conditionalItemsByType[objType] ? itemPool.conditionalItemsByType[objType].length : 0}`);
                console.log(`Available conditional table items for ${objType}: ${itemPool.conditionalTableItemsByType[objType] ? itemPool.conditionalTableItemsByType[objType].length : 0}`);
                
                // Find matching Tiled item using centralized pool matching
                usedItem = itemPool.findMatchFor(scenarioObj);
                
                if (usedItem) {
                    // Check which layer this item came from to determine if it's a table item
                    const imageName = itemPool.getImageNameFromObject(usedItem);
                    const baseType = itemPool.extractBaseTypeFromImageName(imageName);
                    
                    // Determine source layer and log appropriately
                    let sourceLayer = 'unknown';
                    if (itemPool.itemsByType[baseType] && itemPool.itemsByType[baseType].includes(usedItem)) {
                        sourceLayer = 'items (regular)';
                        isTableItem = false;
                    } else if (itemPool.tableItemsByType[baseType] && itemPool.tableItemsByType[baseType].includes(usedItem)) {
                        sourceLayer = 'table_items (regular)';
                        isTableItem = true;
                    } else if (itemPool.conditionalItemsByType[baseType] && itemPool.conditionalItemsByType[baseType].includes(usedItem)) {
                        sourceLayer = 'conditional_items';
                        isTableItem = false;
                    } else if (itemPool.conditionalTableItemsByType[baseType] && itemPool.conditionalTableItemsByType[baseType].includes(usedItem)) {
                        sourceLayer = 'conditional_table_items';
                        isTableItem = true;
                    }
                    
                    console.log(`Using ${objType} from ${sourceLayer} layer`);
                    
                    // Create sprite from matched item
                    sprite = createSpriteFromMatch(usedItem, scenarioObj, position, roomId, index, map);
                    
                    console.log(`Created ${objType} using ${imageName}`);
                    
                    // Track this item as used
                    usedItems.add(imageName);
                    usedItems.add(baseType);
                    itemPool.reserve(usedItem);
                    
                    // If it's a table item, find the closest table and group it
                    if (isTableItem && tableObjects.length > 0) {
                        const closestTable = findClosestTable(sprite, tableObjects);
                        if (closestTable) {
                            const group = tableGroups.find(g => g.table === closestTable);
                            if (group) {
                                // Table items don't need elevation - they're grouped with the table
                                const itemDepth = group.baseDepth + (group.items.length + 1) * 0.01;
                                sprite.setDepth(itemDepth);
                                
                                // No elevation for table items
                                sprite.elevation = 0;
                                group.items.push({ sprite, type: sourceLayer });
                                
                                // Store table items in objects collection so interaction system can find them
                                rooms[roomId].objects[sprite.objectId] = sprite;
                            }
                        }
                    } else {
                        // Set depth and store for non-table items
                        setDepthAndStore(sprite, position, roomId, false);
                    }
                    
                } else {
                    // No matching item found, create at random position (existing fallback behavior)
                    sprite = createSpriteAtRandomPosition(scenarioObj, position, roomId, index, map);

                    // Set depth and store
                    setDepthAndStore(sprite, position, roomId, false);
                }
            });
            
            // 3. Process unreserved Tiled items (existing background decoration items)
            // These are unconditional items that were not used by any scenario object
            const unreservedItems = itemPool.getUnreservedItems();
            
            // Separate table items from regular items for special processing
            const unreservedTableItems = [];
            const unreservedRegularItems = [];
            
            unreservedItems.forEach(tiledItem => {
                const imageName = itemPool.getImageNameFromObject(tiledItem);
                
                // Skip if this exact item was already used by scenario objects
                if (usedItems.has(imageName)) {
                    return;
                }
                
                // Skip if this item type is in starting inventory
                const baseType = itemPool.extractBaseTypeFromImageName(imageName);
                if (startInventoryTypes.has(baseType)) {
                    console.log(`Skipping unreserved item ${imageName} (baseType: ${baseType}) - marked as starting inventory item`);
                    return;
                }
                
                // Check if this is a table item by seeing if it's in tableItemsByType
                if (itemPool.tableItemsByType[baseType] && 
                    itemPool.tableItemsByType[baseType].includes(tiledItem)) {
                    unreservedTableItems.push(tiledItem);
                } else {
                    unreservedRegularItems.push(tiledItem);
                }
            });
            
            // Process regular unreserved items (chairs, lamps, etc.)
            unreservedRegularItems.forEach(tiledItem => {
                const imageName = itemPool.getImageNameFromObject(tiledItem);
                
                // Use processObject to create sprite with all properties (collision, animation, etc.)
                const result = processObject(tiledItem, position, roomId, 'item', map);
                if (result && result.sprite) {
                    // Store unreserved items so they're revealed
                    rooms[roomId].objects[result.sprite.objectId] = result.sprite;
                    console.log(`Added unreserved item ${imageName} to room objects`);
                }
            });
            
            // Process unreserved table items - need to group them with tables and set depth
            unreservedTableItems.forEach(tiledItem => {
                const imageName = itemPool.getImageNameFromObject(tiledItem);
                
                // Use processObject to create sprite with all properties
                const result = processObject(tiledItem, position, roomId, 'table_item', map);
                if (result && result.sprite) {
                    // Find the closest table to group this item with
                    if (tableObjects.length > 0) {
                        const closestTable = findClosestTable(result.sprite, tableObjects);
                        if (closestTable) {
                            const group = tableGroups.find(g => g.table === closestTable);
                            if (group) {
                                group.items.push(result);
                                console.log(`Added unreserved table item ${imageName} to table group`);
                            }
                        }
                    } else {
                        // No tables, just store it as a regular item
                        rooms[roomId].objects[result.sprite.objectId] = result.sprite;
                        console.log(`Added unreserved table item ${imageName} to room objects (no tables to group with)`);
                    }
                }
            });
            
            // Final re-sort and depth assignment for all table groups 
            // (includes both scenario and unreserved table items)
            tableGroups.forEach(group => {
                // Sort items from north to south (lower Y values first)
                group.items.sort((a, b) => a.sprite.y - b.sprite.y);
                
                // Recalculate depths for all items in the group
                group.items.forEach((item, index) => {
                    // Table items don't need elevation - they're grouped with the table
                    const itemDepth = group.baseDepth + (index + 1) * 0.01;
                    item.sprite.setDepth(itemDepth);
                    
                    // No elevation for table items
                    item.sprite.elevation = 0;
                    console.log(`Final depth: table item ${item.sprite.name} to depth ${itemDepth} (position ${index + 1} of ${group.items.length})`);
                });
                
                // Store all group items in room objects
                group.items.forEach(item => {
                    rooms[roomId].objects[item.sprite.objectId] = item.sprite;
                });
            });
            
            // Log summary of item usage
            console.log(`=== Item Usage Summary ===`);
            Object.entries(itemPool.itemsByType).forEach(([baseType, items]) => {
                console.log(`Regular items for ${baseType}: ${items.length} available`);
            });
            Object.entries(itemPool.tableItemsByType).forEach(([baseType, items]) => {
                console.log(`Regular table items for ${baseType}: ${items.length} available`);
            });
            Object.entries(itemPool.conditionalItemsByType).forEach(([baseType, items]) => {
                console.log(`Conditional items for ${baseType}: ${items.length} available`);
            });
            Object.entries(itemPool.conditionalTableItemsByType).forEach(([baseType, items]) => {
                console.log(`Conditional table items for ${baseType}: ${items.length} available`);
            });
            
            return usedItems;
        }
        
        // Helper function to process individual objects
        function processObject(obj, position, roomId, type, map) {
            // Find the tileset that contains this GID
            // Handle multiple tileset instances by finding the most recent one
            let tileset = null;
            let localTileId = 0;
            let bestMatch = null;
            let bestMatchIndex = -1;
            
            for (let i = 0; i < map.tilesets.length; i++) {
                const ts = map.tilesets[i];
                // Handle tilesets with undefined tilecount (individual object tilesets)
                const maxGid = ts.tilecount ? ts.firstgid + ts.tilecount : ts.firstgid + 1;
                if (obj.gid >= ts.firstgid && obj.gid < maxGid) {
                    // Prefer objects tilesets, and among those, prefer the most recent (highest index)
                    if (ts.name === 'objects' || ts.name.includes('objects/') || ts.name.includes('tables/')) {
                        if (bestMatchIndex < i) {
                            bestMatch = ts;
                            bestMatchIndex = i;
                            tileset = ts;
                            localTileId = obj.gid - ts.firstgid;
                        }
                    } else if (!bestMatch) {
                        // Fallback to any matching tileset if no objects tileset found
                        tileset = ts;
                        localTileId = obj.gid - ts.firstgid;
                    }
                }
            }
            
            if (tileset && (tileset.name === 'objects' || tileset.name.includes('objects/') || tileset.name.includes('tables/'))) {
                // This is an ImageCollection or individual object tileset, get the image data
                let imageName = null;
                
                // Check if this is an ImageCollection with images array
                if (tileset.images && tileset.images[localTileId]) {
                    // Get image from the images array
                    const imageData = tileset.images[localTileId];
                    if (imageData && imageData.name) {
                        imageName = imageData.name;
                    }
                } else if (tileset.tileData && tileset.tileData[localTileId]) {
                    // Fallback: get from tileData
                    const tileData = tileset.tileData[localTileId];
                    if (tileData && tileData.image) {
                        const imagePath = tileData.image;
                        imageName = imagePath.split('/').pop().replace('.png', '');
                    }
                } else if (tileset.name.includes('objects/') || tileset.name.includes('tables/')) {
                    // This is an individual object or table tileset, extract name from tileset name
                    imageName = tileset.name.split('/').pop().replace('.png', '');
                }
                
                if (imageName) {
                    console.log(`Creating object from ImageCollection: ${imageName} at (${obj.x}, ${obj.y})`);
                    
                    // Create sprite at the object's position with pixel-perfect coordinates
                    const sprite = gameRef.add.sprite(
                        Math.round(position.x + obj.x),
                        Math.round(position.y + obj.y - obj.height), // Adjust for Tiled's coordinate system
                        imageName
                    );
                    
                    // Set sprite properties
                    sprite.setOrigin(0, 0);
                    sprite.name = imageName;
                    sprite.objectId = `${roomId}_${imageName}_${obj.id}`;
                    sprite.setInteractive({ useHandCursor: true });
                    
                    // Check if this is a chair with wheels
                    if (imageName.startsWith('chair-') && !imageName.startsWith('chair-waiting')) {
                        sprite.hasWheels = true;
                        
                        // Check if this is a swivel chair
                        if (imageName.startsWith('chair-exec-rotate') || 
                            imageName.startsWith('chair-white-1-rotate') || 
                            imageName.startsWith('chair-white-2-rotate')) {
                            sprite.isSwivelChair = true;
                            
                            // Determine starting frame based on image name
                            let frameNumber;
                            if (imageName.startsWith('chair-exec-rotate')) {
                                frameNumber = parseInt(imageName.replace('chair-exec-rotate', ''));
                            } else if (imageName.startsWith('chair-white-1-rotate')) {
                                frameNumber = parseInt(imageName.replace('chair-white-1-rotate', ''));
                            } else if (imageName.startsWith('chair-white-2-rotate')) {
                                frameNumber = parseInt(imageName.replace('chair-white-2-rotate', ''));
                            }
                            
                            sprite.currentFrame = frameNumber - 1; // Convert to 0-based index
                            sprite.rotationSpeed = 0;
                            sprite.maxRotationSpeed = 0.15; // Slower maximum rotation speed
                            sprite.originalTexture = imageName; // Store original texture name
                            sprite.spinDirection = 0; // -1 for counter-clockwise, 1 for clockwise, 0 for no spin
                            
                        }
                        
                        // Calculate elevation for chairs (same as other objects)
                        const roomTopY = position.y;
                        const backWallThreshold = roomTopY + (2 * 32); // Back wall is top 2 tiles
                        const itemBottomY = sprite.y + sprite.height;
                        const elevation = itemBottomY < backWallThreshold ? (backWallThreshold - itemBottomY) : 0;
                        sprite.elevation = elevation;
                        
                    }
                    
                    // Check if this is an animated plant
                    if (imageName.startsWith('plant-large11-top-ani') || 
                        imageName.startsWith('plant-large12-top-ani') || 
                        imageName.startsWith('plant-large13-top-ani')) {
                        
                        sprite.isAnimatedPlant = true;
                        sprite.originalScaleX = sprite.scaleX;
                        sprite.originalScaleY = sprite.scaleY;
                        sprite.originalX = Math.round(sprite.x); // Store pixel-perfect position
                        sprite.originalY = Math.round(sprite.y); // Store pixel-perfect position
                        sprite.originalWidth = Math.round(sprite.width);
                        sprite.originalHeight = Math.round(sprite.height);
                        
                        // Determine which animation to use based on the plant type
                        if (imageName.startsWith('plant-large11-top-ani')) {
                            sprite.animationKey = 'plant-large11-bump';
                        } else if (imageName.startsWith('plant-large12-top-ani')) {
                            sprite.animationKey = 'plant-large12-bump';
                        } else if (imageName.startsWith('plant-large13-top-ani')) {
                            sprite.animationKey = 'plant-large13-bump';
                        }
                        
                        // Ensure the sprite is positioned on pixel boundaries
                        sprite.x = Math.round(sprite.x);
                        sprite.y = Math.round(sprite.y);
                        
                        console.log(`Animated plant ${imageName} ready with animation ${sprite.animationKey}`);
                    }
                    
                    // Set depth based on world Y position with elevation
                    const objectBottomY = sprite.y + sprite.height;
                    
                    // Calculate elevation for items on the back wall (top 2 tiles of room)
                    const roomTopY = position.y;
                    const backWallThreshold = roomTopY + (2 * 32); // Back wall is top 2 tiles
                    const itemBottomY = sprite.y + sprite.height;
                    const elevation = itemBottomY < backWallThreshold ? (backWallThreshold - itemBottomY) : 0;
                    
                    const objectDepth = objectBottomY + 0.5 + elevation;
                    sprite.setDepth(objectDepth);
                    
                    // Store elevation for debugging
                    sprite.elevation = elevation;
                    
                    // Apply rotation if specified
                    if (obj.rotation) {
                        sprite.setRotation(Phaser.Math.DegToRad(obj.rotation));
                    }
                    
                    // Initially hide the object
                    sprite.setVisible(false);
                    
                    // Set up collision for tables
                    if (type === 'table') {
                        // Add physics body to table (static body)
                        gameRef.physics.add.existing(sprite, true);
                        
                        // Wait for the next frame to ensure body is fully initialized
                        gameRef.time.delayedCall(0, () => {
                            if (sprite.body) {
                                // Use direct property assignment (fallback method)
                                sprite.body.immovable = true;
                                
                                // Set custom collision box - bottom quarter of height, inset 10px from sides
                                const tableWidth = sprite.width;
                                const tableHeight = sprite.height;
                                const collisionWidth = tableWidth - 20; // 10px inset on each side
                                const collisionHeight = tableHeight / 4; // Bottom quarter
                                const offsetX = 10; // 10px inset from left
                                const offsetY = tableHeight - collisionHeight; // Bottom quarter
                                
                                sprite.body.setSize(collisionWidth, collisionHeight);
                                sprite.body.setOffset(offsetX, offsetY);
                                
                                console.log(`Set table ${imageName} collision box: ${collisionWidth}x${collisionHeight} at offset (${offsetX}, ${offsetY})`);
                                
                                // Add collision with player
                                const player = window.player;
                                if (player && player.body) {
                                    gameRef.physics.add.collider(player, sprite);
                                    console.log(`Added collision between player and table: ${imageName}`);
                                }
                            }
                        });
                    }
                    
                    // Set up physics for chairs with wheels
                    if (sprite.hasWheels) {
                        // Add physics body to chair (dynamic body for movement)
                        gameRef.physics.add.existing(sprite, false);
                        
                        // Wait for the next frame to ensure body is fully initialized
                        gameRef.time.delayedCall(0, () => {
                            if (sprite.body) {
                                // Set chair as movable
                                sprite.body.immovable = false;
                                sprite.body.setImmovable(false);
                                
                                // Set collision box at base of chair
                                const chairWidth = sprite.width;
                                const chairHeight = sprite.height;
                                const collisionWidth = chairWidth - 10; // 5px inset on each side
                                const collisionHeight = chairHeight / 3; // Bottom third
                                const offsetX = 5; // 5px inset from left
                                const offsetY = chairHeight - collisionHeight; // Bottom third
                                
                                sprite.body.setSize(collisionWidth, collisionHeight);
                                sprite.body.setOffset(offsetX, offsetY);
                                
                                // Set physics properties for bouncing
                                sprite.body.setBounce(0.3, 0.3);
                                sprite.body.setDrag(100, 100);
                                sprite.body.setMaxVelocity(200, 200);
                                
                                
                                // Add collision with player
                                const player = window.player;
                                if (player && player.body) {
                                    // Create collision callback function
                                    const collisionCallback = (player, chair) => {
                                        if (chair.isSwivelChair) {
                                            calculateChairSpinDirection(player, chair);
                                        }
                                    };
                                    
                                    gameRef.physics.add.collider(player, sprite, collisionCallback);
                                }
                                
                                // Store chair reference for collision detection
                                if (!window.chairs) {
                                    window.chairs = [];
                                }
                                window.chairs.push(sprite);
                                
                                // Set up collision with other chairs and items
                                setupChairCollisions(sprite);
                            }
                        });
                    }
                    
                    // Store the object in the room
                    if (!rooms[roomId].objects) {
                        rooms[roomId].objects = {};
                    }
                    rooms[roomId].objects[sprite.objectId] = sprite;
                    
                    // Give default properties to tables (so NPC table collision detection works)
                    if (type === 'table') {
                        const cleanName = imageName.replace(/-.*$/, '').replace(/\d+$/, '');
                        sprite.scenarioData = {
                            name: cleanName,
                            type: 'table',  // Mark explicitly as table type
                            takeable: false,
                            readable: false,
                            observations: `A ${cleanName} in the room`
                        };
                        console.log(`Applied table properties to ${imageName}`);
                    }
                    
                    // Give default properties to regular items (non-scenario items)
                    if (type === 'item' || type === 'table_item') {
                        // Strip out suffix after first dash and any numbers for cleaner names
                        const cleanName = imageName.replace(/-.*$/, '').replace(/\d+$/, '');
                        sprite.scenarioData = {
                            name: cleanName,
                            type: cleanName,
                            takeable: false,
                            readable: false,
                            observations: `A ${cleanName} in the room`
                        };
                        console.log(`Applied default properties to ${type} ${imageName} -> ${cleanName}`);
                    }
                    
                    // Make swivel chairs interactable but don't highlight them
                    if (sprite.isSwivelChair) {
                        sprite.interactable = true;
                        sprite.noInteractionHighlight = true;
                        console.log(`Marked swivel chair ${sprite.objectId} as interactable (no highlight)`);
                    }
                    
                    // Note: Click handling is now done by the main scene's pointerdown handler
                    // which checks for all objects at the clicked position
                    
                    console.log(`Created Tiled object: ${sprite.objectId} at (${sprite.x}, ${sprite.y})`);
                    
                    return { sprite, type };
                } else {
                    console.log(`No image data found for GID ${obj.gid} in objects tileset`);
                }
            } else if (tileset && tileset.name !== 'objects' && !tileset.name.includes('objects/')) {
                // Handle other tilesets (like tables) normally
                console.log(`Skipping non-objects tileset: ${tileset.name}`);
            } else {
                console.log(`No tileset found for GID ${obj.gid}`);
            }
            
            return null;
        }
        
        // Helper function to find the closest table to an item
        function findClosestTable(itemSprite, tableObjects) {
            let closestTable = null;
            let closestDistance = Infinity;
            
            tableObjects.forEach(table => {
                // Calculate distance between item and table centers
                const itemCenterX = itemSprite.x + itemSprite.width / 2;
                const itemCenterY = itemSprite.y + itemSprite.height / 2;
                const tableCenterX = table.sprite.x + table.sprite.width / 2;
                const tableCenterY = table.sprite.y + table.sprite.height / 2;
                
                const distance = Math.sqrt(
                    Math.pow(itemCenterX - tableCenterX, 2) + 
                    Math.pow(itemCenterY - tableCenterY, 2)
                );
                
                if (distance < closestDistance) {
                    closestDistance = distance;
                    closestTable = table;
                }
            });
            
            console.log(`Found closest table for item ${itemSprite.name} at distance ${closestDistance}`);
            return closestTable;
        }

        // Handle objects layer (legacy)
        const objectsLayer = map.getObjectLayer('Object Layer 1');
        console.log(`Object layer found for room ${roomId}:`, objectsLayer ? `${objectsLayer.objects.length} objects` : 'No objects layer');
        if (objectsLayer) {
            
            // Handle collision objects
            objectsLayer.objects.forEach(obj => {
                if (obj.name.toLowerCase().includes('collision') || obj.type === 'collision') {
                    console.log(`Creating collision object: ${obj.name} at (${obj.x}, ${obj.y})`);
                    
                    // Create invisible collision body with pixel-perfect coordinates
                    const collisionBody = gameRef.add.rectangle(
                        Math.round(position.x + obj.x + obj.width/2),
                        Math.round(position.y + obj.y + obj.height/2),
                        Math.round(obj.width),
                        Math.round(obj.height)
                    );
                    
                    // Make it invisible but with collision
                    collisionBody.setVisible(false);
                    collisionBody.setAlpha(0);
                    gameRef.physics.add.existing(collisionBody, true);
                    
                    // Add collision with player
                    const player = window.player;
                    if (player && player.body) {
                        gameRef.physics.add.collider(player, collisionBody);
                        console.log(`Added collision object: ${obj.name}`);
                    }
                    
                    // Store collision body in room for cleanup
                    if (!room.collisionBodies) {
                        room.collisionBodies = [];
                    }
                    room.collisionBodies.push(collisionBody);
                }
            });
            
            // Create a map of room objects by type for easy lookup
            const roomObjectsByType = {};
            objectsLayer.objects.forEach(obj => {
                if (!roomObjectsByType[obj.name]) {
                    roomObjectsByType[obj.name] = [];
                }
                roomObjectsByType[obj.name].push(obj);
            });
            
            // Legacy scenario object processing removed - now handled by conditional matching system
        }
        
        // Set up pending wall collision boxes if player is ready
        const room = rooms[roomId];
        if (room && room.pendingWallCollisionBoxes && window.player && window.player.body) {
            room.pendingWallCollisionBoxes.forEach(collisionBox => {
                gameRef.physics.add.collider(window.player, collisionBox);
            });
            console.log(`Set up ${room.pendingWallCollisionBoxes.length} pending wall collision boxes for room ${roomId}`);
            // Clear pending collision boxes
            room.pendingWallCollisionBoxes = [];
        }
        
        // Set up collisions between existing chairs and new room objects
        setupExistingChairsWithNewRoom(roomId);
        
        // Initialize pathfinding for NPC patrol routes in this room
        const pfManager = pathfindingManager || window.pathfindingManager;
        if (pfManager && rooms[roomId]) {
            console.log(`🔧 Initializing pathfinding for room ${roomId}...`);
            pfManager.initializeRoomPathfinding(roomId, rooms[roomId], position);
        } else {
            console.warn(`⚠️ Cannot initialize pathfinding: pfManager=${!!pfManager}, room=${!!rooms[roomId]}`);
        }
        
        // ===== NPC SPRITE CREATION =====
        // Create NPC sprites for person-type NPCs in this room
        createNPCSpritesForRoom(roomId, rooms[roomId]);
    } catch (error) {
        console.error(`Error creating room ${roomId}:`, error);
        console.error('Error details:', error.stack);
    }
}

export function revealRoom(roomId) {
    // IMPORTANT: revealRoom() makes graphics VISIBLE but does NOT mark as DISCOVERED
    // 
    // "Revealed" = graphics are loaded and visible (for rendering/performance)
    // "Discovered" = player has actually ENTERED the room (for gameplay/events)
    //
    // This separation allows us to:
    // 1. Preload/reveal rooms for performance without marking them as "visited"
    // 2. Trigger "room_discovered" events when player first ENTERS a room
    // 3. Keep "first visit" detection accurate for NPC reactions
    //
    // Rooms are marked as "discovered" in the door transition code, AFTER
    // the room_discovered event is emitted.
    
    if (rooms[roomId]) {
        const room = rooms[roomId];
        
        // Reveal all layers
        Object.values(room.layers).forEach(layer => {
            if (layer && layer.setVisible) {
                layer.setVisible(true);
                layer.setAlpha(1);
            }
        });
        
        // Show door sprites for this room
        if (room.doorSprites) {
            room.doorSprites.forEach(doorSprite => {
                doorSprite.setVisible(true);
                doorSprite.setAlpha(1);
                console.log(`Made door sprite visible for room ${roomId}`);
            });
        }
        
        // Show all objects
        if (room.objects) {
            console.log(`Revealing ${Object.keys(room.objects).length} objects in room ${roomId}`);
            Object.values(room.objects).forEach(obj => {
                if (obj && obj.setVisible && obj.active) { // Only show active objects
                    obj.setVisible(true);
                    obj.alpha = obj.active ? (obj.originalAlpha || 1) : 0.3;
                    console.log(`Made object visible: ${obj.objectId} at (${obj.x}, ${obj.y})`);
                }
            });
        } else {
            console.log(`No objects found in room ${roomId}`);
        }
        
        // NOTE: We do NOT add to discoveredRooms here!
        // Rooms are only marked as "discovered" when the player actually enters them
        // via door transition. This allows revealRoom() to be used for preloading/visibility
        // without affecting the "first visit" detection for NPC events.
    }
    currentRoom = roomId;
}

export function updatePlayerRoom() {
    // Check which room the player is currently in
    const player = window.player;
    if (!player) {
        return; // Player not created yet
    }
    
    // Store previous room for event emission
    const previousRoom = currentPlayerRoom;
    
    // Check for door transitions first
    const doorTransitionRoom = checkDoorTransitions(player);
    if (doorTransitionRoom && doorTransitionRoom !== currentPlayerRoom) {
        // Door transition detected to a different room
        console.log(`Door transition detected: ${currentPlayerRoom} -> ${doorTransitionRoom}`);
        currentPlayerRoom = doorTransitionRoom;
        window.currentPlayerRoom = doorTransitionRoom;
        
        // Check if this is the first time the player has ENTERED this room
        // NOTE: The room may already be "revealed" (graphics visible) from preloading,
        // but we only mark it as "discovered" when the player actually walks through
        // a door into it. This keeps first-visit detection accurate for NPC events.
        const isFirstVisit = !discoveredRooms.has(doorTransitionRoom);
        
        if (isFirstVisit) {
            // Reveal graphics if needed (may already be revealed from preloading)
            revealRoom(doorTransitionRoom);
        }
        
        // Emit NPC event for room entry
        console.log(`🚪 Door transition detected: ${previousRoom} → ${doorTransitionRoom}`);
        console.log(`   eventDispatcher exists: ${!!window.eventDispatcher}`);
        console.log(`   previousRoom !== doorTransitionRoom: ${previousRoom !== doorTransitionRoom}`);
        console.log(`   isFirstVisit: ${isFirstVisit}`);
        
        if (window.eventDispatcher && previousRoom !== doorTransitionRoom) {
            console.log(`🚪 Emitting room_entered event: ${doorTransitionRoom} (firstVisit: ${isFirstVisit})`);
            window.eventDispatcher.emit('room_entered', {
                roomId: doorTransitionRoom,
                previousRoom: previousRoom,
                firstVisit: isFirstVisit
            });
            
            // Also emit room-specific event for easier filtering
            window.eventDispatcher.emit(`room_entered:${doorTransitionRoom}`, {
                roomId: doorTransitionRoom,
                previousRoom: previousRoom,
                firstVisit: isFirstVisit
            });
            
            // Emit room_discovered event for first-time visits
            if (isFirstVisit) {
                console.log(`🗺️  Emitting room_discovered event: ${doorTransitionRoom}`);
                window.eventDispatcher.emit('room_discovered', {
                    roomId: doorTransitionRoom,
                    previousRoom: previousRoom
                });
                
                // Mark as discovered AFTER emitting the event
                // This is the ONLY place where rooms are added to discoveredRooms!
                // By marking discovered here (not in revealRoom), we ensure:
                // 1. The first door transition into a room triggers room_discovered
                // 2. NPCs can react to the player's first visit
                // 3. Subsequent visits don't re-trigger the event
                discoveredRooms.add(doorTransitionRoom);
                window.discoveredRooms = discoveredRooms;
                console.log(`✅ Marked room ${doorTransitionRoom} as discovered`);
                
                // Update NPC talk icons for the new room
                if (window.npcTalkIcons && rooms[doorTransitionRoom].npcSprites) {
                    window.npcTalkIcons.init([], rooms[doorTransitionRoom].npcSprites);
                }
            }
            
            if (previousRoom) {
                window.eventDispatcher.emit('room_exited', {
                    roomId: previousRoom,
                    nextRoom: doorTransitionRoom
                });
            }
        } else {
            console.warn(`⚠️ NOT emitting room events - eventDispatcher: ${!!window.eventDispatcher}, previousRoom: ${previousRoom}, doorTransitionRoom: ${doorTransitionRoom}`);
        }
        
        // Player depth is now handled by the simplified updatePlayerDepth function in player.js
        return; // Exit early to prevent overlap-based detection from overriding
    }    // Only do overlap-based room detection if no door transition occurred
    // and if we don't have a current room (fallback)
    if (currentPlayerRoom) {
        return; // Keep current room if no door transition and we already have one
    }
    
    // Fallback to overlap-based room detection
    let overlappingRooms = [];
    
    // Check all rooms for overlap with proper threshold
    Object.entries(rooms).forEach(([roomId, room]) => {
        const roomBounds = {
            x: room.position.x,
            y: room.position.y,
            width: room.map.widthInPixels,
            height: room.map.heightInPixels
        };
        
        if (isPlayerInBounds(player, roomBounds)) {
            overlappingRooms.push({
                roomId: roomId,
                position: room.position.y // Use Y position for northernmost sorting
            });
            
            // Reveal room if not already discovered
            if (!discoveredRooms.has(roomId)) {
                console.log(`Player overlapping room: ${roomId}`);
                revealRoom(roomId);
            }
        }
    });
    
    // If we're not overlapping any rooms
    if (overlappingRooms.length === 0) {
        currentPlayerRoom = null;
        window.currentPlayerRoom = null;
        return null;
    }
    
    // Sort overlapping rooms by Y position (northernmost first - lower Y values)
    overlappingRooms.sort((a, b) => a.position - b.position);
    
    // Use the northernmost room (lowest Y position) as the main room
    const northernmostRoom = overlappingRooms[0].roomId;
    
    // Update current room (use the northernmost overlapping room as the "main" room)
    if (currentPlayerRoom !== northernmostRoom) {
        console.log(`Player's main room changed to: ${northernmostRoom} (northernmost of ${overlappingRooms.length} overlapping rooms)`);
        currentPlayerRoom = northernmostRoom;
        window.currentPlayerRoom = northernmostRoom;
        
        // Player depth is now handled by the simplified updatePlayerDepth function in player.js
    }
}

// Helper function to check if player properly overlaps with room bounds
function isPlayerInBounds(player, bounds) {
    // Use the player's physics body bounds for more precise detection
    const playerBody = player.body;
    const playerBounds = {
        left: playerBody.x,
        right: playerBody.x + playerBody.width,
        top: playerBody.y,
        bottom: playerBody.y + playerBody.height
    };
    
    // Calculate the overlap area between player and room
    const overlapWidth = Math.min(playerBounds.right, bounds.x + bounds.width) - 
                         Math.max(playerBounds.left, bounds.x);
    const overlapHeight = Math.min(playerBounds.bottom, bounds.y + bounds.height) - 
                          Math.max(playerBounds.top, bounds.y);
    
    // Require a minimum overlap percentage (50% of player width/height)
    const minOverlapPercent = 0.5;
    const playerWidth = playerBounds.right - playerBounds.left;
    const playerHeight = playerBounds.bottom - playerBounds.top;
    
    const widthOverlapPercent = overlapWidth / playerWidth;
    const heightOverlapPercent = overlapHeight / playerHeight;
    
    return overlapWidth > 0 && 
           overlapHeight > 0 && 
           widthOverlapPercent >= minOverlapPercent && 
           heightOverlapPercent >= minOverlapPercent;
}

// Door collisions are now handled by sprite-based system
export function setupDoorCollisions() {
    console.log('Door collisions are now handled by sprite-based system');
}

/**
 * Create NPC sprites for all person-type NPCs in a room
 * @param {string} roomId - Room ID
 * @param {Object} roomData - Room data object
 */
function createNPCSpritesForRoom(roomId, roomData) {
    if (!window.npcManager) {
        console.warn('⚠️ NPCManager not available, skipping NPC sprite creation');
        return;
    }
    
    if (!gameRef) {
        console.warn('⚠️ Game instance not available, skipping NPC sprite creation');
        return;
    }
    
    // Get all NPCs that should appear in this room
    const npcsInRoom = getNPCsForRoom(roomId);
    
    if (npcsInRoom.length === 0) {
        return; // No NPCs for this room
    }
    
    console.log(`Creating ${npcsInRoom.length} NPC sprites for room ${roomId}`);
    
    // Initialize NPC sprites array if needed
    if (!roomData.npcSprites) {
        roomData.npcSprites = [];
    }
    
    npcsInRoom.forEach(npc => {
        // Only create sprites for person-type NPCs
        if (npc.npcType === 'person' || npc.npcType === 'both') {
            try {
                const sprite = NPCSpriteManager.createNPCSprite(gameRef, npc, roomData);
                
                if (sprite) {
                    // Store sprite reference
                    roomData.npcSprites.push(sprite);
                    
                    // Set up collision with player
                    if (window.player) {
                        NPCSpriteManager.createNPCCollision(gameRef, sprite, window.player);
                    }
                    
                    // Set up wall and chair collisions (same as player gets)
                    NPCSpriteManager.setupNPCEnvironmentCollisions(gameRef, sprite, roomId);

                    // Set up NPC-to-NPC collisions with all other NPCs in this room
                    NPCSpriteManager.setupNPCToNPCCollisions(gameRef, sprite, roomId, roomData.npcSprites);

                    // Register behavior if configured
                    // Only for sprite-based NPCs (not phone-only)
                    if (window.npcBehaviorManager && npc.behavior) {
                        window.npcBehaviorManager.registerBehavior(
                            npc.id,
                            sprite,
                            npc.behavior
                        );
                        console.log(`🤖 Behavior registered for ${npc.id}`);
                    }

                    console.log(`✅ NPC sprite created: ${npc.id} in room ${roomId}`);
                }
            } catch (error) {
                console.error(`❌ Error creating NPC sprite for ${npc.id}:`, error);
            }
        }
    });
    
    // Auto-enable LOS visualization if any NPC has los.visualize = true
    if (window.npcManager && gameRef) {
        console.log(`👁️ Checking ${npcsInRoom.length} NPCs for LOS visualization requests...`);
        npcsInRoom.forEach(npc => {
            console.log(`   NPC "${npc.id}": los=${!!npc.los}, visualize=${npc.los?.visualize}`);
        });
        
        const hasVisualNPC = npcsInRoom.some(npc => npc.los?.visualize === true);
        console.log(`👁️ hasVisualNPC: ${hasVisualNPC}`);
        
        if (hasVisualNPC) {
            console.log(`👁️ Auto-enabling LOS visualization for room ${roomId}`);
            console.log(`   npcManager: ${!!window.npcManager}`);
            console.log(`   gameRef: ${!!gameRef}`);
            
            // Get the current scene instance - need to get it from the scene manager
            // gameRef.scene is the SceneManager, we need gameRef.scene.getScene() to get the actual scene
            let currentScene = null;
            if (gameRef && gameRef.scene && typeof gameRef.scene.getScene === 'function') {
                // Get the running scene from the scene manager
                currentScene = gameRef.scene.getScene('default') || 
                               gameRef.scene.scenes?.[0];
            }
            if (!currentScene && window.game?.scene) {
                currentScene = window.game.scene.getScene('default') || 
                               window.game.scene.scenes?.[0];
            }
            
            console.log(`   currentScene: ${!!currentScene}, key: ${currentScene?.key}, isScene: ${currentScene?.add ? 'yes' : 'no'}`);
            
            if (currentScene && typeof currentScene.add?.graphics === 'function') {
                window.npcManager.setLOSVisualization(true, currentScene);
            } else {
                console.warn(`⚠️ Cannot get valid Phaser scene for LOS visualization`, {
                    currentScene: !!currentScene,
                    hasAddMethod: !!currentScene?.add,
                    hasGraphicsMethod: typeof currentScene?.add?.graphics
                });
            }
        } else {
            console.log(`👁️ No NPCs requesting LOS visualization in room ${roomId}`);
        }
    } else {
        console.log(`👁️ Cannot auto-enable LOS: npcManager=${!!window.npcManager}, gameRef=${!!gameRef}`);
    }
}

/**
 * Get all NPCs configured to appear in a specific room
 * @param {string} roomId - Room ID to check
 * @returns {Array} Array of NPC objects for this room
 */
function getNPCsForRoom(roomId) {
    if (!window.npcManager) {
        return [];
    }
    
    const allNPCs = Array.from(window.npcManager.npcs.values());
    return allNPCs.filter(npc => npc.roomId === roomId);
}

/**
 * Destroy NPC sprites when room is unloaded
 * @param {string} roomId - Room ID being unloaded
 */
export function unloadNPCSprites(roomId) {
    if (!rooms[roomId]) return;
    
    const roomData = rooms[roomId];
    
    if (roomData.npcSprites && Array.isArray(roomData.npcSprites)) {
        console.log(`Destroying ${roomData.npcSprites.length} NPC sprites for room ${roomId}`);
        
        roomData.npcSprites.forEach(sprite => {
            if (sprite && !sprite.destroyed) {
                NPCSpriteManager.destroyNPCSprite(sprite);
            }
        });
        
        roomData.npcSprites = [];
    }
}

// Export for global access
window.initializeRooms = initializeRooms;
window.setupDoorCollisions = setupDoorCollisions;
window.loadRoom = loadRoom;
window.unloadNPCSprites = unloadNPCSprites;
window.relocateNPCSprite = NPCSpriteManager.relocateNPCSprite;

// Export functions for module imports
export { updateDoorSpritesVisibility };
