import 'dart:math';

import 'package:feet/api/api.dart';
import 'package:feet/main.dart';
import 'package:feet/pages/article.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:workmanager/workmanager.dart';

@pragma('vm:entry-point')
void callbackDispatcher() {
  Workmanager().executeTask((taskName, inputData) async {
    print("Native called background task: $taskName");

    if (taskName == 'fetchNotifications') {
      final sharedPreferences = await SharedPreferences.getInstance();

      try {
        var initialized = await FlutterLocalNotificationsPlugin().initialize(
          const InitializationSettings(
            android: AndroidInitializationSettings('@mipmap/ic_launcher'),
          ),
        );

        if (initialized == false) {
          print("Notification plugin did not initialize; cancelling task");
          return true;
        }

        var apiUrl = sharedPreferences.getString('apiUrl'),
            apiKey = sharedPreferences.getString('apiKey'),
            latest = sharedPreferences.getInt('newestKnownPost');

        if (apiUrl == null || apiKey == null) {
          print("API URL or API key not set; cancelling task");
          return true;
        }

        if (latest == null || latest == 0) {
          print("newestKnownPost is not set; cancelling task");
          return true;
        }

        var api = FeverAPI(
          apiKey: apiKey,
          apiUrl: apiUrl,
          sharedPrefs: sharedPreferences,
        );

        var response =
            await api.request().withItems().sinceId(latest).execute();
        var items =
            api.cache.items.getAll().values.where((post) => !post.isRead);

        print("Found ${items.length} new unread posts (out of "
            "${api.cache.items.size} total)");
        print(response);

        // Only fetch feeds and favicons if necessary to avoid wasting bandwidth
        if (items.isNotEmpty) {
          print("New posts found, fetching feeds and favicons");
          await api.request().withFeeds().withFavicons().execute();
        }

        for (var item in items) {
          var feed = api.cache.feeds.get(item.feedId);
          print("Notifying for post: ${item.id}");

          AndroidBitmap<Object>? iconData;
          var favicon = api.cache.favicons.get(feed?.faviconId ?? -1);
          if (favicon != null) {
            try {
              iconData = ByteArrayAndroidBitmap.fromBase64String(
                  favicon.data.split(',')[1]);
            } catch (e) {
              print("Failed to decode favicon: $e - Continuing");
            }
          }

          await FlutterLocalNotificationsPlugin().show(
            Random().nextInt(
                10000000), // Can't use the post ID here because FreshRSS's IDs are too large
            feed != null ? 'New post - ${feed.title}' : 'New post',
            item.title,
            payload: 'item/${item.id}',
            NotificationDetails(
              android: AndroidNotificationDetails(
                'new_posts',
                'New posts',
                autoCancel: true,
                channelShowBadge: true,
                importance: Importance.low,
                largeIcon: iconData,
                when: item.createdOnTime.millisecondsSinceEpoch,
                showWhen: true,
              ),
            ),
          );
        }
      } catch (e) {
        print(e);
        return false;
      }

      return true;
    }

    return true;
  });
}

void setupBackgroundTasks() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Workmanager().initialize(
    callbackDispatcher,
    isInDebugMode: kDebugMode,
  );

  await Permission.notification.request();

  await FlutterLocalNotificationsPlugin().initialize(
    const InitializationSettings(
      android: AndroidInitializationSettings('@mipmap/ic_launcher'),
    ),
    onSelectNotification: handleNotificationClick,
  );

  var launchDetails =
      await FlutterLocalNotificationsPlugin().getNotificationAppLaunchDetails();

  if (launchDetails?.payload != null && launchDetails!.payload!.isNotEmpty) {
    handleNotificationClick(launchDetails.payload);
  }

  // Test notification
  //if (kDebugMode) {
  //  await FlutterLocalNotificationsPlugin().show(
  //      Random().nextInt(1000000),
  //      "Test Notification",
  //      "Test Notification Body",
  //      const NotificationDetails(
  //        android: AndroidNotificationDetails(
  //          'new_posts',
  //          'New posts',
  //        ),
  //      ),
  //      payload: "item/1675609277381551");
  //}

  // Runs every 15 minutes
  await Workmanager().registerPeriodicTask(
    'fetch_notifications',
    'fetchNotifications',
    constraints: Constraints(networkType: NetworkType.connected),
    initialDelay: const Duration(seconds: 60),
  );
}

Future<void> handleNotificationClick(String? payload) async {
  print("Notification click: $payload");
  if (globalContext == null) {
    print("Global context not available.");
    return;
  }
  if (payload == null) {
    print("Didn't receive a payload");
    return;
  }

  try {
    if (payload.startsWith('item/')) {
      if (!api.loggedIn()) {
        print("API client is not logged in");
        return;
      }

      var id = toInt(payload.split('/')[1]);
      var item = api.cache.items.get(id);
      if (item == null) {
        var res = await api.request().withItems().withIds([id]).execute();
        item = api.cache.items.get(id);
        if (item == null) throw 'Didn\'t receive item $id from API: $res';
      }

      Navigator.of(globalContext!).push(
        MaterialPageRoute(
          builder: (context) => ArticlePage(article: item!),
        ),
      );

      if (!item.isRead) {
        await api
          .request()
          .markItem(item, ItemMarkType.read)
          .execute();
      }
    }
  } catch (e) {
    print("Error while handling notification click: $e");
  }
}
