import 'dart:async';
import 'dart:io';
import 'dart:math';

import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
import 'package:cryptography_plus/cryptography_plus.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/constants/routes.keys.dart';
import 'package:twonly/src/constants/secure_storage.keys.dart';
import 'package:twonly/src/localization/generated/app_localizations.dart';
import 'package:twonly/src/localization/generated/app_localizations_de.dart';
import 'package:twonly/src/localization/generated/app_localizations_en.dart';
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
import 'package:twonly/src/model/protobuf/client/generated/push_notification.pb.dart';
import 'package:twonly/src/providers/routing.provider.dart';
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';

final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
    FlutterLocalNotificationsPlugin();

Future<void> customLocalPushNotification(String title, String msg) async {
  final androidNotificationDetails = AndroidNotificationDetails(
    '1',
    'System',
    channelDescription: 'System messages.',
    importance: Importance.high,
    priority: Priority.high,
    styleInformation: BigTextStyleInformation(msg),
    icon: 'ic_launcher_foreground',
  );

  const darwinNotificationDetails = DarwinNotificationDetails();
  final notificationDetails = NotificationDetails(
    android: androidNotificationDetails,
    iOS: darwinNotificationDetails,
  );

  final id = Random.secure().nextInt(9999);

  await flutterLocalNotificationsPlugin.show(
    id,
    title,
    msg,
    notificationDetails,
  );
}

Future<void> showPushNotificationFromServerMessages(
  int fromUserId,
  EncryptedContent encryptedContent,
) async {
  final pushData = await getPushNotificationFromEncryptedContent(
    null, // this is the toUserID which must be null as this means that the targetMessageId was send from this user.
    null,
    encryptedContent,
  );
  if (pushData != null) {
    final pushUsers = await getPushKeys(SecureStorageKeys.receivingPushKeys);
    for (final pushUser in pushUsers) {
      if (pushUser.userId.toInt() == fromUserId) {
        String? groupId;
        if (encryptedContent.hasGroupId()) {
          groupId = encryptedContent.groupId;
        }
        return showLocalPushNotification(pushUser, pushData, groupId: groupId);
      }
    }
  }
}

Future<PushNotification?> tryDecryptMessage(
  List<int> key,
  EncryptedPushNotification push,
) async {
  try {
    final chacha20 = FlutterChacha20.poly1305Aead();
    final secretKeyData = SecretKeyData(key);

    final secretBox = SecretBox(
      push.ciphertext,
      nonce: push.nonce,
      mac: Mac(push.mac),
    );

    final plaintext = await chacha20.decrypt(
      secretBox,
      secretKey: secretKeyData,
    );
    return PushNotification.fromBuffer(plaintext);
  } catch (e) {
    // this error is allowed to happen...
    return null;
  }
}

Future<void> showLocalPushNotification(
  PushUser pushUser,
  PushNotification pushNotification, {
  String? groupId,
  String? titleSuffix,
}) async {
  String? title;
  String? body;

  // do not show notification for blocked users...
  if (pushUser.blocked) {
    Log.info('Blocked a message from a blocked user!');
    return;
  }

  var targetGroupId = groupId;
  if (targetGroupId == null) {
    try {
      if (userService.isUserCreated) {
        targetGroupId = getUUIDforDirectChat(
          userService.currentUser.userId,
          pushUser.userId.toInt(),
        );
      }
    } catch (_) {}
  }

  if (targetGroupId != null) {
    try {
      final currentUri = routerProvider.routerDelegate.currentConfiguration.uri;
      final isResumed =
          WidgetsBinding.instance.lifecycleState == AppLifecycleState.resumed;
      if (isResumed && currentUri.path.contains(targetGroupId)) {
        Log.info(
          'Suppressing local push notification because chat with group $targetGroupId is currently open.',
        );
        return;
      }
    } catch (e) {
      Log.error('Error checking current route: $e');
    }
  }

  title = pushUser.displayName;
  if (titleSuffix != null && titleSuffix.isNotEmpty) {
    title = '$title $titleSuffix';
  }

  body = getPushNotificationText(pushNotification);
  if (body == '') {
    Log.error('No push notification type defined!');
  }

  FilePathAndroidBitmap? styleInformation;
  final avatarPath = await getAvatarIcon(pushUser.userId.toInt());
  if (avatarPath != null) {
    styleInformation = FilePathAndroidBitmap(avatarPath);
  }

  final lang = getLocalizations();

  final androidNotificationDetails = AndroidNotificationDetails(
    '0',
    lang.notificationCategoryMessageTitle,
    channelDescription: lang.notificationCategoryMessageDesc,
    importance: Importance.max,
    priority: Priority.max,
    ticker: 'You got a new message.',
    largeIcon: styleInformation,
    icon: 'ic_launcher_foreground',
    groupKey: 'com.twonly.messages',
  );

  const darwinNotificationDetails = DarwinNotificationDetails();
  final notificationDetails = NotificationDetails(
    android: androidNotificationDetails,
    iOS: darwinNotificationDetails,
  );

  String? payload;

  if (groupId != null &&
      (pushNotification.kind == PushKind.TEXT ||
          pushNotification.kind == PushKind.RESPONSE ||
          pushNotification.kind == PushKind.REACTION_TO_AUDIO ||
          pushNotification.kind == PushKind.STORED_MEDIA_FILE ||
          pushNotification.kind == PushKind.REACTION_TO_IMAGE ||
          pushNotification.kind == PushKind.REACTION_TO_TEXT ||
          pushNotification.kind == PushKind.REACTION_TO_VIDEO)) {
    payload = Routes.chatsMessages(groupId);
  } else {
    payload = Routes.chats;
  }

  await flutterLocalNotificationsPlugin.show(
    // Invalid argument (id): must fit within the size of a 32-bit integer
    pushUser.userId.toInt() % 2147483647,
    title,
    body,
    notificationDetails,
    payload: payload,
  );

  if (Platform.isAndroid) {
    final summaryAndroidDetails = AndroidNotificationDetails(
      '0',
      lang.notificationCategoryMessageTitle,
      channelDescription: lang.notificationCategoryMessageDesc,
      importance: Importance.max,
      priority: Priority.max,
      groupKey: 'com.twonly.messages',
      setAsGroupSummary: true,
      icon: 'ic_launcher_foreground',
    );
    final summaryNotificationDetails = NotificationDetails(
      android: summaryAndroidDetails,
    );
    await flutterLocalNotificationsPlugin.show(
      0,
      lang.notificationCategoryMessageTitle,
      '',
      summaryNotificationDetails,
      payload: Routes.chats,
    );
  }
}

Future<String?> getAvatarIcon(int contactId) async {
  final avatarsDirectory = Directory('${AppEnvironment.cacheDir}/avatars');
  final filePath = '${avatarsDirectory.path}/$contactId.png';
  final file = File(filePath);
  if (file.existsSync()) {
    return filePath;
  }
  return null;
}

AppLocalizations getLocalizations() {
  final systemLanguage = Platform.localeName;
  if (systemLanguage.contains('de')) return AppLocalizationsDe();
  return AppLocalizationsEn();
}

String getPushNotificationText(PushNotification pushNotification) {
  final lang = getLocalizations();

  var inGroup = '';

  if (pushNotification.hasAdditionalContent()) {
    inGroup =
        ' ${lang.notificationFillerIn} ${pushNotification.additionalContent}';
  }

  final pushNotificationText = {
    PushKind.TEXT.name: lang.notificationText(inGroup),
    PushKind.TWONLY.name: lang.notificationTwonly(inGroup),
    PushKind.VIDEO.name: lang.notificationVideo(inGroup),
    PushKind.IMAGE.name: lang.notificationImage(inGroup),
    PushKind.AUDIO.name: lang.notificationAudio(inGroup),
    PushKind.CONTACT_REQUEST.name: lang.notificationContactRequest,
    PushKind.ACCEPT_REQUEST.name: lang.notificationAcceptRequest,
    PushKind.STORED_MEDIA_FILE.name: lang.notificationStoredMediaFile,
    PushKind.REACTION.name: lang.notificationReaction,
    PushKind.REOPENED_MEDIA.name: lang.notificationReopenedMedia,
    PushKind.REACTION_TO_VIDEO.name: lang.notificationReactionToVideo(
      pushNotification.additionalContent,
    ),
    PushKind.REACTION_TO_AUDIO.name: lang.notificationReactionToAudio(
      pushNotification.additionalContent,
    ),
    PushKind.REACTION_TO_TEXT.name: lang.notificationReactionToText(
      pushNotification.additionalContent,
    ),
    PushKind.REACTION_TO_IMAGE.name: lang.notificationReactionToImage(
      pushNotification.additionalContent,
    ),
    PushKind.RESPONSE.name: lang.notificationResponse(inGroup),
    PushKind.ADDED_TO_GROUP.name: lang.notificationAddedToGroup(
      pushNotification.additionalContent,
    ),
  };

  return pushNotificationText[pushNotification.kind.name] ?? '';
}
