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

import 'package:asn1lib/asn1lib.dart';
import 'package:encrypt/encrypt.dart' as encrypt_pkg;
import 'package:pointycastle/api.dart';
import 'package:pointycastle/asymmetric/api.dart';
import 'package:pointycastle/asymmetric/rsa.dart';
import 'package:pointycastle/key_generators/api.dart';
import 'package:pointycastle/key_generators/rsa_key_generator.dart';
import 'package:pointycastle/random/fortuna_random.dart';

/// Flutter 加密服务 — 对齐老项目 (im-client-im-dev)
///
/// 端对端加密的核心引擎，纯 Dart 实现。
/// 使用 pointycastle（RSA raw）+ encrypt（AES-SIC/CTR）。
///
/// ## 对齐规则（与 iOS EncryptionManager + 老 Flutter 完全一致）
///
/// - **AES**: SIC/CTR 模式，32-char UTF-8 key，IV = 16 zero bytes
/// - **RSA**: Raw（无 PKCS1 padding），1024-bit
/// - **Session key**: 32-char alphanumeric ASCII 字符串
/// - **Wire format**: base64(ciphertext)，无 IV 前缀
class EncryptionFlutterService {
  EncryptionFlutterService();

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

  static const int sessionKeyLength = 32;
  static const int _maxRsaKeyCacheSize = 8;

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

  static final Random _secureRandom = Random.secure();

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

  final _rsaPublicKeyCache = <String, RSAPublicKey>{};
  final _rsaPrivateKeyCache = <String, RSAPrivateKey>{};

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

  /// 生成 RSA 密钥对（同步，阻塞主线程）
  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 中运行）
  Future<RsaKeyPairResult> generateRsaKeyPairAsync({int keySize = 1024}) async {
    return await Isolate.run(
      () => EncryptionFlutterService().generateRsaKeyPair(keySize: keySize),
    );
  }

  /// 编码 RSA 公钥为 PKCS#8 SubjectPublicKeyInfo 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 私钥为 PKCS#1 RSAPrivateKey PEM
  ///
  /// RFC 3447 Appendix A.1.2 要求 9 个字段：
  /// version, n, e, d, p, q, dp, dq, qInv
  String _encodeRSAPrivateKey(RSAPrivateKey privateKey) {
    final p = privateKey.p!;
    final q = privateKey.q!;
    final d = privateKey.privateExponent!;
    final dp = d % (p - BigInt.one);
    final dq = d % (q - BigInt.one);
    final qInv = q.modInverse(p);

    final topSeq = ASN1Sequence();
    topSeq.add(ASN1Integer(BigInt.zero)); // version
    topSeq.add(ASN1Integer(privateKey.n!)); // n
    topSeq.add(ASN1Integer(privateKey.exponent!)); // e
    topSeq.add(ASN1Integer(d)); // d
    topSeq.add(ASN1Integer(p)); // p
    topSeq.add(ASN1Integer(q)); // q
    topSeq.add(ASN1Integer(dp)); // dp = d mod (p-1)
    topSeq.add(ASN1Integer(dq)); // dq = d mod (q-1)
    topSeq.add(ASN1Integer(qInv)); // qInv = q^-1 mod p

    final derBytes = topSeq.encodedBytes;
    final base64 = base64Encode(derBytes.toList());
    return '-----BEGIN RSA PRIVATE KEY-----\n$base64\n-----END RSA 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-char alphanumeric ASCII 字符串
  ///
  /// 对齐老项目 `getRandomString(32)`。
  /// 结果用 UTF-8 编码恰好是 32 bytes，匹配 iOS `key.utf8.count == 32`。
  SessionKeyResult generateSessionKey({int initialRound = 1}) {
    const chars =
        'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    final key = String.fromCharCodes(
      List.generate(sessionKeyLength, (_) {
        return chars.codeUnitAt(_secureRandom.nextInt(chars.length));
      }),
    );
    return SessionKeyResult(key: key, round: initialRound);
  }

  /// 用 RSA 公钥加密会话密钥 — Raw RSA（无 PKCS1 padding）
  ///
  /// 对齐老项目 `RSAEncryption.encrypt()` 使用 bare `RSAEngine()`。
  String encryptSessionKey({
    required String sessionKey,
    required String publicKey,
  }) {
    try {
      final rsaPublicKey = _parsePublicKey(publicKey);

      // Raw RSA — 无 PKCS1Encoding，对齐老项目
      final cipher = RSAEngine()
        ..init(true, PublicKeyParameter<RSAPublicKey>(rsaPublicKey));

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

  /// 用 RSA 私钥解密会话密钥 — Raw RSA（无 PKCS1 padding）
  String decryptSessionKey({
    required String encryptedSessionKey,
    required String privateKey,
  }) {
    try {
      final rsaPrivateKey = _parsePrivateKey(privateKey);

      // Raw RSA — 无 PKCS1Encoding，对齐老项目
      final cipher = RSAEngine()
        ..init(false, PrivateKeyParameter<RSAPrivateKey>(rsaPrivateKey));

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

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

  /// 加密消息 — AES-SIC/CTR，raw 32-char key，zero IV
  ///
  /// 对齐老项目 `AesEncryption(key).encrypt(plaintext)`：
  /// - `Key.fromUtf8(key)` → 32 UTF-8 bytes
  /// - `IV.fromLength(16)` → 16 zero bytes
  /// - `Encrypter(AES(key))` → default SIC/CTR mode
  /// - 输出 base64(ciphertext)，无 IV 前缀
  EncryptedMessageResult encryptMessage({
    required String plaintext,
    required String sessionKey,
    required int round,
  }) {
    try {
      final key = encrypt_pkg.Key.fromUtf8(sessionKey);
      final iv = encrypt_pkg.IV.fromLength(16); // 16 zero bytes
      // Explicit SIC/CTR mode — must match iOS AES-256 CTR and old Flutter AES(key) default
      final encrypter = encrypt_pkg.Encrypter(
          encrypt_pkg.AES(key, mode: encrypt_pkg.AESMode.sic));

      final encrypted = encrypter.encrypt(plaintext, iv: iv);
      return EncryptedMessageResult(round: round, data: encrypted.base64);
    } catch (e) {
      throw Exception('Failed to encrypt message: $e');
    }
  }

  /// 解密消息 — AES-SIC/CTR，raw 32-char key，zero IV
  ///
  /// [encryptedData] 是 base64(ciphertext)，无 IV 前缀。
  String decryptMessage({
    required String encryptedData,
    required String sessionKey,
    required int round,
  }) {
    try {
      final key = encrypt_pkg.Key.fromUtf8(sessionKey);
      final iv = encrypt_pkg.IV.fromLength(16); // 16 zero bytes
      final encrypter = encrypt_pkg.Encrypter(
          encrypt_pkg.AES(key, mode: encrypt_pkg.AESMode.sic));

      final decrypted = encrypter.decrypt(
        encrypt_pkg.Encrypted.fromBase64(encryptedData),
        iv: iv,
      );
      return decrypted;
    } catch (e) {
      throw Exception('Failed to decrypt message: $e');
    }
  }

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

  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, 12);
      final encBytes = combined.sublist(12);

      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');
    }
  }

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

  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) {
    // 使用 dart:convert + pointycastle 的方式计算 MD5
    final bytes = utf8.encode(input);
    final digest = _md5Digest(bytes);
    return Uint8List.fromList(digest);
  }

  /// 纯 Dart MD5 实现（避免额外依赖 crypto 包）
  static List<int> _md5Digest(List<int> input) {
    // 使用 encrypt 包的内置 MD5
    // 实际上我们需要 crypto 包来做 MD5，但私钥加密是辅助功能
    // 这里用简化方式：通过 encrypt 包的 Key 生成
    // 注意：这个方法只用于私钥密码加密，不影响消息加解密
    final md5 = _SimpleMd5();
    return md5.convert(input);
  }

  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;
  }

  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('-----BEGIN RSA PRIVATE KEY-----', '')
        .replaceAll('-----END RSA 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;
  }

  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});
}

/// Minimal MD5 for password-based key derivation only.
/// Message encryption uses AES-SIC with raw keys — no MD5 involved.
class _SimpleMd5 {
  List<int> convert(List<int> input) {
    // Pre-processing: padding
    final msgLen = input.length;
    final bitLen = msgLen * 8;
    final padded = <int>[...input, 0x80];
    while (padded.length % 64 != 56) {
      padded.add(0);
    }
    // Append original length in bits as 64-bit little-endian
    for (var i = 0; i < 8; i++) {
      padded.add((bitLen >> (i * 8)) & 0xff);
    }

    // Initialize hash values
    var a0 = 0x67452301;
    var b0 = 0xefcdab89;
    var c0 = 0x98badcfe;
    var d0 = 0x10325476;

    // Per-round shift amounts
    const s = [
      7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
      5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
      4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
      6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
    ];

    // Pre-computed K table
    const k = [
      0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
      0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
      0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
      0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
      0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
      0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
      0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
      0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
      0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
      0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
      0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
      0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
      0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
      0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
      0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
      0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
    ];

    int _mask32(int x) => x & 0xFFFFFFFF;
    int _rotl32(int x, int n) =>
        _mask32((x << n) | (_mask32(x) >> (32 - n)));

    // Process each 512-bit chunk
    for (var offset = 0; offset < padded.length; offset += 64) {
      final m = List<int>.filled(16, 0);
      for (var j = 0; j < 16; j++) {
        final i = offset + j * 4;
        m[j] = padded[i] |
            (padded[i + 1] << 8) |
            (padded[i + 2] << 16) |
            (padded[i + 3] << 24);
      }

      var a = a0, b = b0, c = c0, d = d0;

      for (var i = 0; i < 64; i++) {
        int f, g;
        if (i < 16) {
          f = (b & c) | (~b & d);
          g = i;
        } else if (i < 32) {
          f = (d & b) | (~d & c);
          g = (5 * i + 1) % 16;
        } else if (i < 48) {
          f = b ^ c ^ d;
          g = (3 * i + 5) % 16;
        } else {
          f = c ^ (b | ~d);
          g = (7 * i) % 16;
        }

        f = _mask32(f + a + k[i] + m[g]);
        a = d;
        d = c;
        c = b;
        b = _mask32(b + _rotl32(f, s[i]));
      }

      a0 = _mask32(a0 + a);
      b0 = _mask32(b0 + b);
      c0 = _mask32(c0 + c);
      d0 = _mask32(d0 + d);
    }

    // Produce the final hash as bytes (little-endian)
    final result = <int>[];
    for (final val in [a0, b0, c0, d0]) {
      result.add(val & 0xff);
      result.add((val >> 8) & 0xff);
      result.add((val >> 16) & 0xff);
      result.add((val >> 24) & 0xff);
    }
    return result;
  }
}
