import 'dart:convert';
import 'dart:isolate';
import 'dart:math';
import 'dart:typed_data';

import 'package:asn1lib/asn1lib.dart';
import 'package:crypto/crypto.dart';
import 'package:encrypt/encrypt.dart' as encrypt_pkg;
import 'package:pointycastle/api.dart';
import 'package:pointycastle/asymmetric/api.dart';
import 'package:pointycastle/asymmetric/pkcs1.dart';
import 'package:pointycastle/asymmetric/rsa.dart';
import 'package:pointycastle/digests/sha256.dart';
import 'package:pointycastle/key_derivators/api.dart';
import 'package:pointycastle/key_derivators/pbkdf2.dart';
import 'package:pointycastle/key_generators/api.dart';
import 'package:pointycastle/key_generators/rsa_key_generator.dart';
import 'package:pointycastle/macs/hmac.dart';
import 'package:pointycastle/random/fortuna_random.dart';

/// 密钥派生模式
///
/// 决定 [EncryptionFlutterService._deriveKeyForRound] 使用哪种算法。
/// 默认 [md5]，可选 [pbkdf2]（增强安全性）。
///
/// 解密旧数据时必须使用加密时相同的模式，
/// 通过消息的 version 字段区分。
enum KdfMode {
  /// MD5 简单哈希（默认模式）
  ///
  /// 适用于 session key 已是 32 字节强随机值的场景。
  /// 性能好，每次调用 < 0.1ms。
  md5,

  /// PBKDF2-HMAC-SHA256（可选增强模式）
  ///
  /// 适用于从弱密码派生密钥的场景。
  /// 性能取决于迭代次数，10000 次约 10-50ms。
  pbkdf2,
}

/// Flutter 加密服务
///
/// 端对端加密的核心引擎，纯 Dart 实现。
/// 使用 pointycastle（RSA）+ encrypt（AES）+ crypto（MD5）。
///
/// ## 性能优化
///
/// - **RSA 密钥生成**：通过 [generateRsaKeyPairAsync] 在 Isolate 中运行，
///   避免阻塞主线程（1024-bit 约 150ms，2048-bit 约 300ms）
/// - **RSA 解析缓存**：[_parsePublicKey] / [_parsePrivateKey] 缓存 ASN1 解析结果，
///   同一密钥 PEM 只做一次 BigInt 构造，后续命中缓存（LRU，上限 8 条）
/// - **Session key bytes 缓存**：[_getSessionKeyBytes] 缓存 base64 → Uint8List 结果，
///   同一 session 的多条消息只解码一次（LRU，上限 64 条）
/// - **派生密钥缓存**：[_deriveKeyForRound] 结果按 (sessionKey, round, mode) 缓存，
///   同一 session + round 的重复加解密直接命中（LRU，上限 64 条）
/// - **Random.secure() 复用**：全局单例，不再每次调用创建新实例
/// - **KDF 双模式**：MD5（默认）/ PBKDF2（可选，增强安全性）
///
/// ## 正确的接入姿势（避免重复读文件）
///
/// 调用方（App 层）在登录后调一次 [CipherGuardSdkApi.setActiveKeyPair]，
/// 把从安全存储读出的公私钥注入 SDK 内存。后续加解密使用
/// [CipherGuardSdkApi.encryptSessionKeyWithActiveKey] /
/// [CipherGuardSdkApi.decryptSessionKeyWithActiveKey]，
/// 不再每次传 key 参数，也不再重复读文件。
class EncryptionFlutterService {
  // ==================== 配置 ====================

  /// 密钥派生模式，默认 MD5
  final KdfMode kdfMode;

  /// PBKDF2 迭代次数（仅 PBKDF2 模式有效，默认 10000）
  final int pbkdf2Iterations;

  EncryptionFlutterService({
    this.kdfMode = KdfMode.md5,
    this.pbkdf2Iterations = 10000,
  });

  // ==================== 常量 ====================

  static const int sessionKeySize = 32;
  static const int gcmIvLength = 12;
  static const int _maxDerivedKeyCacheSize = 64;
  static const int _maxRsaKeyCacheSize = 8;
  static const int _maxSessionKeyBytesCacheSize = 64;

  // ==================== 性能优化：复用 Random 实例 ====================

  /// 全局 Random.secure() 单例，避免每次调用创建新实例
  static final Random _secureRandom = Random.secure();

  // ==================== 性能优化：派生密钥 LRU 缓存 ====================

  /// 派生密钥缓存：'sessionKey:round:mode' -> Uint8List
  ///
  /// 同一 session + round 的加解密只派生一次，后续直接命中缓存。
  /// LinkedHashMap 保持插入顺序，满时淘汰最早条目。
  final _derivedKeyCache = <String, Uint8List>{};

  /// 清空派生密钥缓存（session key 轮换时调用）
  void clearDerivedKeyCache() => _derivedKeyCache.clear();

  // ==================== 性能优化：RSA 解析缓存 ====================

  /// RSA 公钥解析缓存：PEM -> RSAPublicKey
  ///
  /// RSA 密钥生命周期长（通常每设备一对），ASN1 解析 + BigInt 构造代价较高。
  /// 解析结果在内存中复用，省去重复解析开销。上限 8 条，满时淘汰最早。
  final _rsaPublicKeyCache = <String, RSAPublicKey>{};

  /// RSA 私钥解析缓存：PEM -> RSAPrivateKey
  final _rsaPrivateKeyCache = <String, RSAPrivateKey>{};

  // ==================== 性能优化：session key bytes 缓存 ====================

  /// Session key Base64 → 字节缓存
  ///
  /// _deriveKeyForRound 和 _pbkdf2Derive 每次都需要 base64Decode(sessionKey)，
  /// 对同一会话的多条消息重复解码。缓存后只解码一次，满时淘汰最早。
  final _sessionKeyBytesCache = <String, Uint8List>{};

  // ==================== RSA 密钥管理 ====================

  /// 生成 RSA 密钥对（同步，阻塞主线程）
  ///
  /// 建议使用 [generateRsaKeyPairAsync] 代替，避免 UI 卡顿。
  RsaKeyPairResult generateRsaKeyPair({int keySize = 1024}) {
    try {
      final secureRandom = FortunaRandom();
      secureRandom.seed(KeyParameter(_generateSecureRandomBytes(32)));

      final keyGen = RSAKeyGenerator();
      keyGen.init(
        ParametersWithRandom(
          RSAKeyGeneratorParameters(BigInt.parse('65537'), keySize, 64),
          secureRandom,
        ),
      );

      final keyPair = keyGen.generateKeyPair();
      final rsaPublicKey = keyPair.publicKey;
      final rsaPrivateKey = keyPair.privateKey;

      final publicKeyPem = _encodeRSAPublicKey(rsaPublicKey);
      final privateKeyPem = _encodeRSAPrivateKey(rsaPrivateKey);

      return RsaKeyPairResult(
        publicKey: publicKeyPem,
        privateKey: privateKeyPem,
      );
    } catch (e) {
      throw Exception('Failed to generate RSA key pair: $e');
    }
  }

  /// 生成 RSA 密钥对（异步，在 Isolate 中运行，不阻塞主线程）
  ///
  /// RSA 密钥生成是 CPU 密集型操作（1024-bit 约 150ms，2048-bit 约 300ms），
  /// 放在 Isolate 中避免主线程卡顿。
  ///
  /// **Isolate 隔离说明**：
  /// Isolate 内会创建一个**默认配置**的 EncryptionFlutterService（KdfMode.md5），
  /// 不会继承当前实例的 kdfMode / pbkdf2Iterations。
  /// 这对 RSA 密钥生成没有影响（RSA 不走 KDF），但如果将来需要在
  /// Isolate 中执行依赖 KDF 的操作（如消息加解密），需要传递配置参数。
  Future<RsaKeyPairResult> generateRsaKeyPairAsync({int keySize = 1024}) async {
    return await Isolate.run(
      () => EncryptionFlutterService().generateRsaKeyPair(keySize: keySize),
    );
  }

  /// 编码 RSA 公钥为 PEM 格式
  String _encodeRSAPublicKey(RSAPublicKey publicKey) {
    final topSeq = ASN1Sequence();

    final algoSeq = ASN1Sequence();
    algoSeq.add(ASN1ObjectIdentifier([1, 2, 840, 113549, 1, 1, 1]));
    algoSeq.add(ASN1Null());
    topSeq.add(algoSeq);

    final keySeq = ASN1Sequence();
    keySeq.add(ASN1Integer(publicKey.n!));
    keySeq.add(ASN1Integer(publicKey.exponent!));

    final keyBytes = keySeq.encodedBytes;
    final keyList = List<int>.from(keyBytes);
    keyList.insert(0, 0);
    topSeq.add(ASN1BitString(keyList));

    final derBytes = topSeq.encodedBytes;
    final base64 = base64Encode(derBytes.toList());
    return '-----BEGIN PUBLIC KEY-----\n$base64\n-----END PUBLIC KEY-----';
  }

  /// 编码 RSA 私钥为 PEM 格式
  String _encodeRSAPrivateKey(RSAPrivateKey privateKey) {
    final topSeq = ASN1Sequence();
    topSeq.add(ASN1Integer(BigInt.zero));
    topSeq.add(ASN1Integer(privateKey.n!));
    topSeq.add(ASN1Integer(privateKey.exponent!));
    topSeq.add(ASN1Integer(privateKey.privateExponent!));
    topSeq.add(ASN1Integer(privateKey.p!));
    topSeq.add(ASN1Integer(privateKey.q!));

    final derBytes = topSeq.encodedBytes;
    final base64 = base64Encode(derBytes.toList());
    return '-----BEGIN PRIVATE KEY-----\n$base64\n-----END PRIVATE KEY-----';
  }

  // ==================== 私钥加密/解密 ====================

  /// 用密码加密私钥（AES-CBC，密码通过 MD5 派生密钥）
  String encryptPrivateKey({
    required String privateKey,
    required String password,
  }) {
    try {
      final aesKey = _md5Hash(password);
      final iv = _generateSecureRandomBytes(16);

      final secretKey = encrypt_pkg.Key(aesKey);
      final encryptor = encrypt_pkg.Encrypter(
        encrypt_pkg.AES(secretKey, mode: encrypt_pkg.AESMode.cbc),
      );

      final encrypted = encryptor.encrypt(privateKey, iv: encrypt_pkg.IV(iv));
      final encryptedBytes = encrypted.bytes;

      final combined = Uint8List(iv.length + encryptedBytes.length);
      combined.setAll(0, iv);
      combined.setAll(iv.length, encryptedBytes);

      return base64Encode(combined);
    } catch (e) {
      throw Exception('Failed to encrypt private key: $e');
    }
  }

  /// 用密码解密私钥（AES-CBC，密码通过 MD5 派生密钥）
  String decryptPrivateKey({
    required String encryptedPrivateKey,
    required String password,
  }) {
    try {
      final aesKey = _md5Hash(password);
      final combined = base64Decode(encryptedPrivateKey);
      final iv = combined.sublist(0, 16);
      final encBytes = combined.sublist(16);

      final secretKey = encrypt_pkg.Key(aesKey);
      final encryptor = encrypt_pkg.Encrypter(
        encrypt_pkg.AES(secretKey, mode: encrypt_pkg.AESMode.cbc),
      );

      final decrypted = encryptor.decrypt(
        encrypt_pkg.Encrypted(encBytes),
        iv: encrypt_pkg.IV(iv),
      );

      return decrypted;
    } catch (e) {
      throw Exception('Failed to decrypt private key: $e');
    }
  }

  // ==================== 会话密钥管理 ====================

  /// 生成会话密钥（32 字节随机）
  SessionKeyResult generateSessionKey({int initialRound = 1}) {
    final keyBytes = _generateSecureRandomBytes(sessionKeySize);
    final key = base64Encode(keyBytes);

    return SessionKeyResult(key: key, round: initialRound);
  }

  /// 用 RSA 公钥加密会话密钥
  String encryptSessionKey({
    required String sessionKey,
    required String publicKey,
  }) {
    try {
      final rsaPublicKey = _parsePublicKey(publicKey);

      final cipher = PKCS1Encoding(RSAEngine());
      cipher.init(true, PublicKeyParameter<RSAPublicKey>(rsaPublicKey));

      final encryptedBytes = cipher.process(utf8.encode(sessionKey));
      return base64Encode(encryptedBytes);
    } catch (e) {
      throw Exception('Failed to encrypt session key: $e');
    }
  }

  /// 用 RSA 私钥解密会话密钥
  String decryptSessionKey({
    required String encryptedSessionKey,
    required String privateKey,
  }) {
    try {
      final rsaPrivateKey = _parsePrivateKey(privateKey);

      final cipher = PKCS1Encoding(RSAEngine());
      cipher.init(false, PrivateKeyParameter<RSAPrivateKey>(rsaPrivateKey));

      final decryptedBytes = cipher.process(base64Decode(encryptedSessionKey));
      return utf8.decode(decryptedBytes);
    } catch (e) {
      throw Exception('Failed to decrypt session key: $e');
    }
  }

  // ==================== 消息加密/解密 ====================

  /// 加密消息（AES-CTR，使用 round 派生密钥）
  EncryptedMessageResult encryptMessage({
    required String plaintext,
    required String sessionKey,
    required int round,
  }) {
    try {
      final actualKey = _deriveKeyForRound(sessionKey, round);
      final iv = _generateSecureRandomBytes(16);

      final secretKey = encrypt_pkg.Key(actualKey);
      final encryptor = encrypt_pkg.Encrypter(
        encrypt_pkg.AES(secretKey, mode: encrypt_pkg.AESMode.ctr),
      );

      final encrypted = encryptor.encrypt(plaintext, iv: encrypt_pkg.IV(iv));
      final encryptedBytes = encrypted.bytes;

      final combined = Uint8List(iv.length + encryptedBytes.length);
      combined.setAll(0, iv);
      combined.setAll(iv.length, encryptedBytes);

      final data = base64Encode(combined);

      return EncryptedMessageResult(round: round, data: data);
    } catch (e) {
      throw Exception('Failed to encrypt message: $e');
    }
  }

  /// 解密消息（AES-CTR，使用 round 派生密钥）
  String decryptMessage({
    required String encryptedData,
    required String sessionKey,
    required int round,
  }) {
    try {
      final actualKey = _deriveKeyForRound(sessionKey, round);
      final combined = base64Decode(encryptedData);
      final iv = combined.sublist(0, 16);
      final encBytes = combined.sublist(16);

      final secretKey = encrypt_pkg.Key(actualKey);
      final encryptor = encrypt_pkg.Encrypter(
        encrypt_pkg.AES(secretKey, mode: encrypt_pkg.AESMode.ctr),
      );

      final decrypted = encryptor.decrypt(
        encrypt_pkg.Encrypted(encBytes),
        iv: encrypt_pkg.IV(iv),
      );

      return decrypted;
    } catch (e) {
      throw Exception('Failed to decrypt message: $e');
    }
  }

  // ==================== 推送通知解密 ====================

  /// 设置 AES secret（用于推送通知解密）
  void setAesSecret(String aesSecret) {
    _aesSecret = aesSecret;
  }

  String? _aesSecret;

  /// 解密推送通知（AES-GCM）
  String decryptPushNotification({required String encryptedData}) {
    try {
      final secret = _aesSecret;
      if (secret == null) {
        throw Exception('AES_SECRET not set');
      }

      final secretBytes = _hexStringToBytes(secret);
      final combined = base64Decode(encryptedData);
      final iv = combined.sublist(0, gcmIvLength);
      final encBytes = combined.sublist(gcmIvLength);

      final secretKey = encrypt_pkg.Key(secretBytes);
      final encryptor = encrypt_pkg.Encrypter(
        encrypt_pkg.AES(secretKey, mode: encrypt_pkg.AESMode.gcm),
      );

      final decrypted = encryptor.decrypt(
        encrypt_pkg.Encrypted(encBytes),
        iv: encrypt_pkg.IV(iv),
      );

      return decrypted;
    } catch (e) {
      throw Exception('Failed to decrypt push notification: $e');
    }
  }

  // ==================== 内部方法 ====================

  /// 生成安全随机字节（复用全局 Random.secure() 实例）
  Uint8List _generateSecureRandomBytes(int length) {
    final bytes = Uint8List(length);
    for (var i = 0; i < length; i++) {
      bytes[i] = _secureRandom.nextInt(256);
    }
    return bytes;
  }

  /// MD5 哈希（用于密码派生密钥）
  Uint8List _md5Hash(String input) {
    final bytes = utf8.encode(input);
    final hash = md5.convert(bytes).bytes;
    return Uint8List.fromList(hash);
  }

  /// 按 round 派生 AES 密钥（带 LRU 缓存）
  ///
  /// 支持两种模式：
  /// - [KdfMode.md5]：MD5(sessionKey + round)，兼容模式，< 0.1ms
  /// - [KdfMode.pbkdf2]：PBKDF2-HMAC-SHA256(sessionKey, salt=round)，约 10-50ms
  ///
  /// 两种模式都会将 round 参与派生计算，保证不同 round 产出不同密钥。
  /// 缓存命中时直接返回，跳过计算。
  /// 缓存满时淘汰最久未访问的条目（LRU）。
  Uint8List _deriveKeyForRound(String sessionKey, int targetRound) {
    final modeName = kdfMode == KdfMode.md5 ? 'md5' : 'pbkdf2';
    final cacheKey = '$sessionKey:$targetRound:$modeName';

    // 缓存命中 — 移至末尾以维护 LRU 顺序
    final cached = _derivedKeyCache.remove(cacheKey);
    if (cached != null) {
      _derivedKeyCache[cacheKey] = cached;
      return cached;
    }

    // 计算派生密钥
    final Uint8List result;
    switch (kdfMode) {
      case KdfMode.md5:
        // 将 sessionKey + round 一起参与 hash，保证不同 round 产出不同密钥
        final keyBytes = _getSessionKeyBytes(sessionKey);
        final roundBytes = utf8.encode(':$targetRound');
        final combined = Uint8List(keyBytes.length + roundBytes.length)
          ..setRange(0, keyBytes.length, keyBytes)
          ..setRange(
            keyBytes.length,
            keyBytes.length + roundBytes.length,
            roundBytes,
          );
        final hash = md5.convert(combined).bytes;
        result = Uint8List.fromList(hash);
      case KdfMode.pbkdf2:
        result = _pbkdf2Derive(sessionKey, targetRound);
    }

    // LRU 淘汰：满时移除最久未访问的条目（Map 头部）
    if (_derivedKeyCache.length >= _maxDerivedKeyCacheSize) {
      _derivedKeyCache.remove(_derivedKeyCache.keys.first);
    }
    _derivedKeyCache[cacheKey] = result;

    return result;
  }

  /// PBKDF2-HMAC-SHA256 密钥派生
  ///
  /// salt 包含 round 信息，不同 round 派生不同密钥。
  /// 迭代次数由 [pbkdf2Iterations] 控制（默认 10000）。
  /// 输出 16 字节（AES-128 密钥）。
  Uint8List _pbkdf2Derive(String sessionKey, int targetRound) {
    final keyBytes = _getSessionKeyBytes(sessionKey);
    final salt = utf8.encode('round:$targetRound');

    final derivator = PBKDF2KeyDerivator(HMac(SHA256Digest(), 64));
    derivator.init(
      Pbkdf2Parameters(Uint8List.fromList(salt), pbkdf2Iterations, 16),
    );

    return derivator.process(Uint8List.fromList(keyBytes));
  }

  /// 解析 RSA 公钥 PEM（带缓存）
  RSAPublicKey _parsePublicKey(String pem) {
    final cached = _rsaPublicKeyCache.remove(pem);
    if (cached != null) {
      _rsaPublicKeyCache[pem] = cached;
      return cached;
    }

    final b64 = pem
        .replaceAll('-----BEGIN PUBLIC KEY-----', '')
        .replaceAll('-----END PUBLIC KEY-----', '')
        .replaceAll('\n', '')
        .trim();
    final bytes = base64Decode(b64);

    final asn1Parser = ASN1Parser(Uint8List.fromList(bytes));
    final topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;

    final subjectPublicKeyInfo = topLevelSeq.elements[1] as ASN1BitString;
    final keyBytes = subjectPublicKeyInfo.contentBytes();
    final keyParser = ASN1Parser(Uint8List.fromList(keyBytes));
    final keySeq = keyParser.nextObject() as ASN1Sequence;

    final modulus = keySeq.elements[0] as ASN1Integer;
    final publicExponent = keySeq.elements[1] as ASN1Integer;

    final key = RSAPublicKey(
      modulus.valueAsBigInteger,
      publicExponent.valueAsBigInteger,
    );

    if (_rsaPublicKeyCache.length >= _maxRsaKeyCacheSize) {
      _rsaPublicKeyCache.remove(_rsaPublicKeyCache.keys.first);
    }
    _rsaPublicKeyCache[pem] = key;
    return key;
  }

  /// 解析 RSA 私钥 PEM（带缓存）
  RSAPrivateKey _parsePrivateKey(String pem) {
    final cached = _rsaPrivateKeyCache.remove(pem);
    if (cached != null) {
      _rsaPrivateKeyCache[pem] = cached;
      return cached;
    }

    final b64 = pem
        .replaceAll('-----BEGIN PRIVATE KEY-----', '')
        .replaceAll('-----END PRIVATE KEY-----', '')
        .replaceAll('\n', '')
        .trim();
    final bytes = base64Decode(b64);

    final asn1Parser = ASN1Parser(Uint8List.fromList(bytes));
    final keySeq = asn1Parser.nextObject() as ASN1Sequence;

    final modulus = keySeq.elements[1] as ASN1Integer;
    final privateExponent = keySeq.elements[3] as ASN1Integer;
    final p = keySeq.elements[4] as ASN1Integer;
    final q = keySeq.elements[5] as ASN1Integer;

    final key = RSAPrivateKey(
      modulus.valueAsBigInteger,
      privateExponent.valueAsBigInteger,
      p.valueAsBigInteger,
      q.valueAsBigInteger,
    );

    if (_rsaPrivateKeyCache.length >= _maxRsaKeyCacheSize) {
      _rsaPrivateKeyCache.remove(_rsaPrivateKeyCache.keys.first);
    }
    _rsaPrivateKeyCache[pem] = key;
    return key;
  }

  /// session key Base64 → 字节（带缓存）
  ///
  /// 同一 session key 在多条消息加解密中反复 decode，缓存后只做一次。
  Uint8List _getSessionKeyBytes(String sessionKey) {
    final cached = _sessionKeyBytesCache.remove(sessionKey);
    if (cached != null) {
      _sessionKeyBytesCache[sessionKey] = cached;
      return cached;
    }
    final bytes = base64Decode(sessionKey);
    if (_sessionKeyBytesCache.length >= _maxSessionKeyBytesCacheSize) {
      _sessionKeyBytesCache.remove(_sessionKeyBytesCache.keys.first);
    }
    _sessionKeyBytesCache[sessionKey] = bytes;
    return bytes;
  }

  /// Hex 字符串转字节
  Uint8List _hexStringToBytes(String hex) {
    final len = hex.length;
    final data = Uint8List(len ~/ 2);
    for (var i = 0; i < len; i += 2) {
      data[i ~/ 2] =
          (int.parse(hex[i], radix: 16) << 4) +
          int.parse(hex[i + 1], radix: 16);
    }
    return data;
  }
}

// ==================== Result Classes ====================

class RsaKeyPairResult {
  final String publicKey;
  final String privateKey;

  RsaKeyPairResult({required this.publicKey, required this.privateKey});
}

class SessionKeyResult {
  final String key;
  final int round;

  SessionKeyResult({required this.key, required this.round});
}

class EncryptedMessageResult {
  final int round;
  final String data;

  EncryptedMessageResult({required this.round, required this.data});
}
