const crypto = require('crypto');

/**
 * Utility für ID-Hashing
 * Konvertiert numerische IDs in Hashes und zurück
 */
class HashId {
  constructor() {
    // Secret aus Umgebungsvariable oder Fallback
    this.secret = process.env.HASH_ID_SECRET || 'timeclock-hash-secret-change-in-production';
  }

  /**
   * Konvertiert eine numerische ID in einen Hash
   * @param {number} id - Numerische ID
   * @returns {string} Hash-String
   */
  encode(id) {
    if (!id) return null;
    
    // Erstelle einen deterministischen Hash aus ID + Secret
    const hmac = crypto.createHmac('sha256', this.secret);
    hmac.update(id.toString());
    const hash = hmac.digest('base64url'); // base64url ist URL-sicher
    
    // Füge die ID in verschlüsselter Form hinzu für Dekodierung
    const encrypted = this.encryptId(id);
    
    // Format: {verschlüsselte-id}.{hash-prefix}
    return `${encrypted}.${hash.substring(0, 12)}`;
  }

  /**
   * Konvertiert einen Hash zurück in eine numerische ID
   * @param {string} hash - Hash-String
   * @returns {number|null} Numerische ID oder null bei Fehler
   */
  decode(hash) {
    if (!hash || typeof hash !== 'string') return null;
    
    try {
      // Extrahiere verschlüsselte ID
      const parts = hash.split('.');
      if (parts.length !== 2) return null;
      
      const encrypted = parts[0];
      const hashPart = parts[1];
      
      // Entschlüssele ID
      const id = this.decryptId(encrypted);
      if (!id) return null;
      
      // Verifiziere Hash
      const expectedHash = this.encode(id);
      if (!expectedHash || !expectedHash.endsWith(hashPart)) {
        return null;
      }
      
      return id;
    } catch (error) {
      console.error('Fehler beim Dekodieren der Hash-ID:', error);
      return null;
    }
  }

  /**
   * Verschlüsselt eine ID
   * @private
   */
  encryptId(id) {
    const cipher = crypto.createCipheriv(
      'aes-256-cbc',
      crypto.scryptSync(this.secret, 'salt', 32),
      Buffer.alloc(16, 0) // IV
    );
    
    let encrypted = cipher.update(id.toString(), 'utf8', 'base64url');
    encrypted += cipher.final('base64url');
    
    return encrypted;
  }

  /**
   * Entschlüsselt eine ID
   * @private
   */
  decryptId(encrypted) {
    try {
      const decipher = crypto.createDecipheriv(
        'aes-256-cbc',
        crypto.scryptSync(this.secret, 'salt', 32),
        Buffer.alloc(16, 0) // IV
      );
      
      let decrypted = decipher.update(encrypted, 'base64url', 'utf8');
      decrypted += decipher.final('utf8');
      
      return parseInt(decrypted, 10);
    } catch (error) {
      return null;
    }
  }

  /**
   * Konvertiert ein Objekt mit IDs in eines mit Hashes
   * @param {Object} obj - Objekt mit ID-Feldern
   * @param {Array<string>} idFields - Array von Feldnamen, die IDs enthalten
   * @returns {Object} Objekt mit gehashten IDs
   */
  encodeObject(obj, idFields = ['id', 'user_id', 'auth_info_id']) {
    if (!obj) return obj;
    
    const result = { ...obj };
    
    for (const field of idFields) {
      if (result[field] !== null && result[field] !== undefined) {
        result[field] = this.encode(result[field]);
      }
    }
    
    return result;
  }

  /**
   * Konvertiert ein Array von Objekten
   * @param {Array} array - Array von Objekten
   * @param {Array<string>} idFields - Array von Feldnamen, die IDs enthalten
   * @returns {Array} Array mit gehashten IDs
   */
  encodeArray(array, idFields = ['id', 'user_id', 'auth_info_id']) {
    if (!Array.isArray(array)) return array;
    
    return array.map(obj => this.encodeObject(obj, idFields));
  }
}

// Singleton-Instanz
const hashId = new HashId();

module.exports = hashId;



