// SPDX-FileCopyrightText: 2026 FocusFlow contributors
// SPDX-License-Identifier: AGPL-3.0-only

part of '../../encrypted_backup.dart';

/// Top-level helper that performs the `_encryptBytes` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
Future<List<int>> _encryptBytes({
  required String passphrase,
  required List<int> plaintext,
}) async {
  final salt = _randomBytes(_saltLength);
  final nonce = _randomBytes(_nonceLength);
  final key = await _deriveKey(passphrase: passphrase, salt: salt);
  final algorithm = AesGcm.with256bits();
  final secretBox = await algorithm.encrypt(
    plaintext,
    secretKey: key,
    nonce: nonce,
  );
  final envelope = <String, Object?>{
    'magic': _formatMagic,
    'kdf': 'pbkdf2-hmac-sha256',
    'iterations': _kdfIterations,
    'cipher': 'aes-256-gcm',
    'salt': base64Encode(salt),
    'nonce': base64Encode(secretBox.nonce),
    'ciphertext': base64Encode(secretBox.cipherText),
    'mac': base64Encode(secretBox.mac.bytes),
  };
  return _utf8.convert(jsonEncode(envelope));
}

/// Top-level helper that performs the `_decryptBytes` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
Future<List<int>> _decryptBytes({
  required String passphrase,
  required List<int> encoded,
}) async {
  try {
    final envelope = jsonDecode(utf8.decode(encoded)) as Map<String, Object?>;
    if (envelope['magic'] != _formatMagic ||
        envelope['kdf'] != 'pbkdf2-hmac-sha256' ||
        envelope['iterations'] != _kdfIterations ||
        envelope['cipher'] != 'aes-256-gcm') {
      throw const BackupDecryptionException('Unsupported backup format.');
    }

    final salt = base64Decode(envelope['salt'] as String);
    final nonce = base64Decode(envelope['nonce'] as String);
    final cipherText = base64Decode(envelope['ciphertext'] as String);
    final mac = Mac(base64Decode(envelope['mac'] as String));
    final key = await _deriveKey(passphrase: passphrase, salt: salt);
    final algorithm = AesGcm.with256bits();
    return await algorithm.decrypt(
      SecretBox(cipherText, nonce: nonce, mac: mac),
      secretKey: key,
    );
  } on BackupDecryptionException catch (error, stackTrace) {
    logger.error(
      () => 'Encrypted backup decryption failed with known backup error.',
      error: error,
      stackTrace: stackTrace,
    );
    rethrow;
  } on Object catch (error, stackTrace) {
    logger.error(
      () => 'Encrypted backup decryption failed.',
      error: error,
      stackTrace: stackTrace,
    );
    throw const BackupDecryptionException();
  }
}

/// Top-level helper that performs the `_deriveKey` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
Future<SecretKey> _deriveKey({
  required String passphrase,
  required List<int> salt,
}) {
  final algorithm = Pbkdf2(
    macAlgorithm: Hmac.sha256(),
    iterations: _kdfIterations,
    bits: _keyLength * 8,
  );
  return algorithm.deriveKey(
    secretKey: SecretKey(utf8.encode(passphrase)),
    nonce: salt,
  );
}

/// Top-level helper that performs the `_randomBytes` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
List<int> _randomBytes(int length) {
  final random = Random.secure();
  return Uint8List.fromList(
    List<int>.generate(length, (_) => random.nextInt(256)),
  );
}
