part of '../../settings_modal.dart';

// ── Megolm key export / import – top-level so compute() can use them ─────────

const _kMegolmHeader = '-----BEGIN MEGOLM SESSION DATA-----';
const _kMegolmFooter = '-----END MEGOLM SESSION DATA-----';

// PBKDF2-HMAC-SHA512.  args: {pass: Uint8List, salt: Uint8List, iter: int}
Uint8List _runPbkdf2(Map<String, dynamic> args) {
  final pass = args['pass'] as Uint8List;
  final salt = args['salt'] as Uint8List;
  final iterations = args['iter'] as int;
  const blockLen = 64; // SHA-512 digest size
  const dkLen = 64;

  final hmac = mcrypto.Hmac(mcrypto.sha512, pass);
  final blockCount = (dkLen / blockLen).ceil();
  final dk = Uint8List(blockCount * blockLen);

  for (var i = 1; i <= blockCount; i++) {
    final saltI = Uint8List(salt.length + 4);
    saltI.setRange(0, salt.length, salt);
    ByteData.sublistView(saltI, salt.length).setUint32(0, i);

    var u = Uint8List.fromList(hmac.convert(saltI).bytes);
    final block = Uint8List.fromList(u);

    for (var j = 1; j < iterations; j++) {
      u = Uint8List.fromList(hmac.convert(u).bytes);
      for (var k = 0; k < blockLen; k++) { block[k] ^= u[k]; }
    }
    dk.setRange((i - 1) * blockLen, i * blockLen, block);
  }
  return dk.sublist(0, dkLen);
}

Uint8List _aesCtr(Uint8List key, Uint8List iv, Uint8List data) {
  final cipher = pc.SICStreamCipher(pc.AESEngine())
    ..init(true, pc.ParametersWithIV(pc.KeyParameter(key), iv));
  final out = Uint8List(data.length);
  cipher.processBytes(data, 0, data.length, out, 0);
  return out;
}

// args: {passphrase: String, sessions: List<dynamic>}  →  encrypted String
String _encryptMegolmKeys(Map<String, dynamic> args) {
  final pass = args['passphrase'] as String;
  final sessions = args['sessions'] as List<dynamic>;

  final rng = Random.secure();
  final salt = Uint8List.fromList(List.generate(16, (_) => rng.nextInt(256)));
  // IV: 8 random bytes as nonce, last 8 bytes 0x00 (counter starts at 0)
  final iv = Uint8List(16);
  for (var i = 0; i < 8; i++) { iv[i] = rng.nextInt(256); }

  const iterations = 100000;
  final dk = _runPbkdf2({
    'pass': Uint8List.fromList(utf8.encode(pass)),
    'salt': salt,
    'iter': iterations,
  });
  final aesKey = dk.sublist(0, 32);
  final hmacKey = dk.sublist(32, 64);

  final plaintext = Uint8List.fromList(utf8.encode(jsonEncode(sessions)));
  final encrypted = _aesCtr(aesKey, iv, plaintext);

  final iterBe = Uint8List(4)..buffer.asByteData().setUint32(0, iterations);

  // header bytes: 0x01 | salt(16) | iv(16) | iterBe(4) | encrypted(n)
  final header = Uint8List(1 + 16 + 16 + 4 + encrypted.length);
  var off = 0;
  header[off++] = 0x01;
  header.setRange(off, off += 16, salt);
  header.setRange(off, off += 16, iv);
  header.setRange(off, off += 4, iterBe);
  header.setRange(off, off + encrypted.length, encrypted);

  final mac = Uint8List.fromList(
    mcrypto.Hmac(mcrypto.sha256, hmacKey).convert(header).bytes,
  );

  final full = Uint8List(header.length + 32);
  full.setRange(0, header.length, header);
  full.setRange(header.length, full.length, mac);

  return '$_kMegolmHeader\n${base64.encode(full)}\n$_kMegolmFooter';
}

// args: {passphrase: String, data: List<int>}  →  List<dynamic> sessions
List<dynamic> _decryptMegolmKeys(Map<String, dynamic> args) {
  final pass = args['passphrase'] as String;
  final raw = Uint8List.fromList(args['data'] as List<int>);

  if (raw.length < 1 + 16 + 16 + 4 + 32 || raw[0] != 0x01) {
    throw Exception('Ungültiges Dateiformat (Version ${raw.isEmpty ? "?" : raw[0]}).');
  }

  final salt = raw.sublist(1, 17);
  final iv = raw.sublist(17, 33);
  final iterations = raw.buffer.asByteData().getUint32(33);
  final encrypted = raw.sublist(37, raw.length - 32);
  final storedMac = raw.sublist(raw.length - 32);

  if (iterations < 1 || iterations > 10000000) {
    throw Exception('Iterationsanzahl außerhalb des erlaubten Bereichs.');
  }

  final dk = _runPbkdf2({
    'pass': Uint8List.fromList(utf8.encode(pass)),
    'salt': Uint8List.fromList(salt),
    'iter': iterations,
  });
  final aesKey = dk.sublist(0, 32);
  final hmacKey = dk.sublist(32, 64);

  // Verify MAC over all bytes except the appended MAC itself
  final toMac = raw.sublist(0, raw.length - 32);
  final expectedMac = mcrypto.Hmac(mcrypto.sha256, hmacKey).convert(toMac).bytes;
  var macOk = true;
  for (var i = 0; i < 32; i++) {
    if (expectedMac[i] != storedMac[i]) { macOk = false; break; }
  }
  if (!macOk) throw Exception('Falsches Passwort oder beschädigte Datei.');

  final decrypted = _aesCtr(aesKey, Uint8List.fromList(iv), Uint8List.fromList(encrypted));
  return jsonDecode(utf8.decode(decrypted)) as List<dynamic>;
}

