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

import 'package:http/http.dart' as http;
import 'package:twonly/locator.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
import 'package:twonly/src/services/user.service.dart';
import 'package:twonly/src/utils/keyvalue.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';

const userStudySurveyKey = 'user_study_survey';

// PLEASE DO NOT SPAM OR TRY SENDING DIRECTLY TO THIS URL!
// You're just making my master's thesis more difficult and destroy scientific data. :/
const surveyUrlBase = 'https://survey.twonly.org/upload.php';

Future<void> handleUserStudyUpload() async {
  try {
    final token = userService.currentUser.userStudyParticipantsToken;
    if (token == null) return;

    // in case the survey was taken offline try again
    final userStudySurvey = await KeyValueStore.get(userStudySurveyKey);
    if (userStudySurvey != null) {
      final response = await http.post(
        Uri.parse('$surveyUrlBase/create/$token'),
        body: jsonEncode(userStudySurvey),
        headers: {'Content-Type': 'application/json'},
      );
      if (response.statusCode != 200) {
        Log.warn(
          'Got different status code for survey upload: ${response.statusCode}',
        );
        return;
      }
      await KeyValueStore.delete(userStudySurveyKey);
    }

    if (userService.currentUser.lastUserStudyDataUpload != null &&
        isToday(userService.currentUser.lastUserStudyDataUpload!)) {
      // Only send updates once a day.
      // This enables to see if improvements to actually work.
      return;
    }

    final contacts = await twonlyDB.contactsDao.getAllContacts();
    final verifications = await twonlyDB.keyVerificationDao
        .getFirstVerificationTypeByContacts();
    final udVerifiedByContactsCount = await twonlyDB.keyVerificationDao
        .getTransferredTrustVerificationsCount();

    final totalContactsWithVerificationBadge = await twonlyDB.keyVerificationDao
        .getCountOfContactsWithVerificationBadge();

    final udFriendsShared = await twonlyDB.contactsDao
        .getContactsAnnouncedViaUserDiscovery();

    final udAllAnnouncedUsers = await twonlyDB.userDiscoveryDao
        .getAllAnnouncedUsersWithRelations();

    var udUnknownAnnouncedUsers = 0;

    for (final udUser in udAllAnnouncedUsers.keys) {
      if (!contacts.any((c) => c.userId == udUser.announcedUserId)) {
        udUnknownAnnouncedUsers += 1;
      }
    }

    final dataCollection = {
      'total_contacts': contacts.length,

      'user_discovery_enabled': userService.currentUser.isUserDiscoveryEnabled,
      'user_discovery_required_send_images':
          userService.currentUser.requiredSendImages,
      'user_discovery_threshold':
          userService.currentUser.userDiscoveryThreshold,
      'user_discovery_requires_manual_approval':
          userService.currentUser.userDiscoveryRequiresManualApproval,
      'user_discovery_share_promotion':
          userService.currentUser.userDiscoverySharePromotion,

      'user_discovery_count_friends_shared': udFriendsShared.length,

      'user_discovery_count_announced_users': udAllAnnouncedUsers.length,
      'user_discovery_count_unknown_announced_users': udUnknownAnnouncedUsers,

      'user_study_count_new_friends_via_suggestion':
          userService.currentUser.userStudyCountNewFriendsViaSuggestion,
      'user_discovery_count_verified_by_contacts': udVerifiedByContactsCount,

      'accepted_contacts': contacts.where((c) => c.accepted).length,
      'verified_contacts': verifications.length,
      'total_contacts_with_verification_badge':
          totalContactsWithVerificationBadge,
      'verified_contacts_via_migrated_from_old_version': verifications.values
          .where((c) => c == VerificationType.migratedFromOldVersion)
          .length,
      'verified_contacts_via_qr_scanned': verifications.values
          .where((c) => c == VerificationType.qrScanned)
          .length,
      'verified_contacts_via_link': verifications.values
          .where((c) => c == VerificationType.link)
          .length,
      'verified_contacts_via_secret_qr_token': verifications.values
          .where((c) => c == VerificationType.secretQrToken)
          .length,
      'verified_contacts_via_contact_shared_by_verified': verifications.values
          .where((c) => c == VerificationType.contactSharedByVerified)
          .length,
    };

    final response = await http.post(
      Uri.parse('$surveyUrlBase/push/$token'),
      body: jsonEncode(dataCollection),
      headers: {'Content-Type': 'application/json'},
    );
    if (response.statusCode == 200) {
      await UserService.update((u) {
        u.lastUserStudyDataUpload = DateTime.now();
      });
    }
    if (response.statusCode == 404) {
      // Token is unknown to the server...
      await UserService.update((u) {
        u
          ..lastUserStudyDataUpload = null
          ..userStudyParticipantsToken = null;
      });
    }
  } catch (e) {
    if (e is http.ClientException ||
        e is SocketException ||
        e is TimeoutException) {
      Log.warn('Error uploading user study data: $e');
    } else {
      Log.error(e);
    }
  }
}
