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

import 'package:Capsule/utils/encryption/aes_helper.dart';
import 'package:Capsule/utils/encryption/crypto_utils.dart';
import 'package:pointycastle/impl.dart';

import '/models/conversations.dart';
import '/utils/storage/database.dart';

Future<ConversationUser> getConversationUser(Conversation conversation, String userId) async {
  final db = await getDatabaseConnection();

  final List<Map<String, dynamic>> maps = await db.query(
    'conversation_users',
    where: 'conversation_id = ? AND user_id = ?',
    whereArgs: [ conversation.id, userId ],
  );

  if (maps.length != 1) {
    throw ArgumentError('Invalid conversation_id or username');
  }

  return ConversationUser(
    id: maps[0]['id'],
    userId: maps[0]['user_id'],
    conversationId: maps[0]['conversation_id'],
    username: maps[0]['username'],
    associationKey: maps[0]['association_key'],
    publicKey: CryptoUtils.rsaPublicKeyFromPem(maps[0]['asymmetric_public_key']),
    admin: maps[0]['admin'] == 1,
  );

}

// A method that retrieves all the dogs from the dogs table.
Future<List<ConversationUser>> getConversationUsers(Conversation conversation) async {
  final db = await getDatabaseConnection();

  final List<Map<String, dynamic>> maps = await db.query(
    'conversation_users',
    where: 'conversation_id = ?',
    whereArgs: [ conversation.id ],
    orderBy: 'username',
  );

  List<ConversationUser> conversationUsers = List.generate(maps.length, (i) {
    return ConversationUser(
      id: maps[i]['id'],
      userId: maps[i]['user_id'],
      conversationId: maps[i]['conversation_id'],
      username: maps[i]['username'],
      associationKey: maps[i]['association_key'],
      publicKey: CryptoUtils.rsaPublicKeyFromPem(maps[i]['asymmetric_public_key']),
      admin: maps[i]['admin'] == 1,
    );
  });

  int index = 0;
  List<ConversationUser> finalConversationUsers = [];

  for (ConversationUser conversationUser in conversationUsers) {
    if (!conversationUser.admin) {
      finalConversationUsers.add(conversationUser);
      continue;
    }

    finalConversationUsers.insert(index, conversationUser);
    index++;
  }

  return finalConversationUsers;
}

Future<List<Map<String, dynamic>>> getEncryptedConversationUsers(Conversation conversation, Uint8List symKey) async {
  final db = await getDatabaseConnection();

  final List<Map<String, dynamic>> maps = await db.query(
    'conversation_users',
    where: 'conversation_id = ?',
    whereArgs: [conversation.id],
    orderBy: 'username',
  );

  List<Map<String, dynamic>> conversationUsers = List.generate(maps.length, (i) {
    return {
      'id': maps[i]['id'],
      'conversation_id': maps[i]['conversation_id'],
      'user_id': AesHelper.aesEncrypt(symKey, Uint8List.fromList(maps[i]['user_id'].codeUnits)),
      'username': AesHelper.aesEncrypt(symKey, Uint8List.fromList(maps[i]['username'].codeUnits)),
      'association_key': AesHelper.aesEncrypt(symKey, Uint8List.fromList(maps[i]['association_key'].codeUnits)),
      'public_key': AesHelper.aesEncrypt(symKey, Uint8List.fromList(maps[i]['asymmetric_public_key'].codeUnits)),
      'admin': AesHelper.aesEncrypt(symKey, Uint8List.fromList((maps[i]['admin'] == 1 ? 'true' : 'false').codeUnits)),
    };
  });

  return conversationUsers;
}

class ConversationUser{
  String id;
  String userId;
  String conversationId;
  String username;
  String associationKey;
  RSAPublicKey publicKey;
  bool admin;
  ConversationUser({
    required this.id,
    required this.userId,
    required this.conversationId,
    required this.username,
    required this.associationKey,
    required this.publicKey,
    required this.admin,
  });

  factory ConversationUser.fromJson(Map<String, dynamic> json, Uint8List symmetricKey) {

    String userId = AesHelper.aesDecrypt(
      symmetricKey,
      base64.decode(json['user_id']),
    );

    String username = AesHelper.aesDecrypt(
      symmetricKey,
      base64.decode(json['username']),
    );

    String associationKey = AesHelper.aesDecrypt(
      symmetricKey,
      base64.decode(json['association_key']),
    );

    String admin = AesHelper.aesDecrypt(
      symmetricKey,
      base64.decode(json['admin']),
    );

    String publicKeyString = AesHelper.aesDecrypt(
      symmetricKey,
      base64.decode(json['public_key']),
    );

    RSAPublicKey publicKey = CryptoUtils.rsaPublicKeyFromPem(publicKeyString);

    return ConversationUser(
      id: json['id'],
      conversationId: json['conversation_detail_id'],
      userId: userId,
      username: username,
      associationKey: associationKey,
      publicKey: publicKey,
      admin: admin == 'true',
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'user_id': userId,
      'username': username,
      'association_key': associationKey,
      'asymmetric_public_key': publicKeyPem(),
      'admin': admin ? 'true' : 'false',
    };
  }

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'user_id': userId,
      'conversation_id': conversationId,
      'username': username,
      'association_key': associationKey,
      'asymmetric_public_key': publicKeyPem(),
      'admin': admin ? 1 : 0,
    };
  }

  String publicKeyPem() {
    return CryptoUtils.encodeRSAPublicKeyToPem(publicKey);
  }
}
