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

import 'package:flutter/foundation.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';

import '../Entities/drift_database.dart';
import '../Repositories/transaction_repository.dart';
import '../Services/date_service.dart';
import '../Services/initializer.dart';
import '../Services/transaction_service.dart';
import 'port_controller.dart';
import 'transaction_controller.dart';

/// Kümmert sich um die Verwendung von Benachrichtigungen auf verschiedenen
/// Plattformen
class LocalNotifications {
  /// Gibt die aktuell gültige Instanz dieser Klasse zurück
  factory LocalNotifications() => _instance;

  LocalNotifications._internal() {
    unawaited(_initialise());
  }

  static final _instance = LocalNotifications._internal();

  final FlutterLocalNotificationsPlugin _localNotificationsPlugin =
      FlutterLocalNotificationsPlugin();
  final Initializer _initializer = Initializer();

  Future<void> _initialise() async {
    if (_initializer.initialized) {
      return;
    }

    await _localNotificationsPlugin
        .resolvePlatformSpecificImplementation<
          AndroidFlutterLocalNotificationsPlugin
        >()
        ?.requestNotificationsPermission();

    const AndroidInitializationSettings androidInitializationSettings =
        AndroidInitializationSettings('ic_launcher_foreground');
    const LinuxInitializationSettings linuxInitializationSettings =
        LinuxInitializationSettings(defaultActionName: 'Öffnen');

    const InitializationSettings initializationSettings =
        InitializationSettings(
          android: androidInitializationSettings,
          linux: linuxInitializationSettings,
        );

    await _localNotificationsPlugin.initialize(
      initializationSettings,
      onDidReceiveNotificationResponse: _onDidReceiveNotificationResponse,
      onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
    );

    if (!kIsWeb && !Platform.isLinux) {
      final NotificationAppLaunchDetails? notificationAppLaunchDetails =
          await _localNotificationsPlugin.getNotificationAppLaunchDetails();
      if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
        _onDidReceiveNotificationResponse(
          NotificationResponse(
            notificationResponseType:
                NotificationResponseType.selectedNotification,
            payload:
                notificationAppLaunchDetails!.notificationResponse?.payload,
          ),
        );
      }
    }

    _initializer.setInitialized();
  }

  /// Zeigt eine Benachrichtigung an, die einen dazu auffordert,
  /// automatisch generierte Transaktionen zu überprüfen
  Future<void> showTransactionsToCheckNotification(
    final List<Transaction> transactions,
  ) async {
    await _initializer.waitUntilInitialized();

    const AndroidNotificationDetails androidNotificationDetails =
        AndroidNotificationDetails(
          'transactions_to_check',
          'Transaktionen zu prüfen',
          channelDescription:
              'Zeigt an, dass es zu prüfende Transaktionen gibt',
          actions: <AndroidNotificationAction>[
            AndroidNotificationAction('mark_checked', 'Als geprüft markieren'),
            AndroidNotificationAction(
              'show_transactions',
              'Anzeigen',
              showsUserInterface: true,
            ),
            // TODO: Prüfen, ob die App geöffnet wird
          ],
        );
    const LinuxNotificationDetails linuxNotificationDetails =
        LinuxNotificationDetails(
          actions: [
            LinuxNotificationAction(
              key: 'mark_checked',
              label: 'Als geprüft markieren',
            ),
            LinuxNotificationAction(
              key: 'show_transactions',
              label: 'Anzeigen',
            ),
          ],
        );
    const NotificationDetails notificationDetails = NotificationDetails(
      android: androidNotificationDetails,
      linux: linuxNotificationDetails,
    );

    final String title;
    final String body;

    if (transactions.length == 1) {
      title = 'Transaktion prüfen';
      body =
          'Es wurde eine neue Transaktion anhand einer '
          'wiederkehrenden Transaktion erstellt:\n'
          '${transactions[0].name} - '
          '${DateService.dateFormat.format(transactions[0].date!)} - '
          '${transactions[0].amount} €\n\n'
          'Diese muss überprüft werden!';
    } else {
      int counter = 0;
      final List<String> transactionsToShow = [];

      for (final Transaction transaction in transactions) {
        if (counter >= 10) {
          break;
        }

        transactionsToShow.add(
          '${transaction.name} - '
          '${DateService.dateFormat.format(transaction.date!)} - '
          '${transaction.amount}',
        );
        counter++;
      }

      title = 'Transaktionen prüfen';
      body =
          'Es wurden neue Transaktionen anhand '
          'wiederkehrender Transaktionen erstellt:\n'
          '${transactionsToShow.join('\n')}\n\n'
          'Diese müssen überprüft werden!';
    }

    await _localNotificationsPlugin.show(
      0,
      title,
      body,
      notificationDetails,
      payload: TransactionService.transactionsToString(transactions),
    );
  }

  void _onDidReceiveNotificationResponse(
    final NotificationResponse notificationResponse,
  ) {
    if (notificationResponse.actionId == 'mark_checked') {
      TransactionRepository().markTransactionsAsChecked(
        TransactionService.transactionsFromString(notificationResponse.payload),
      );

      if (kIsWeb) {
        unawaited(TransactionController().updateTransactions());
      } else {
        PortController().getPort('update-transactions')?.send('ready');
      }
    } else {
      if (kIsWeb) {
        TransactionController().goToTransactions(
          transactions: TransactionService.transactionsFromString(
            notificationResponse.payload,
          ),
        );
      } else {
        PortController()
            .getPort('go-to-transactions')
            ?.send(notificationResponse.payload);
      }
    }
  }

  /// Wird von den LocalNotifications aufgerufen,
  /// wenn eine Aktion im Hintergrund abgehandelt werden soll
  @pragma('vm:entry-point')
  static void notificationTapBackground(
    final NotificationResponse notificationResponse,
  ) {
    if (notificationResponse.actionId == 'mark_checked') {
      TransactionRepository().markTransactionsAsChecked(
        TransactionService.transactionsFromString(notificationResponse.payload),
      );
    }
  }
}
