/**
 * Traveller NPC Generator - Utilitaires
 * 
 * Ce fichier contient les classes et fonctions utilitaires pour le générateur.
 */

const MODULE_ID = 'mgt2-compendium-amiral-denisov';

// ============================================================================
// Classe de gestion des erreurs
// ============================================================================

/**
 * Erreur spécifique au générateur de PNJ Traveller
 */
export class TravellerNpcError extends Error {
  /**
   * @param {string} message - Message d'erreur
   * @param {string} code - Code d'erreur
   * @param {Object} [details={}] - Détails supplémentaires
   */
  constructor(message, code, details = {}) {
    super(message);
    this.name = 'TravellerNpcError';
    this.code = code;
    this.details = details;
    this.isTravellerNpcError = true;
    this.timestamp = new Date().toISOString();
  }

  /**
   * Crée une erreur à partir d'une erreur existante
   * @param {Error} error - Erreur originale
   * @param {string} code - Code d'erreur
   * @returns {TravellerNpcError}
   */
  static from(error, code = 'UNKNOWN') {
    if (error?.isTravellerNpcError) {
      return error;
    }
    return new TravellerNpcError(
      error?.message || 'Erreur inconnue',
      code,
      { originalError: error }
    );
  }

  /**
   * Log l'erreur dans la console
   */
  log() {
    console.error(`${MODULE_ID} | [${this.code}] ${this.message}`, {
      details: this.details,
      timestamp: this.timestamp
    });
  }

  /**
   * Affiche une notification à l'utilisateur et log l'erreur
   */
  notify() {
    ui.notifications?.error(`${this.code}: ${this.message}`);
    this.log();
  }

  /**
   * Crée une notification sans lancer d'erreur
   * @param {string} message - Message
   * @param {string} code - Code
   */
  static warn(message, code) {
    const error = new TravellerNpcError(message, code);
    error.log();
    return error;
  }
}

// ============================================================================
// Classe de cache pour le module
// ============================================================================

/**
 * Système de cache générique pour le module
 */
export class ModuleCache {
  /**
   * @param {string} moduleId - ID du module
   * @param {number} [defaultTTL=300000] - Durée de vie par défaut (5 min)
   */
  constructor(moduleId, defaultTTL = 300000) {
    this.moduleId = moduleId;
    this.defaultTTL = defaultTTL;
    this.cache = new Map();
    this.pending = new Map();
    this.timestamps = new Map();
  }

  /**
   * Récupère une valeur du cache ou la fetch
   * @template T
   * @param {string} key - Clé de cache
   * @param {Function} fetchFn - Fonction de récupération
   * @param {Object} [options={}] - Options
   * @param {boolean} [options.forceRefresh=false] - Forcer le rafraîchissement
   * @param {number} [options.ttl] - TTL spécifique
   * @returns {Promise<T>}
   */
  async getOrFetch(key, fetchFn, options = {}) {
    const { forceRefresh = false, ttl = this.defaultTTL } = options;

    // Si déjà en cache et non expiré
    if (!forceRefresh && this.cache.has(key)) {
      const timestamp = this.timestamps.get(key);
      if (Date.now() - timestamp < ttl) {
        return foundry.utils.deepClone(this.cache.get(key));
      }
      // Expiré, on le supprime
      this.clear(key);
    }

    // Si déjà en cours de fetch pour cette clé
    if (this.pending.has(key)) {
      return this.pending.get(key);
    }

    // Nouveau fetch
    const promise = (async () => {
      try {
        const result = await fetchFn();
        const cachedResult = result ? foundry.utils.deepClone(result) : null;
        this.cache.set(key, cachedResult);
        this.timestamps.set(key, Date.now());
        this.pending.delete(key);
        return foundry.utils.deepClone(cachedResult);
      } catch (error) {
        console.warn(`${this.moduleId} | Erreur de cache pour ${key}:`, error);
        this.pending.delete(key);
        throw error;
      }
    })();

    this.pending.set(key, promise);
    return promise;
  }

  /**
   * Vide une entrée du cache
   * @param {string} key - Clé à supprimer
   */
  clear(key) {
    this.cache.delete(key);
    this.pending.delete(key);
    this.timestamps.delete(key);
  }

  /**
   * Vide tout le cache
   */
  clearAll() {
    this.cache.clear();
    this.pending.clear();
    this.timestamps.clear();
  }

  /**
   * Vérifie si une clé est en cache
   * @param {string} key - Clé à vérifier
   * @returns {boolean}
   */
  has(key) {
    if (!this.cache.has(key)) return false;
    const timestamp = this.timestamps.get(key);
    return Date.now() - timestamp < this.defaultTTL;
  }

  /**
   * Récupère une valeur du cache sans vérification d'expiration
   * @template T
   * @param {string} key - Clé
   * @returns {T|null}
   */
  get(key) {
    return this.cache.get(key) ?? null;
  }
}

// ============================================================================
// Instance de cache pour le module
// ============================================================================

/**
 * Instance de cache partagée pour le générateur de PNJ Traveller
 * @type {ModuleCache}
 */
export const travellerNpcCache = new ModuleCache(MODULE_ID, 300000); // 5 min TTL

// ============================================================================
// Codes d'erreur
// ============================================================================

/**
 * Codes d'erreur standard pour le module
 */
export const ERROR_CODES = {
  INVALID_OPTIONS: 'INVALID_OPTIONS',
  INVALID_ROLE: 'INVALID_ROLE',
  INVALID_CATEGORY: 'INVALID_CATEGORY',
  INVALID_EXPERIENCE: 'INVALID_EXPERIENCE',
  INVALID_GENDER: 'INVALID_GENDER',
  ACTOR_CREATION_FAILED: 'ACTOR_CREATION_FAILED',
  MGT2E_NOT_ACTIVE: 'MGT2E_NOT_ACTIVE',
  BASE_ACTOR_NOT_FOUND: 'BASE_ACTOR_NOT_FOUND',
  CACHE_ERROR: 'CACHE_ERROR',
  GENERATION_ERROR: 'GENERATION_ERROR'
};

// ============================================================================
// Fonctions utilitaires générales
// ============================================================================

/**
 * Formate un message de debug
 * @param {string} message - Message
 * @param {Object} [data={}] - Données supplémentaires
 */
export function debug(message, data = {}) {
  if (game.settings.get(MODULE_ID, 'debug') || game.user?.isGM) {
    console.debug(`${MODULE_ID} | ${message}`, data);
  }
}

/**
 * Formate un message de log
 * @param {string} message - Message
 * @param {Object} [data={}] - Données supplémentaires
 */
export function log(message, data = {}) {
  console.log(`${MODULE_ID} | ${message}`, data);
}

/**
 * Formate un message d'avertissement
 * @param {string} message - Message
 * @param {Object} [data={}] - Données supplémentaires
 */
export function warn(message, data = {}) {
  console.warn(`${MODULE_ID} | ${message}`, data);
}

/**
 * Formate un message d'erreur
 * @param {string} message - Message
 * @param {Object} [data={}] - Données supplémentaires
 */
export function error(message, data = {}) {
  console.error(`${MODULE_ID} | ${message}`, data);
}

// ============================================================================
// Export par défaut
// ============================================================================

export default {
  TravellerNpcError,
  ModuleCache,
  travellerNpcCache,
  ERROR_CODES,
  debug,
  log,
  warn,
  error
};
