import { randomBytes, scryptSync, createCipheriv, createDecipheriv, timingSafeEqual } from 'node:crypto';

const MAGIC = Buffer.from('KONTO\x01', 'utf8');
const SALT_LEN = 16;
const IV_LEN = 12;
const TAG_LEN = 16;
const KEY_LEN = 32;
const SCRYPT_N = 1 << 15;
const SCRYPT_R = 8;
const SCRYPT_P = 1;
const SCRYPT_MAXMEM = 64 * 1024 * 1024;

function deriveKey(password, salt) {
  return scryptSync(Buffer.from(password, 'utf8'), salt, KEY_LEN, {
    N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P, maxmem: SCRYPT_MAXMEM,
  });
}

export function encrypt(plaintext, password) {
  const salt = randomBytes(SALT_LEN);
  const iv = randomBytes(IV_LEN);
  const key = deriveKey(password, salt);
  const cipher = createCipheriv('aes-256-gcm', key, iv);
  const ct = Buffer.concat([cipher.update(Buffer.from(plaintext, 'utf8')), cipher.final()]);
  const tag = cipher.getAuthTag();
  return Buffer.concat([MAGIC, salt, iv, tag, ct]);
}

export function decrypt(blob, password) {
  if (blob.length < MAGIC.length + SALT_LEN + IV_LEN + TAG_LEN) {
    throw new Error('Config-Datei zu kurz / kein gültiges Format.');
  }
  const magic = blob.subarray(0, MAGIC.length);
  if (!timingSafeEqual(magic, MAGIC)) {
    throw new Error('Unbekanntes Dateiformat (Magic-Header passt nicht).');
  }
  let off = MAGIC.length;
  const salt = blob.subarray(off, off += SALT_LEN);
  const iv = blob.subarray(off, off += IV_LEN);
  const tag = blob.subarray(off, off += TAG_LEN);
  const ct = blob.subarray(off);
  const key = deriveKey(password, salt);
  const decipher = createDecipheriv('aes-256-gcm', key, iv);
  decipher.setAuthTag(tag);
  try {
    const pt = Buffer.concat([decipher.update(ct), decipher.final()]);
    return pt.toString('utf8');
  } catch {
    throw new Error('Falsches Master-Passwort oder Datei beschädigt.');
  }
}
