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

import 'package:Capsule/models/messages.dart';
import 'package:Capsule/utils/storage/write_file.dart';
import 'package:http/http.dart' as http;
import 'package:sqflite/sqflite.dart';
import 'package:uuid/uuid.dart';

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

Future<void> sendMessage(Conversation conversation, {
  String? data,
  List<File> files = const []
}) async {

  MyProfile profile = await MyProfile.getProfile();

  var uuid = const Uuid();

  ConversationUser currentUser = await getConversationUser(conversation, profile.id);

  List<Message> messages = [];
  List<Map<String, dynamic>> messagesToSend = [];

  final db = await getDatabaseConnection();

  if (data != null) {
    TextMessage message = TextMessage(
        id: uuid.v4(),
        symmetricKey: '',
        userSymmetricKey: '',
        senderId: currentUser.userId,
        senderUsername: profile.username,
        associationKey: currentUser.associationKey,
        createdAt: DateTime.now().toIso8601String(),
        failedToSend: false,
        text: data,
    );

    messages.add(message);
    messagesToSend.add(await message.payloadJson(
        conversation,
    ));

    await db.insert(
        'messages',
        message.toMap(),
        conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }

  for (File file in files) {

    String messageId = uuid.v4();

    File writtenFile = await writeImage(
      messageId,
      file.readAsBytesSync(),
    );

    ImageMessage message = ImageMessage(
        id: messageId,
        symmetricKey: '',
        userSymmetricKey: '',
        senderId: currentUser.userId,
        senderUsername: profile.username,
        associationKey: currentUser.associationKey,
        createdAt: DateTime.now().toIso8601String(),
        failedToSend: false,
        file: writtenFile,
    );

    messages.add(message);
    messagesToSend.add(await message.payloadJson(
        conversation,
    ));

    await db.insert(
        'messages',
        message.toMap(),
        conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }

  String sessionCookie = await getSessionCookie();

  return http.post(
    await MyProfile.getServerUrl('api/v1/auth/message'),
    headers: <String, String>{
      'Content-Type': 'application/json; charset=UTF-8',
      'cookie': sessionCookie,
    },
    body: jsonEncode(messagesToSend),
  )
  .then((resp) {
    if (resp.statusCode != 204) {
      throw Exception('Unable to send message');
    }
  })
  .catchError((exception) {
    for (Message message in messages) {
      message.failedToSend = true;
      db.update(
        'messages',
        message.toMap(),
        where: 'id = ?',
        whereArgs: [message.id],
      );
    }
    throw exception;
  });
}

Future<void> updateMessageThread(Conversation conversation, {MyProfile? profile}) async {
  profile ??= await MyProfile.getProfile();
  ConversationUser currentUser = await getConversationUser(conversation, profile.id);

  var resp = await http.get(
      await MyProfile.getServerUrl('api/v1/auth/messages/${currentUser.associationKey}'),
      headers: {
        'cookie': await getSessionCookie(),
      }
  );

  if (resp.statusCode != 200) {
    throw Exception(resp.body);
  }

  List<dynamic> messageThreadJson = jsonDecode(resp.body);

  final db = await getDatabaseConnection();

  for (var i = 0; i < messageThreadJson.length; i++) {
    var messageJson = messageThreadJson[i] as Map<String, dynamic>;

    var message =  messageJson['message_data']['attachment_id'] != null ?
      await ImageMessage.fromJson(
        messageJson,
        profile.privateKey!,
      ) :
      TextMessage.fromJson(
        messageJson,
        profile.privateKey!,
      );

    ConversationUser messageUser = await getConversationUser(conversation, message.senderId);
    message.senderUsername = messageUser.username;

    await db.insert(
        'messages',
        message.toMap(),
        conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }
}

Future<void> updateMessageThreads({List<Conversation>? conversations}) async {
  // try {
    MyProfile profile = await MyProfile.getProfile();

    conversations ??= await getConversations();

    for (var i = 0; i < conversations.length; i++) {
      await updateMessageThread(conversations[i], profile: profile);
    }
  // } catch(SocketException) {
  //   return;
  // }
}
