import crypto from 'crypto'

// Verschlüsselungskonfiguration
// v1 (legacy): aes-256-cbc (ohne Authentizitätsschutz)
const LEGACY_ALGORITHM = 'aes-256-cbc'
const LEGACY_IV_LENGTH = 16

// v2 (default): aes-256-gcm (AEAD, state-of-the-art)
const ALGORITHM = 'aes-256-gcm'
const IV_LENGTH = 12
const AUTH_TAG_LENGTH = 16
const SALT_LENGTH = 32
const VERSION_PREFIX = 'v2:'

/**
 * Generiert einen Schlüssel aus einem Passwort und Salt
 */
function deriveKey(password, salt) {
  return crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha512')
}

function encryptV2GCM(text, password) {
  // Salt generieren
  const salt = crypto.randomBytes(SALT_LENGTH)

  // Schlüssel ableiten
  const key = deriveKey(password, salt)

  // IV generieren (12 bytes ist Best Practice für GCM)
  const iv = crypto.randomBytes(IV_LENGTH)

  // Cipher erstellen
  const cipher = crypto.createCipheriv(ALGORITHM, key, iv)

  // Verschlüsseln
  const encrypted = Buffer.concat([
    cipher.update(text, 'utf8'),
    cipher.final()
  ])

  // Auth-Tag holen
  const tag = cipher.getAuthTag()

  // Salt + IV + Tag + Ciphertext kombinieren
  const combined = Buffer.concat([salt, iv, tag, encrypted])

  return `${VERSION_PREFIX}${combined.toString('base64')}`
}

function decryptLegacyCBC(encryptedData, password) {
  try {
    // Base64 dekodieren
    const combined = Buffer.from(encryptedData, 'base64')

    // Prüfe minimale Länge (salt + iv = 32 + 16 = 48 bytes)
    if (combined.length < SALT_LENGTH + LEGACY_IV_LENGTH) {
      throw new Error(`Ungültige Datenlänge: ${combined.length} bytes (erwartet mindestens ${SALT_LENGTH + LEGACY_IV_LENGTH})`)
    }

    // Komponenten extrahieren (v1: salt(32) + iv(16) + ciphertext)
    const salt = combined.subarray(0, SALT_LENGTH)
    const iv = combined.subarray(SALT_LENGTH, SALT_LENGTH + LEGACY_IV_LENGTH)
    const encrypted = combined.subarray(SALT_LENGTH + LEGACY_IV_LENGTH)

    // Prüfe ob verschlüsselte Daten vorhanden sind
    if (encrypted.length === 0) {
      throw new Error('Keine verschlüsselten Daten gefunden')
    }

    // Schlüssel ableiten
    const key = deriveKey(password, salt)

    // Decipher erstellen
    const decipher = crypto.createDecipheriv(LEGACY_ALGORITHM, key, iv)

    // Entschlüsseln
    const decrypted = Buffer.concat([
      decipher.update(encrypted),
      decipher.final()
    ])

    return decrypted.toString('utf8')
  } catch (error) {
    // Re-throw mit mehr Kontext
    throw new Error(`Legacy CBC Entschlüsselung fehlgeschlagen: ${error.message}`)
  }
}

function decryptV2GCM(encryptedData, password) {
  try {
    const b64 = encryptedData.slice(VERSION_PREFIX.length)
    const combined = Buffer.from(b64, 'base64')

    // Prüfe minimale Länge (salt + iv + tag = 32 + 12 + 16 = 60 bytes)
    const minLength = SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH
    if (combined.length < minLength) {
      throw new Error(`Ungültige Datenlänge: ${combined.length} bytes (erwartet mindestens ${minLength})`)
    }

    // v2: salt(32) + iv(12) + tag(16) + ciphertext
    const salt = combined.subarray(0, SALT_LENGTH)
    const iv = combined.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH)
    const tagStart = SALT_LENGTH + IV_LENGTH
    const tag = combined.subarray(tagStart, tagStart + AUTH_TAG_LENGTH)
    const encrypted = combined.subarray(tagStart + AUTH_TAG_LENGTH)

    // Prüfe ob verschlüsselte Daten vorhanden sind
    if (encrypted.length === 0) {
      throw new Error('Keine verschlüsselten Daten gefunden')
    }

    const key = deriveKey(password, salt)
    const decipher = crypto.createDecipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_LENGTH })
    decipher.setAuthTag(tag)

    const decrypted = Buffer.concat([
      decipher.update(encrypted),
      decipher.final()
    ])

    return decrypted.toString('utf8')
  } catch (error) {
    // Re-throw mit mehr Kontext
    throw new Error(`GCM v2 Entschlüsselung fehlgeschlagen: ${error.message}`)
  }
}

/**
 * Verschlüsselt einen Text
 */
export function encrypt(text, password) {
  try {
    return encryptV2GCM(text, password)
  } catch (error) {
    console.error('Verschlüsselungsfehler:', error)
    throw new Error('Fehler beim Verschlüsseln der Daten')
  }
}

/**
 * Entschlüsselt einen Text
 */
export function decrypt(encryptedData, password) {
  try {
    if (!encryptedData || typeof encryptedData !== 'string') {
      throw new Error('Ungültige verschlüsselte Daten: muss ein String sein')
    }

    if (!password || typeof password !== 'string') {
      throw new Error('Verschlüsselungsschlüssel fehlt oder ist ungültig')
    }

    if (encryptedData.startsWith(VERSION_PREFIX)) {
      // v2 GCM Format
      return decryptV2GCM(encryptedData, password)
    }

    // Fallback: legacy CBC ohne Prefix
    return decryptLegacyCBC(encryptedData, password)
  } catch (error) {
    console.error('Entschlüsselungsfehler:', error)
    // Re-throw mit mehr Details für besseres Debugging
    if (error.message.includes('Entschlüsselung fehlgeschlagen')) {
      throw error
    }
    throw new Error(`Fehler beim Entschlüsseln der Daten: ${error.message}`)
  }
}

/**
 * Verschlüsselt ein Objekt (konvertiert zu JSON)
 */
export function encryptObject(obj, password) {
  const jsonString = JSON.stringify(obj)
  return encrypt(jsonString, password)
}

/**
 * Entschlüsselt ein Objekt (konvertiert von JSON)
 */
export function decryptObject(encryptedData, password) {
  const jsonString = decrypt(encryptedData, password)
  return JSON.parse(jsonString)
}

/**
 * Generiert einen sicheren Schlüssel für die Datenverschlüsselung
 */
export function generateEncryptionKey() {
  return crypto.randomBytes(32).toString('hex')
}

