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

class SM3 {
  static const List<int> _iv = [
    0x7380166F,
    0x4914B2B9,
    0x172442D7,
    0xDA8A0600,
    0xA96F30BC,
    0x163138AA,
    0xE38DEE4D,
    0xB0FB0E4E
  ];

  static int _rotl(int x, int n) {
    assert(n >= 0 && n < 32);
    n = n & 31; // 保证 n 在 0~31 之间
    return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF;
  }

  static int _p0(int x) => x ^ _rotl(x, 9) ^ _rotl(x, 17);
  static int _p1(int x) => x ^ _rotl(x, 15) ^ _rotl(x, 23);

  static int _ff(int x, int y, int z, int j) =>
      (j < 16) ? (x ^ y ^ z) : ((x & y) | (x & z) | (y & z));
  static int _gg(int x, int y, int z, int j) =>
      (j < 16) ? (x ^ y ^ z) : ((x & y) | (~x & z));

  static List<int> digest(Uint8List data) {
    int len = data.length * 8;
    List<int> msg = List.from(data);
    msg.add(0x80);
    while ((msg.length * 8) % 512 != 448) {
      msg.add(0x00);
    }
    for (int i = 7; i >= 0; i--) {
      msg.add((len >> (i * 8)) & 0xFF);
    }

    List<int> v = List.from(_iv);

    for (int i = 0; i < msg.length; i += 64) {
      List<int> b = msg.sublist(i, i + 64);
      List<int> w = List.filled(68, 0);
      List<int> w1 = List.filled(64, 0);

      for (int j = 0; j < 16; j++) {
        w[j] = (b[j * 4] << 24) |
            (b[j * 4 + 1] << 16) |
            (b[j * 4 + 2] << 8) |
            (b[j * 4 + 3]);
      }
      for (int j = 16; j < 68; j++) {
        int rotl3 = _rotl(w[j - 3], 15);
        int rotl13 = _rotl(w[j - 13], 7);
        w[j] = _p1(w[j - 16] ^ w[j - 9] ^ rotl3) ^ rotl13 ^ w[j - 6];
        w[j] &= 0xFFFFFFFF;
      }
      for (int j = 0; j < 64; j++) {
        w1[j] = w[j] ^ w[j + 4];
      }

      int a = v[0], b_ = v[1], c = v[2], d = v[3];
      int e = v[4], f = v[5], g = v[6], h = v[7];

      for (int j = 0; j < 64; j++) {
        int tj = (j < 16) ? 0x79CC4519 : 0x7A879D8A;
        int rotlA12 = _rotl(a, 12);
        int rotlTj = _rotl(tj, j & 0x1F); // 保证位移参数在0~31
        int ss1 = _rotl((rotlA12 + e + rotlTj) & 0xFFFFFFFF, 7);
        int ss2 = ss1 ^ rotlA12;
        int tt1 = (_ff(a, b_, c, j) + d + ss2 + w1[j]) & 0xFFFFFFFF;
        int tt2 = (_gg(e, f, g, j) + h + ss1 + w[j]) & 0xFFFFFFFF;
        d = c;
        c = _rotl(b_, 9);
        b_ = a;
        a = tt1;
        h = g;
        g = _rotl(f, 19);
        f = e;
        e = _p0(tt2);
      }

      v[0] ^= a;
      v[1] ^= b_;
      v[2] ^= c;
      v[3] ^= d;
      v[4] ^= e;
      v[5] ^= f;
      v[6] ^= g;
      v[7] ^= h;
    }

    List<int> out = [];
    for (int i = 0; i < v.length; i++) {
      out.addAll([
        (v[i] >> 24) & 0xFF,
        (v[i] >> 16) & 0xFF,
        (v[i] >> 8) & 0xFF,
        v[i] & 0xFF
      ]);
    }
    return out;
  }
}

// SM2椭圆曲线参数
class SM2Curve {
  static final BigInt p = BigInt.parse(
      'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF',
      radix: 16);
  static final BigInt a = BigInt.parse(
      'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC',
      radix: 16);
  static final BigInt b = BigInt.parse(
      '28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93',
      radix: 16);
  static final BigInt n = BigInt.parse(
      'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B61C6B3A2F4F6B7E4B8E5F5FF',
      radix: 16);
  static final BigInt gx = BigInt.parse(
      '32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7',
      radix: 16);
  static final BigInt gy = BigInt.parse(
      'BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0',
      radix: 16);
}

// 椭圆曲线点
class ECPoint {
  BigInt? x;
  BigInt? y;
  bool isInfinity;

  ECPoint(this.x, this.y) : isInfinity = false;
  ECPoint.infinity()
      : isInfinity = true,
        x = null,
        y = null;

  @override
  String toString() {
    if (isInfinity) return 'O';
    return '(${x!.toRadixString(16)}, ${y!.toRadixString(16)})';
  }
}

// SM2加解密类
class SM2Crypto {
  static final Random _random = Random.secure();

  // 模运算
  static BigInt _mod(BigInt a, BigInt m) {
    BigInt result = a % m;
    return result < BigInt.zero ? result + m : result;
  }

  // 模逆运算
  static BigInt _modInverse(BigInt a, BigInt m) {
    if (a < BigInt.zero) a = _mod(a, m);

    BigInt g = _gcd(a, m);
    if (g != BigInt.one) {
      throw Exception('Modular inverse does not exist');
    }

    return _mod(_extendedGcd(a, m)[0], m);
  }

  // 扩展欧几里德算法
  static List<BigInt> _extendedGcd(BigInt a, BigInt b) {
    if (a == BigInt.zero) {
      return [BigInt.zero, BigInt.one, b];
    }

    List<BigInt> result = _extendedGcd(b % a, a);
    BigInt x1 = result[0];
    BigInt y1 = result[1];
    BigInt gcd = result[2];

    BigInt x = y1 - (b ~/ a) * x1;
    BigInt y = x1;

    return [x, y, gcd];
  }

  // 最大公约数
  static BigInt _gcd(BigInt a, BigInt b) {
    while (b != BigInt.zero) {
      BigInt temp = b;
      b = a % b;
      a = temp;
    }
    return a;
  }

  // 椭圆曲线点加法
  static ECPoint _pointAdd(ECPoint p1, ECPoint p2) {
    if (p1.isInfinity) return p2;
    if (p2.isInfinity) return p1;

    if (p1.x == p2.x) {
      if (p1.y == p2.y) {
        return _pointDouble(p1);
      } else {
        return ECPoint.infinity();
      }
    }

    BigInt dx = _mod(p2.x! - p1.x!, SM2Curve.p);
    BigInt dy = _mod(p2.y! - p1.y!, SM2Curve.p);
    BigInt s = _mod(dy * _modInverse(dx, SM2Curve.p), SM2Curve.p);

    BigInt x3 = _mod(s * s - p1.x! - p2.x!, SM2Curve.p);
    BigInt y3 = _mod(s * (p1.x! - x3) - p1.y!, SM2Curve.p);

    return ECPoint(x3, y3);
  }

  // 椭圆曲线点倍乘
  static ECPoint _pointDouble(ECPoint p) {
    if (p.isInfinity) return p;

    BigInt s = _mod(
        (BigInt.from(3) * p.x! * p.x! + SM2Curve.a) *
            _modInverse(BigInt.from(2) * p.y!, SM2Curve.p),
        SM2Curve.p);

    BigInt x3 = _mod(s * s - BigInt.from(2) * p.x!, SM2Curve.p);
    BigInt y3 = _mod(s * (p.x! - x3) - p.y!, SM2Curve.p);

    return ECPoint(x3, y3);
  }

  // 椭圆曲线标量乘法
  static ECPoint _pointMultiply(BigInt k, ECPoint point) {
    if (k == BigInt.zero || point.isInfinity) {
      return ECPoint.infinity();
    }

    ECPoint result = ECPoint.infinity();
    ECPoint addend = point;

    while (k > BigInt.zero) {
      if (k.isOdd) {
        result = _pointAdd(result, addend);
      }
      addend = _pointDouble(addend);
      k = k >> 1;
    }

    return result;
  }

  // 生成随机数
  static BigInt _generateRandomBigInt(BigInt max) {
    int byteLength = (max.bitLength + 7) ~/ 8;
    BigInt randomNumber;

    do {
      List<int> bytes = List.generate(byteLength, (_) => _random.nextInt(256));
      randomNumber = BigInt.parse(
          bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join(),
          radix: 16);
    } while (randomNumber >= max || randomNumber == BigInt.zero);

    return randomNumber;
  }

  // KDF密钥派生函数
  static Uint8List _kdf(Uint8List z, int klen) {
    List<int> result = [];
    int ct = 1;

    while (result.length < klen) {
      List<int> input = [
        ...z,
        ...[(ct >> 24) & 0xFF, (ct >> 16) & 0xFF, (ct >> 8) & 0xFF, ct & 0xFF]
      ];
      List<int> hash = _sm3Hash(Uint8List.fromList(input));
      result.addAll(hash);
      ct++;
    }

    return Uint8List.fromList(result.take(klen).toList());
  }

  // SM3哈希函数（简化版本）
  static List<int> _sm3Hash(Uint8List data) {
    return SM3.digest(data);
  }

  // 字节数组转换为十六进制字符串
  static String _bytesToHex(Uint8List bytes) {
    return bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
  }

  // 十六进制字符串转换为字节数组
  static Uint8List _hexToBytes(String hex) {
    if (hex.length % 2 != 0) hex = '0' + hex;
    List<int> bytes = [];
    for (int i = 0; i < hex.length; i += 2) {
      bytes.add(int.parse(hex.substring(i, i + 2), radix: 16));
    }
    return Uint8List.fromList(bytes);
  }

  // BigInt转换为定长字节数组
  static Uint8List _bigIntToBytes(BigInt value, int length) {
    String hex = value.toRadixString(16).padLeft(length * 2, '0');
    return _hexToBytes(hex);
  }

  // 公钥压缩（与gm-crypto保持一致）
  static String compressPublicKey(String publicKey) {
    if (publicKey.length == 66) return publicKey; // 已经是压缩格式

    if (publicKey.startsWith('04')) {
      publicKey = publicKey.substring(2);
    }

    String x = publicKey.substring(0, 64);
    String y = publicKey.substring(64, 128);

    BigInt yBig = BigInt.parse(y, radix: 16);
    String prefix = yBig.isEven ? '02' : '03';

    return prefix + x;
  }

  // 公钥解压缩
  static String decompressPublicKey(String compressedKey) {
    if (compressedKey.length == 130) return compressedKey;
    if (compressedKey.length != 66) throw Exception('压缩公钥长度非法'); // 已经是非压缩格式

    String prefix = compressedKey.substring(0, 2);
    String x = compressedKey.substring(2);

    BigInt xBig = BigInt.parse(x, radix: 16);

    // 计算y²
    BigInt ySquared =
        _mod(xBig * xBig * xBig + SM2Curve.a * xBig + SM2Curve.b, SM2Curve.p);

    // 计算y
    BigInt y = _modSqrt(ySquared, SM2Curve.p);

    // 根据前缀选择正确的y值
    if ((prefix == '02' && y.isOdd) || (prefix == '03' && y.isEven)) {
      y = SM2Curve.p - y;
    }

    return '04' + x + y.toRadixString(16).padLeft(64, '0');
  }

  // 模平方根
  static BigInt _modSqrt(BigInt a, BigInt p) {
    // 使用Tonelli-Shanks算法或简化版本
    return a.modPow((p + BigInt.one) ~/ BigInt.from(4), p);
  }

  // SM2加密（支持C1C2C3和C1C3C2两种模式）
  static String encrypt(String message, String publicKey,
      {bool c1c3c2Mode = true}) {
    // 确保公钥是完整格式
    String fullPublicKey = decompressPublicKey(publicKey);

    if (fullPublicKey.startsWith('04')) {
      fullPublicKey = fullPublicKey.substring(2);
    }

    String xHex = fullPublicKey.substring(0, 64);
    String yHex = fullPublicKey.substring(64, 128);

    BigInt px = BigInt.parse(xHex, radix: 16);
    BigInt py = BigInt.parse(yHex, radix: 16);
    ECPoint publicKeyPoint = ECPoint(px, py);

    Uint8List messageBytes = utf8.encode(message);

    while (true) {
      // 生成随机数k
      BigInt k = _generateRandomBigInt(SM2Curve.n);

      // 计算C1 = k*G
      ECPoint c1Point = _pointMultiply(k, ECPoint(SM2Curve.gx, SM2Curve.gy));

      // 计算k*P
      ECPoint kPPoint = _pointMultiply(k, publicKeyPoint);

      // 计算x2||y2
      Uint8List x2Bytes = _bigIntToBytes(kPPoint.x!, 32);
      Uint8List y2Bytes = _bigIntToBytes(kPPoint.y!, 32);
      Uint8List x2y2 = Uint8List.fromList([...x2Bytes, ...y2Bytes]);

      // KDF生成密钥流
      Uint8List keyStream = _kdf(x2y2, messageBytes.length);

      // 检查密钥流是否全零
      bool allZero = keyStream.every((b) => b == 0);
      if (allZero) continue;

      // 计算C2 = M ⊕ t
      Uint8List c2 = Uint8List.fromList(List.generate(
          messageBytes.length, (i) => messageBytes[i] ^ keyStream[i]));

      // 计算C3 = Hash(x2||M||y2)
      Uint8List hashInput =
          Uint8List.fromList([...x2Bytes, ...messageBytes, ...y2Bytes]);
      List<int> c3 = _sm3Hash(hashInput);

      // 组装密文
      Uint8List c1x = _bigIntToBytes(c1Point.x!, 32);
      Uint8List c1y = _bigIntToBytes(c1Point.y!, 32);

      String result;
      if (c1c3c2Mode) {
        // C1||C3||C2模式（新标准，默认）
        result = '04' +
            _bytesToHex(c1x) +
            _bytesToHex(c1y) +
            _bytesToHex(Uint8List.fromList(c3)) +
            _bytesToHex(c2);
      } else {
        // C1||C2||C3模式（旧标准）
        result = '04' +
            _bytesToHex(c1x) +
            _bytesToHex(c1y) +
            _bytesToHex(c2) +
            _bytesToHex(Uint8List.fromList(c3));
      }

      return result;
    }
  }

  // SM2解密（自动检测C1C2C3和C1C3C2模式）
  static String decrypt(String ciphertext, String privateKey,
      {bool? c1c3c2Mode}) {
    // 解析密文
    if (ciphertext.startsWith('04')) {
      ciphertext = ciphertext.substring(2);
    }

    // C1点坐标（64字节）
    String c1x = ciphertext.substring(0, 64);
    String c1y = ciphertext.substring(64, 128);

    // 构造C1点
    BigInt c1xBig = BigInt.parse(c1x, radix: 16);
    BigInt c1yBig = BigInt.parse(c1y, radix: 16);
    ECPoint c1Point = ECPoint(c1xBig, c1yBig);

    // 私钥
    BigInt d = BigInt.parse(privateKey, radix: 16);

    // 计算d*C1
    ECPoint dC1Point = _pointMultiply(d, c1Point);

    // 计算x2||y2
    Uint8List x2Bytes = _bigIntToBytes(dC1Point.x!, 32);
    Uint8List y2Bytes = _bigIntToBytes(dC1Point.y!, 32);
    Uint8List x2y2 = Uint8List.fromList([...x2Bytes, ...y2Bytes]);

    String remainingCiphertext = ciphertext.substring(128);

    // 如果没有指定模式，先尝试C1C3C2模式，失败后尝试C1C2C3模式
    if (c1c3c2Mode == null) {
      try {
        return _decryptWithMode(
            remainingCiphertext, x2Bytes, y2Bytes, x2y2, true);
      } catch (e) {
        try {
          return _decryptWithMode(
              remainingCiphertext, x2Bytes, y2Bytes, x2y2, false);
        } catch (e2) {
          throw Exception(
              'Decryption failed with both C1C3C2 and C1C2C3 modes');
        }
      }
    } else {
      return _decryptWithMode(
          remainingCiphertext, x2Bytes, y2Bytes, x2y2, c1c3c2Mode);
    }
  }

  // 辅助解密函数
  static String _decryptWithMode(String remainingCiphertext, Uint8List x2Bytes,
      Uint8List y2Bytes, Uint8List x2y2, bool c1c3c2Mode) {
    String c2, c3;

    if (c1c3c2Mode) {
      // C1C3C2模式：C3哈希值（64字节），然后是C2密文
      c3 = remainingCiphertext.substring(0, 64);
      c2 = remainingCiphertext.substring(64);
    } else {
      // C1C2C3模式：先是C2密文，最后64字节是C3哈希值
      c2 = remainingCiphertext.substring(0, remainingCiphertext.length - 64);
      c3 = remainingCiphertext.substring(remainingCiphertext.length - 64);
    }

    // 解析C2
    Uint8List c2Bytes = _hexToBytes(c2);

    // KDF生成密钥流
    Uint8List keyStream = _kdf(x2y2, c2Bytes.length);

    // 解密得到明文
    Uint8List messageBytes = Uint8List.fromList(
        List.generate(c2Bytes.length, (i) => c2Bytes[i] ^ keyStream[i]));

    // 验证C3
    Uint8List hashInput =
        Uint8List.fromList([...x2Bytes, ...messageBytes, ...y2Bytes]);
    List<int> computedC3 = _sm3Hash(hashInput);
    String computedC3Hex = _bytesToHex(Uint8List.fromList(computedC3));

    if (computedC3Hex != c3) {
      throw Exception('Hash verification failed');
    }

    return utf8.decode(messageBytes);
  }

  // 生成密钥对
  static Map<String, String> generateKeyPair() {
    BigInt privateKey = _generateRandomBigInt(SM2Curve.n);
    ECPoint publicKeyPoint =
        _pointMultiply(privateKey, ECPoint(SM2Curve.gx, SM2Curve.gy));

    String privateKeyHex = privateKey.toRadixString(16).padLeft(64, '0');
    String publicKeyHex = '04' +
        publicKeyPoint.x!.toRadixString(16).padLeft(64, '0') +
        publicKeyPoint.y!.toRadixString(16).padLeft(64, '0');

    return {
      'privateKey': privateKeyHex,
      'publicKey': publicKeyHex,
      'compressedPublicKey': compressPublicKey(publicKeyHex),
    };
  }
}

// 使用示例
// void main() {
//   // 生成密钥对
//   Map<String, String> keyPair = SM2Crypto.generateKeyPair();
//   print('Private Key: ${keyPair['privateKey']}');
//   print('Public Key: ${keyPair['publicKey']}');
//   print('Compressed Public Key: ${keyPair['compressedPublicKey']}');
  
//   String message = 'Hello SM2!';
  
//   // 测试C1C3C2模式（新标准，默认）
//   print('\n=== C1C3C2模式测试 ===');
//   String ciphertext1 = SM2Crypto.encrypt(message, keyPair['publicKey']!, c1c3c2Mode: true);
//   print('C1C3C2 Ciphertext: $ciphertext1');
//   String decrypted1 = SM2Crypto.decrypt(ciphertext1, keyPair['privateKey']!);
//   print('C1C3C2 Decrypted: $decrypted1');
  
//   // 测试C1C2C3模式（旧标准）
//   print('\n=== C1C2C3模式测试 ===');
//   String ciphertext2 = SM2Crypto.encrypt(message, keyPair['publicKey']!, c1c3c2Mode: false);
//   print('C1C2C3 Ciphertext: $ciphertext2');
//   String decrypted2 = SM2Crypto.decrypt(ciphertext2, keyPair['privateKey']!);
//   print('C1C2C3 Decrypted: $decrypted2');
  
//   // 测试自动模式检测
//   print('\n=== 自动模式检测测试 ===');
//   String autoDecrypted1 = SM2Crypto.decrypt(ciphertext1, keyPair['privateKey']!);
//   String autoDecrypted2 = SM2Crypto.decrypt(ciphertext2, keyPair['privateKey']!);
//   print('Auto detect C1C3C2: $autoDecrypted1');
//   print('Auto detect C1C2C3: $autoDecrypted2');
  
//   // 测试与压缩公钥的兼容性
//   print('\n=== 压缩公钥兼容性测试 ===');
//   String ciphertext3 = SM2Crypto.encrypt(message, keyPair['compressedPublicKey']!);
//   String decrypted3 = SM2Crypto.decrypt(ciphertext3, keyPair['privateKey']!);
//   print('Compressed key test: $decrypted3');
// }
