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

import 'package:pointycastle/pointycastle.dart';
import 'package:uuid/uuid.dart';

import '/models/image_message.dart';
import '/models/conversation_users.dart';
import '/models/my_profile.dart';
import '/models/text_messages.dart';
import '/models/conversations.dart';
import '/utils/encryption/crypto_utils.dart';
import '/utils/storage/database.dart';

const messageTypeReceiver = 'receiver';
const messageTypeSender = 'sender';

Future<List<Message>> getMessagesForThread(Conversation conversation) async {
  final db = await getDatabaseConnection();

  final List<Map<String, dynamic>> maps = await db.rawQuery(
    '''
    SELECT * FROM messages WHERE association_key IN (
      SELECT association_key FROM conversation_users WHERE conversation_id = ?
    )
    ORDER BY created_at DESC;
    ''',
    [conversation.id]
  );

  return List.generate(maps.length, (i) {
    if (maps[i]['data'] == null) {

      File file = File(maps[i]['file']);

      return ImageMessage(
        id: maps[i]['id'],
        symmetricKey: maps[i]['symmetric_key'],
        userSymmetricKey: maps[i]['user_symmetric_key'],
        file: file,
        senderId: maps[i]['sender_id'],
        senderUsername: maps[i]['sender_username'],
        associationKey: maps[i]['association_key'],
        createdAt: maps[i]['created_at'],
        failedToSend: maps[i]['failed_to_send'] == 1,
      );
    }

    return TextMessage(
      id: maps[i]['id'],
      symmetricKey: maps[i]['symmetric_key'],
      userSymmetricKey: maps[i]['user_symmetric_key'],
      text: maps[i]['data'],
      senderId: maps[i]['sender_id'],
      senderUsername: maps[i]['sender_username'],
      associationKey: maps[i]['association_key'],
      createdAt: maps[i]['created_at'],
      failedToSend: maps[i]['failed_to_send'] == 1,
    );
  });
}

class Message {
  String id;
  String symmetricKey;
  String userSymmetricKey;
  String senderId;
  String senderUsername;
  String associationKey;
  String createdAt;
  bool failedToSend;

  Message({
    required this.id,
    required this.symmetricKey,
    required this.userSymmetricKey,
    required this.senderId,
    required this.senderUsername,
    required this.associationKey,
    required this.createdAt,
    required this.failedToSend,
  });

  Future<List<Map<String, String>>> payloadJsonBase(
    Uint8List symmetricKey,
    Uint8List userSymmetricKey,
    Conversation conversation,
    String messageId,
    String messageDataId,
  ) async {
    MyProfile profile = await MyProfile.getProfile();
    if (profile.publicKey == null) {
      throw Exception('Could not get profile.publicKey');
    }

    RSAPublicKey publicKey = profile.publicKey!;

    List<Map<String, String>> messages = [];
    List<ConversationUser> conversationUsers = await getConversationUsers(conversation);

    for (var i = 0; i < conversationUsers.length; i++) {
      ConversationUser user = conversationUsers[i];

      if (profile.id == user.userId) {
        messages.add({
          'id': messageId,
          'message_data_id': messageDataId,
          'symmetric_key': base64.encode(CryptoUtils.rsaEncrypt(
            userSymmetricKey,
            publicKey,
          )),
          'association_key': user.associationKey,
        });

        continue;
      }

      ConversationUser conversationUser = await getConversationUser(conversation, user.userId);
      RSAPublicKey friendPublicKey = conversationUser.publicKey;

      messages.add({
        'message_data_id': messageDataId,
        'symmetric_key': base64.encode(CryptoUtils.rsaEncrypt(
          userSymmetricKey,
          friendPublicKey,
        )),
        'association_key': user.associationKey,
      });
    }

    return messages;
  }

  String getContent() {
    return '';
  }

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'symmetric_key': symmetricKey,
      'user_symmetric_key': userSymmetricKey,
      'sender_id': senderId,
      'sender_username': senderUsername,
      'association_key': associationKey,
      'created_at': createdAt,
      'failed_to_send': failedToSend ? 1 : 0,
    };
  }
}
