

import 'dart:convert';
import 'dart:ffi';
import 'dart:io';
import 'dart:math';

import 'package:ffi/ffi.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';
import 'package:prime_chat/screens/ChatScreen.dart';
import 'package:tuple/tuple.dart';

import 'Encrypt.dart';

void testPrint(bool x, String name) {
  print("test $name: $x");
}

Uint8List randomVector(int length) {
  final random = Random.secure();
  final values = List<int>.generate(length, (i) => random.nextInt(255));
  return Uint8List.fromList(values);
}

void testSM2(Pointer<SM2Key> key) {
  const plaintext = "Hello, GmSSL!";
  var plain = Uint8List.fromList(plaintext.codeUnits);
  plain = randomVector(255);
  try {
    final encrypted = sm2Encrypt(key, plain);
    final decrypted = sm2Decrypt(key, encrypted);
    testPrint(listEquals(decrypted, plain), "sm2 encrypt & decrypt");
  } on Error catch(e) {
    debugPrint(e.toString());
    return;
  }

}

Future<void> testSM2PrivateKeyPem(Pointer<SM2Key> key) async {

  final pass = "123456".toNativeUtf8();
  final private = key.privateKey.list;
  final tempDirPath = (await getTemporaryDirectory()).path;
  final path = "$tempDirPath/private.pem";
  sm2PrivateKeyInfoEncryptToPEM(key, pass, path);
  sm2PrivateKeyInfoDecryptFromPEM(key, pass, path);
  testPrint(listEquals(private, key.privateKey.list), "private key <-> PEM");
}

void testSM2PublicKeyDER(Pointer<SM2Key> key) {
  final public = Tuple2(key.publicKey.x.list, key.publicKey.y.list);
  try {
    final der = sm2PublicKeyInfoToDER(key);
    debugPrint("der.length = ${der.length}");
    debugPrint("der = $der");
    final encoded = base64Encode(der);
    debugPrint("pem = $encoded");
    final res = sm2PublicKeyInfoFromDER(key, der);
    // assert(res>=0, "sm2PublicKeyInfoFromDER error");
    if (res != 1) {
      debugPrint("sm2PublicKeyInfoFromDER error! res == $res");
    }
  } on Error catch(e) {
    debugPrint(e.toString());
    return;
  }
  testPrint(listEquals(public.item1, key.publicKey.x.list) &&
      listEquals(public.item2, key.publicKey.y.list), "public key <-> DER");
}

void testSM4() {
  final plain = randomVector(1<<16);
  // debugPrint("plain=");
  // debugPrint(base64Encode(plain));
  final sm4key = randomVector(16);
  final iv = randomVector(16);
  final sm4enc = sm4cbcEncrypt(plain, sm4key, iv);
  // debugPrint("sm4enc=");
  // debugPrint(base64Encode(sm4enc));
  final sm4dec = sm4cbcDecrypt(sm4enc, sm4key, iv);
  // debugPrint("sm4dec=");
  // debugPrint(base64Encode(sm4dec));
  testPrint(listEquals(plain, sm4dec), "sm4 encrypt & decrypt");
}

void testPemToDerToKey(Pointer<SM2Key> key, String tempPath) {

  final public = Tuple2(key.publicKey.x.list, key.publicKey.y.list);
  final private = key.privateKey.list;
  assert(sm2PublicKeyInfoToPEM(key, tempPath) == 1);
  File pemFile = File(tempPath);
  final lines = pemFile.readAsLinesSync();
  lines.remove("-----BEGIN PUBLIC KEY-----");
  lines.remove("-----END PUBLIC KEY-----");
  final content = lines.join();
  debugPrint("pem=$content");
  final der = base64Decode(content);
  debugPrint("der.length=${der.length} der=$der");
  assert(sm2PublicKeyInfoFromDER(key, der)==1);
  testPrint(listEquals(public.item1, key.publicKey.x.list) &&
      listEquals(public.item2, key.publicKey.y.list), "der -> publicKey");
  final equals = listEquals(private, key.privateKey.list);
  debugPrint("privateKey equals : $equals");
}


void encryptTest() async {
  final tempDirPath = (await getTemporaryDirectory()).path;
  final errorPath = "$tempDirPath/error.txt";
  final tempPath = "$tempDirPath/temp.pem";
  final pf = freopen(errorPath, "w", stderrAddress);
  debugPrint("==========encrypt test start===========");
  try {
    // int s = add(1, 5);
    // debugPrint("test libadd sum = $s");
    final key = gm.newSM2Key();
    key.generate();

    testPemToDerToKey(key, tempPath);

    final key1 = gm.newSM2Key();

    testSM2(key);
    // testSM2(key);
    // testSM2(key);
    // testSM2(key);
    await testSM2PrivateKeyPem(key);
    testSM2(key);
    testSM2PublicKeyDER(key);
    // testSM2PublicKeyDER(key);
    // testSM2PublicKeyDER(key);
    // testSM2PublicKeyDER(key);
    // testSM2(key);
    // testSM2(key);
    // testSM2(key);
    // testSM2(key);

    // TODO: ERROR sm2PublicKeyInfoToDER

    // final private = sm2PrivateKeyInfoDecryptFromPEMString(key, pass);
    // final publicPem = await sm2PublicKeyInfoToPEMString(key);
    // debugPrint(privatePem);
    // debugPrint(publicPem);

    testSM4();


  } on Error catch (e) {
    debugPrint(e.toString());
  }
  debugPrint("==========encrypt test end===========");
  gm.fclose(pf);
  File errorFile = File(errorPath);
  final errorInfo = errorFile.readAsStringSync();
  debugPrint("error info = ");
  debugPrint(errorInfo);

}
