// modules/dss_sound_helper.js

import { CONFIG } from '../dss_sound_config.js';

/**
 * Logger utility based on DEBUG settings.
 * @param {string} message - The message to log.
 * @param {string} type - The type of message (e.g., 'SOUND_EFFECT', 'BG_MUSIC', 'PROXIMITY').
 */
export function log(message, type = 'INFO') {
  const timestamp = new Date().toISOString();
  if (CONFIG.DEBUG.ALL || CONFIG.DEBUG[type.toUpperCase()]) {
    console.log(`[DSS][${timestamp}][${type}] ${message}`);
  }
}

/**
 * Retrieve sound configurations that are enabled to play.
 * @returns {Array} - Array of enabled sound configurations.
 */
export function getEnabledSounds() {
  return Object.entries(CONFIG.SOUNDS)
    .filter(([_, config]) => config.PLAY)
    .map(([name, config]) => ({ name, config }));
}

/**
 * Retrieve sound configurations that are enabled to play for a specific event type.
 * @param {string} eventType - The event type to filter by (e.g., "proximity").
 * @returns {Array} - Array of enabled sound configurations for the specified event type.
 */
export function getEnabledSoundsByEvent(eventType) {
  return getEnabledSounds()
    .filter(soundEntry => soundEntry.config.EVENT === eventType)
    .map(soundEntry => soundEntry.config);
}

/**
 * Checks if the event meets the specified conditions.
 * @param {Object} conditions - Conditions to check.
 * @param {Object} event - The event object.
 * @returns {boolean} - True if all conditions are met, false otherwise.
 */
export function checkConditions(conditions, event) {
  if (!conditions) return true; // No conditions to check

  for (const [key, value] of Object.entries(conditions)) {
    // Support nested condition checks if necessary
    if (typeof value === 'object' && value !== null) {
      if (!deepMatch(event[key], value)) {
        return false;
      }
    } else {
      if (event[key] !== value) {
        return false;
      }
    }
  }
  return true;
}

/**
 * Deeply matches two objects.
 * @param {any} obj1 
 * @param {any} obj2 
 * @returns {boolean} 
 */
function deepMatch(obj1, obj2) {
  if (typeof obj1 !== typeof obj2) return false;
  if (typeof obj1 !== 'object' || obj1 === null || obj2 === null) {
    return obj1 === obj2;
  }
  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);
  if (keys1.length !== keys2.length) return false;
  for (const key of keys1) {
    if (!keys2.includes(key)) return false;
    if (!deepMatch(obj1[key], obj2[key])) return false;
  }
  return true;
}

/**
 * Plays a sound for a specific player.
 * @param {import("@minecraft/server").Player} player - The player to play the sound for.
 * @param {string} sound - The sound event name.
 * @param {Object} options - Sound options.
 * @param {number} options.volume - Volume of the sound.
 * @param {number} options.pitch - Pitch of the sound.
 * @param {boolean} options.loop - Whether the sound should loop.
 */
export function playSound(player, sound, { volume = 1.0, pitch = 1.0, loop = true } = {}) {
  try {
    // Validate that player and sound are valid
    if (!player) {
      log(`Error playing sound: player is undefined or null`, 'SOUND_EFFECT');
      return;
    }
    
    if (!sound) {
      log(`Error playing sound: sound is undefined or null for player ${player.name}`, 'SOUND_EFFECT');
      return;
    }
    
    // Debug what we're about to play
    log(`Attempting to play sound "${sound}" for player "${player.name}" with volume=${volume}, pitch=${pitch}, loop=${loop}`, 'SOUND_EFFECT');
    
    // Check if the player object has the playSound method
    if (typeof player.playSound !== 'function') {
      log(`Error: player.playSound is not a function for player ${player.name}`, 'SOUND_EFFECT');
      return;
    }
    
    // Try to play the sound
    player.playSound(sound, {
      volume,
      pitch,
      loop
    });

    log(`Successfully played sound: ${sound} for player: ${player.name}`, 'SOUND_EFFECT');
  } catch (error) {
    log(`Error playing sound "${sound}" for player "${player.name}": ${error.message}`, 'SOUND_EFFECT');
    
    // Try to use tellraw to notify the player directly that there was an error
    try {
      player.runCommand(`tellraw @s {"rawtext":[{"text":"§cSound error: ${error.message}"}]}`);
    } catch (e) {
      // Ignore errors from the tellraw attempt
    }
    
    // Fallback to a default sound if available
    if (sound !== "minecraft:block.note_block.bass") {
      log(`Attempting to play fallback sound for "${sound}"`, 'SOUND_EFFECT');
      try {
        player.playSound("minecraft:block.note_block.bass", { volume: 0.5, pitch: 1.0, loop: false });
      } catch (fallbackError) {
        log(`Even fallback sound failed: ${fallbackError.message}`, 'SOUND_EFFECT');
      }
    }
  }
}

/**
 * Stops a sound for a specific player.
 * @param {import("@minecraft/server").Player} player - The player to stop the sound for.
 * @param {string} sound - The sound event name.
 */
export function stopSound(player, sound) {
  try {
    // player.stopSound(sound);
    player.runCommandAsync(`stopsound @s ${sound}`);
    log(`Stopped sound: ${sound} for player: ${player.name}`, 'PROXIMITY');
  } catch (error) {
    log(`Error stopping sound "${sound}" for player "${player.name}": ${error.message}`, 'PROXIMITY');
  }
}

/**
 * Calculates the distance between two points.
 * @param {import("@minecraft/server").Vector3} pos1 
 * @param {import("@minecraft/server").Vector3} pos2 
 * @returns {number} Distance between pos1 and pos2
 */
export function calculateDistance(pos1, pos2) {
  return Math.hypot(pos2.x - pos1.x, pos2.y - pos1.y, pos2.z - pos1.z);
}

/**
 * Sound cooldown management to prevent spamming sounds.
 */
const soundCooldowns = {};

/**
 * Determines if a sound can be played based on cooldown.
 * @param {string} playerName - Name of the player.
 * @param {string} soundName - Name of the sound.
 * @param {number} cooldown - Cooldown duration in milliseconds.
 * @returns {boolean} - True if the sound can be played, false otherwise.
 */
export function canPlaySound(playerName, soundName, cooldown = 1000) { // cooldown in ms
  const key = `${playerName}:${soundName}`;
  const now = Date.now();
  if (soundCooldowns[key] && (now - soundCooldowns[key]) < cooldown) {
    return false;
  }
  soundCooldowns[key] = now;
  return true;
}

// Enhanced debugging system
export class SoundDebugger {
  constructor(config) {
    this.enabled = config.DEBUG.ALL;
    this.categories = new Set(
      Object.entries(config.DEBUG)
        .filter(([k, v]) => v && k !== 'ALL')
        .map(([k]) => k)
    );
  }

  log(message, category, data = {}) {
    if (!this.enabled && !this.categories.has(category)) return;
    
    const debugInfo = {
      timestamp: new Date().toISOString(),
      category,
      message,
      ...data
    };
    
    console.log(`[DSS][${debugInfo.category}] ${JSON.stringify(debugInfo)}`);
  }
}
