import 'dart:convert';

import 'package:Capsule/exceptions/update_data_exception.dart';
import 'package:Capsule/utils/storage/get_file.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:http/http.dart' as http;
import 'package:pointycastle/export.dart';
import 'package:sqflite/sqflite.dart';

import '/components/flash_message.dart';
import '/models/conversation_users.dart';
import '/models/conversations.dart';
import '/models/my_profile.dart';
import '/utils/encryption/aes_helper.dart';
import '/utils/storage/database.dart';
import '/utils/storage/session_cookie.dart';

Future<void> updateConversation(
  Conversation conversation,
  {
    includeUsers = false,
    updatedImage = false,
  } ) async {
  String sessionCookie = await getSessionCookie();

  Map<String, dynamic> conversationJson = await conversation.payloadJson(includeUsers: includeUsers);

  var resp = await http.put(
    await MyProfile.getServerUrl('api/v1/auth/conversations'),
    headers: <String, String>{
      'Content-Type': 'application/json; charset=UTF-8',
      'cookie': sessionCookie,
    },
    body: jsonEncode(conversationJson),
  );

  if (resp.statusCode != 204) {
    throw UpdateDataException('Unable to update conversation, please try again later.');
  }

  if (!updatedImage) {
    return;
  }

  Map<String, dynamic> attachmentJson = conversation.payloadImageJson();

  resp = await http.post(
    await MyProfile.getServerUrl('api/v1/auth/conversations/${conversation.id}/image'),
    headers: <String, String>{
      'Content-Type': 'application/json; charset=UTF-8',
      'cookie': sessionCookie,
    },
    body: jsonEncode(attachmentJson),
  );

  if (resp.statusCode != 204) {
    throw UpdateDataException('Unable to update conversation image, please try again later.');
  }
}

// TODO: Refactor this function
Future<void> updateConversations() async {
  RSAPrivateKey privKey = await MyProfile.getPrivateKey();

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

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

    List<Conversation> conversations = [];
    List<String> conversationsDetailIds = [];

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

    if (conversationsJson.isEmpty) {
      return;
    }

    for (var i = 0; i < conversationsJson.length; i++) {
      Conversation conversation = Conversation.fromJson(
        conversationsJson[i] as Map<String, dynamic>,
        privKey,
      );
      conversations.add(conversation);
      conversationsDetailIds.add(conversation.id);
    }

    Map<String, String> params = {};
    params['conversation_detail_ids'] = conversationsDetailIds.join(',');
    var uri = await MyProfile.getServerUrl('api/v1/auth/conversation_details');
    uri = uri.replace(queryParameters: params);

    resp = await http.get(
      uri,
      headers: {
        'cookie': await getSessionCookie(),
      }
    );

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

    final db = await getDatabaseConnection();

    List<dynamic> conversationsDetailsJson = jsonDecode(resp.body);
    for (var i = 0; i < conversationsDetailsJson.length; i++) {
      var conversationDetailJson = conversationsDetailsJson[i] as Map<String, dynamic>;
      var conversation = findConversationByDetailId(conversations, conversationDetailJson['id']);

      conversation.twoUser = AesHelper.aesDecrypt(
        base64.decode(conversation.symmetricKey),
        base64.decode(conversationDetailJson['two_user']),
      ) == 'true';

      if (conversation.twoUser) {
        MyProfile profile = await MyProfile.getProfile();

        final db = await getDatabaseConnection();

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

        if (maps.length != 1) {
          throw ArgumentError('Invalid user id');
        }

        conversation.name = maps[0]['username'];
      } else {
        conversation.name = AesHelper.aesDecrypt(
          base64.decode(conversation.symmetricKey),
          base64.decode(conversationDetailJson['name']),
        );
      }

      // TODO: Handle exception here
      if (conversationDetailJson['attachment_id'] != null) {
        conversation.icon = await getFile(
          '$defaultServerUrl/files/${conversationDetailJson['attachment']['image_link']}',
          conversation.id,
          conversation.symmetricKey,
        );
      }

      await db.insert(
        'conversations',
        conversation.toMap(),
        conflictAlgorithm: ConflictAlgorithm.replace,
      );

      List<dynamic> usersData = conversationDetailJson['users'];

      for (var i = 0; i < usersData.length; i++) {
        ConversationUser conversationUser = ConversationUser.fromJson(
          usersData[i] as Map<String, dynamic>,
          base64.decode(conversation.symmetricKey),
        );

        await db.insert(
          'conversation_users',
          conversationUser.toMap(),
          conflictAlgorithm: ConflictAlgorithm.replace,
        );
      }
    }
  // } catch (SocketException) {
  //   return;
  // }
}


Future<void> uploadConversation(Conversation conversation, BuildContext context) async {
  String sessionCookie = await getSessionCookie();

  Map<String, dynamic> conversationJson = await conversation.payloadJson();

  var resp = await http.post(
    await MyProfile.getServerUrl('api/v1/auth/conversations'),
    headers: <String, String>{
      'Content-Type': 'application/json; charset=UTF-8',
      'cookie': sessionCookie,
    },
    body: jsonEncode(conversationJson),
  );

  if (resp.statusCode != 200) {
    showMessage('Failed to create conversation', context);
  }
}

