// Event handling for Structure Building System
// Manages itemUseOn events and coordinates with other modules

import { world, system } from '@minecraft/server';
import { getStructureItemByItemId } from './main.js';
import { buildStructure } from './utils.js';
import { validateStructurePlacement, validateStructureSpace } from './validation.js';
import { showStructureSelectionUI, showDimensionSelectionUI } from './ui.js';
import { registerBlockBreakEvents } from './breaking.js';
import { isExclusiveStructureBlock } from './config.js';
import { structureItems } from './config.js';
import { ItemStack } from '@minecraft/server';
import { 
  initializeInventoryValidator, 
  validateAllInventoriesNow, 
  getInventoryValidatorStatus 
} from './inv_check.js';
import { debug } from './utils.js';

let cooldowns = new Map();
const COOLDOWN_TICKS = 20; // 1 second at 20 TPS

/**
 * Initialize cooldown system with a repeating tick handler
 */
function initializeCooldownSystem() {
  system.runInterval(() => {
    // Decrement all active cooldowns
    for (const [playerId, ticks] of cooldowns.entries()) {
      if (ticks <= 1) {
        cooldowns.delete(playerId);
      } else {
        cooldowns.set(playerId, ticks - 1);
      }
    }
  }, 1); // Run every tick
}

/**
 * Check if player is on cooldown
 * @param {string} playerId - The player's ID
 * @returns {boolean} True if player is on cooldown
 */
function isPlayerOnCooldown(playerId) {
  return cooldowns.has(playerId);
}

/**
 * Set cooldown for a player
 * @param {string} playerId - The player's ID
 */
function setCooldown(playerId) {
  cooldowns.set(playerId, COOLDOWN_TICKS);
}

/**
 * Register all structure-related event handlers
 */
export function registerStructureEvents() {
  registerBlockBreakEvents();
  registerExplosionEvents();
  initializeCooldownSystem();
  initializeInventoryValidator();
  
  world.beforeEvents.itemUseOn.subscribe(event => {
    const player = event.source;
    const playerId = player.id;
    const { itemStack, block } = event;

    if (isExclusiveStructureBlock(itemStack.typeId)) { 
      player.runCommandAsync(`clear @s ${itemStack.typeId}`)
      event.cancel = true;
    }

    // Check if player is on cooldown
    if (isPlayerOnCooldown(playerId)) {
      return; // Skip handling if on cooldown
    }
    
    // Set cooldown for this player
    setCooldown(playerId);
    
    // Handle the item use event
    system.run(() => {
      if (!block.typeId.includes('_16')) handleItemUseOn(event);
    });
  });
  debug('info', 'All structure events registered');
}

/**
 * Register explosion event handlers for structure breaking
 */
function registerExplosionEvents() {
  world.beforeEvents.explosion.subscribe(handleExplosion);
  debug('info', 'Explosion structure events registered');
}

/**
 * Handle explosion events to detect and break structures
 * @param {ExplosionBeforeEvent} event - The explosion event
 */
function handleExplosion(event) {
  const affectedBlocks = event.getImpactedBlocks();
  const dimension = event.dimension;
  
  if (affectedBlocks.length === 0) {
    return; // No blocks affected
  }
  
  debug('info', `Explosion affecting ${affectedBlocks.length} blocks`);
  
  // Find all structure blocks that will be destroyed
  const structureBlocksToDestroy = [];
  
  for (const blockLocation of affectedBlocks) {
    const block = dimension.getBlock(blockLocation);
    if (block && isExclusiveStructureBlock(block.typeId)) {
      structureBlocksToDestroy.push({
        location: blockLocation,
        blockType: block.typeId
      });
      debug('info', `Structure block in explosion: ${block.typeId} at ${blockLocation.x},${blockLocation.y},${blockLocation.z}`);
    }
  }
  
  if (structureBlocksToDestroy.length === 0) {
    return; // No structure blocks affected
  }
  
  debug('info', `Found ${structureBlocksToDestroy.length} structure blocks in explosion`);
  
  // Group structure blocks by connected structures
  const structureGroups = groupStructureBlocks(structureBlocksToDestroy, dimension);
  
  // For each structure group, break the entire structure and spawn items
  for (const structureGroup of structureGroups) {
    processExplosionStructureGroup(structureGroup, dimension, event);
  }
}

/**
 * Group structure blocks into connected structures using flood-fill
 * @param {Array} structureBlocks - Array of structure blocks with location and blockType
 * @param {Dimension} dimension - The dimension to check in
 * @returns {Array} Array of structure groups
 */
function groupStructureBlocks(structureBlocks, dimension) {
  const visited = new Set();
  const groups = [];
  
  // Helper function to create location key
  const locationKey = (loc) => `${loc.x},${loc.y},${loc.z}`;
  
  // All 6 directions: up, down, north, south, east, west
  const directions = [
    { x: 0, y: 1, z: 0 },   // Up
    { x: 0, y: -1, z: 0 },  // Down
    { x: 0, y: 0, z: -1 },  // North
    { x: 0, y: 0, z: 1 },   // South
    { x: 1, y: 0, z: 0 },   // East
    { x: -1, y: 0, z: 0 }   // West
  ];
  
  for (const structureBlock of structureBlocks) {
    const startKey = locationKey(structureBlock.location);
    
    if (visited.has(startKey)) {
      continue; // Already processed this block
    }
    
    // Start new group with flood-fill from this block
    const group = [];
    const toVisit = [structureBlock.location];
    
    while (toVisit.length > 0) {
      const currentLocation = toVisit.pop();
      const key = locationKey(currentLocation);
      
      if (visited.has(key)) {
        continue;
      }
      
      const currentBlock = dimension.getBlock(currentLocation);
      if (!currentBlock || !isExclusiveStructureBlock(currentBlock.typeId)) {
        continue;
      }
      
      // Mark as visited and add to current group
      visited.add(key);
      group.push({
        location: currentLocation,
        blockType: currentBlock.typeId
      });
      
      // Check all 6 adjacent positions for more structure blocks
      for (const dir of directions) {
        const nextLocation = {
          x: currentLocation.x + dir.x,
          y: currentLocation.y + dir.y,
          z: currentLocation.z + dir.z
        };
        
        const nextKey = locationKey(nextLocation);
        
        if (!visited.has(nextKey)) {
          toVisit.push(nextLocation);
        }
      }
    }
    
    if (group.length > 0) {
      groups.push(group);
      debug('info', `Found connected structure group with ${group.length} blocks`);
    }
  }
  
  return groups;
}

/**
 * Process a structure group affected by explosion
 * @param {Array} structureGroup - Array of connected structure blocks
 * @param {Dimension} dimension - The dimension
 * @param {ExplosionBeforeEvent} event - The explosion event
 */
function processExplosionStructureGroup(structureGroup, dimension, event) {
  if (structureGroup.length === 0) {
    return;
  }
  
  debug('info', `Processing structure group with ${structureGroup.length} blocks`);
  
  // Determine structure type by analyzing the blocks
  const structureType = detectStructureTypeFromBlocks(structureGroup, dimension);
  
  if (!structureType) {
    debug('error', 'Could not determine structure type for explosion group');
    return;
  }
  
  debug('info', `Detected structure type: ${structureType}`);
  
  // Add all remaining structure blocks to the explosion's impact list
  // This ensures the entire structure gets destroyed by the explosion
  const impactedBlocks = event.getImpactedBlocks();
  const blocksToAdd = [];
  
  for (const structureBlock of structureGroup) {
    // Check if this block is already in the explosion impact list
    const alreadyImpacted = impactedBlocks.some(impactedBlock => 
      impactedBlock.location.x === structureBlock.location.x &&
      impactedBlock.location.y === structureBlock.location.y &&
      impactedBlock.location.z === structureBlock.location.z
    );
    
    if (!alreadyImpacted) {
      // Get the actual Block object from the dimension
      const block = dimension.getBlock(structureBlock.location);
      if (block && isExclusiveStructureBlock(block.typeId)) {
        blocksToAdd.push(block);
      }
    }
  }
  
  // Update the explosion impact with additional structure blocks
  if (blocksToAdd.length > 0) {
    event.setImpactedBlocks([...impactedBlocks, ...blocksToAdd]);
    debug('info', `Added ${blocksToAdd.length} additional structure blocks to explosion impact`);
  }
  
  // Schedule item spawning after explosion completes
  const centerLocation = calculateStructureCenter(structureGroup);
  
  // Use system.runTimeout to spawn item after explosion finishes
  system.runTimeout(() => {
    spawnStructureItemFromExplosion(centerLocation, structureType, dimension);
  }, 2); // Wait 2 ticks for explosion to complete
}

/**
 * Detect structure type from a group of blocks (simplified version)
 * @param {Array} structureGroup - Array of structure blocks
 * @param {Dimension} dimension - The dimension
 * @returns {string|null} The detected structure type
 */
function detectStructureTypeFromBlocks(structureGroup, dimension) {
  // Count occurrences of each block type
  const blockCounts = {};
  const blockTypes = new Set();
  
  for (const structureBlock of structureGroup) {
    blockTypes.add(structureBlock.blockType);
    blockCounts[structureBlock.blockType] = (blockCounts[structureBlock.blockType] || 0) + 1;
  }
  
  debug('info', `Block analysis for explosion: ${JSON.stringify(blockCounts)}`);
  
  // Simplified detection logic (reuse from breaking.js concepts)
  if (blockTypes.has('dss_st:dss_st_street_light_light')) {
    const lightCount = blockCounts['dss_st:dss_st_street_light_light'] || 0;
    return lightCount >= 2 ? 'double-street-light' : 'single-street-light';
  }
  
  if (blockTypes.has('dss_st:dss_st_transformer')) {
    return 'util-pole-trans';
  }
  
  if (blockTypes.has('dss_st:dss_st_util_pole_base')) {
    return 'util-pole';
  }
  
  debug('warn', 'Could not determine structure type from blocks');
  return null;
}

/**
 * Calculate the center location of a structure group
 * @param {Array} structureGroup - Array of structure blocks
 * @returns {Vector3} Center location
 */
function calculateStructureCenter(structureGroup) {
  if (structureGroup.length === 0) {
    return { x: 0, y: 0, z: 0 };
  }
  
  let sumX = 0, sumY = 0, sumZ = 0;
  
  for (const block of structureGroup) {
    sumX += block.location.x;
    sumY += block.location.y;
    sumZ += block.location.z;
  }
  
  return {
    x: Math.floor(sumX / structureGroup.length) + 0.5,
    y: Math.floor(sumY / structureGroup.length) + 1,
    z: Math.floor(sumZ / structureGroup.length) + 0.5
  };
}

/**
 * Spawn structure item from explosion
 * @param {Vector3} location - The spawn location
 * @param {string} structureType - The structure type
 * @param {Dimension} dimension - The dimension
 */
function spawnStructureItemFromExplosion(location, structureType, dimension) {
  const structureConfig = structureItems[structureType];
  
  if (!structureConfig) {
    debug('error', `Unknown structure type: ${structureType}`);
    return;
  }
  
  const itemId = structureConfig.item;
  const displayName = structureConfig.display_name;
  
  dimension.spawnItem(new ItemStack(itemId), location);
  
  debug('info', `Spawned structure item from explosion: ${itemId} (${displayName}) at ${location.x}, ${location.y}, ${location.z}`);
}

// Export inventory validation functions for admin access
export { validateAllInventoriesNow, getInventoryValidatorStatus };

/**
 * Handle itemUseOn events for structure building
 * @param {Object} eventData - The itemUseOn event data
 */
export function handleItemUseOn(eventData) {
    const { source: player, itemStack, block, blockFace } = eventData;
    const blockLocation = block.location;

    if (!itemStack || !player) return;
    
    const itemId = itemStack.typeId;
    const structureItem = getStructureItemByItemId(itemId);
    
    if (!structureItem) return; // Not a structure item, ignore
    
    debug('info', `Player ${player.name} used structure item: ${structureItem.display_name}`);
    
    // Validate placement location
    const validationResult = validateStructurePlacement(blockLocation, blockFace, structureItem, player);
    if (!validationResult.valid) {
      player.onScreenDisplay.setActionBar(`§c${validationResult.reason}`);
      return;
    }
    
    // If multiple templates available, show selection UI
    if (structureItem.structure_templates.length > 1) {
      showStructureSelectionUI(player, structureItem, blockLocation, blockFace);
    } else {
      // Show dimension selection UI for the single template
      const templateKey = structureItem.structure_templates[0];
      showDimensionSelectionUI(player, structureItem, templateKey, blockLocation, blockFace);
    }
}

/**
 * Build a structure at the specified location
 * @param {Player} player - The player building the structure
 * @param {Object} structureItem - The structure item configuration
 * @param {string} templateKey - The template key to use
 * @param {Vector3} location - The build location
 * @param {string} face - The block face that was clicked
 * @param {Object} selectedDimensions - Optional selected dimensions from UI sliders
 */
export function buildStructureAtLocation(player, structureItem, templateKey, blockLocation, blockFace, selectedDimensions = null) {
    debug('info', `Building structure: ${structureItem.display_name} at ${blockLocation.x}, ${blockLocation.y}, ${blockLocation.z}`);
    
    // Validate that there's enough space for the structure with selected dimensions
    const spaceValidation = validateStructureSpace(structureItem, templateKey, blockLocation, blockFace, player, selectedDimensions);
    
    if (!spaceValidation.valid) {
        // Show actionbar message for space validation failure
        player.onScreenDisplay.setActionBar(spaceValidation.reason);
        debug('warn', `Structure placement blocked: ${spaceValidation.reason}`);
        if (spaceValidation.details) {
            debug('info', `Space validation details: ${spaceValidation.details}`);
        }
        return;
    }
    
    const result = buildStructure(structureItem, templateKey, blockLocation, blockFace, player, selectedDimensions);
    
    if (result.success) {
        player.onScreenDisplay.setActionBar(`§aStructure built: ${structureItem.display_name} at ${blockLocation.x}, ${blockLocation.y}, ${blockLocation.z}`);
        debug('info', `Structure built: ${structureItem.display_name} at ${blockLocation.x}, ${blockLocation.y}, ${blockLocation.z}`);
        player.runCommandAsync(`clear @s ${structureItem.item} 0 1`)
    } else {
        player.onScreenDisplay.setActionBar(`§cFailed to build structure: ${result.error}`);
        debug('error', `Failed to build structure: ${result.error}`);
    }
}

/**
 * Handle structure building errors and provide user feedback
 * @param {Player} player - The player to send feedback to
 * @param {string} error - The error message
 */
export function handleStructureBuildError(player, error) {
  const userFriendlyMessage = getUserFriendlyErrorMessage(error);
  player.onScreenDisplay.setActionBar(`§c${userFriendlyMessage}`);
  debug('error', `Structure build error for ${player.name}: ${error}`);
}

/**
 * Convert technical error messages to user-friendly ones
 * @param {string} error - The technical error message
 * @returns {string} User-friendly error message
 */
function getUserFriendlyErrorMessage(error) {
  if (error.includes('permission')) {
    return 'You do not have permission to build here.';
  }
  if (error.includes('space')) {
    return 'Not enough space to build this structure.';
  }
  if (error.includes('block')) {
    return 'Cannot place structure on this type of block.';
  }
  return 'Failed to build structure. Please try again.';
}