import 'package:appflowy/core/helpers/url_launcher.dart';
import 'package:appflowy/startup/tasks/device_info_task.dart';
import 'package:appflowy_backend/log.dart';
import 'package:auto_updater/auto_updater.dart';
import 'package:collection/collection.dart';
import 'package:http/http.dart' as http;
import 'package:universal_platform/universal_platform.dart';
import 'package:xml/xml.dart' as xml;

final versionChecker = VersionChecker();

/// Version checker class to handle update checks using appcast XML feeds
class VersionChecker {
  factory VersionChecker() => _instance;

  VersionChecker._internal();
  String? _feedUrl;

  static final VersionChecker _instance = VersionChecker._internal();

  /// Sets the appcast XML feed URL
  void setFeedUrl(String url) {
    _feedUrl = url;

    if (UniversalPlatform.isWindows || UniversalPlatform.isMacOS) {
      autoUpdater.setFeedURL(url);
      // disable the auto update check
      autoUpdater.setScheduledCheckInterval(0);
    }
  }

  /// Checks for updates by fetching and parsing the appcast XML
  /// Returns a list of [AppcastItem] or throws an exception if the feed URL is not set
  Future<AppcastItem?> checkForUpdateInformation() async {
    if (_feedUrl == null) {
      Log.error('Feed URL is not set');
      return null;
    }

    try {
      final response = await http.get(Uri.parse(_feedUrl!));
      if (response.statusCode != 200) {
        Log.info('Failed to fetch appcast XML: ${response.statusCode}');
        return null;
      }

      // Parse XML content
      final document = xml.XmlDocument.parse(response.body);
      final items = document.findAllElements('item');

      // Convert XML items to AppcastItem objects
      return items
          .map(_parseAppcastItem)
          .nonNulls
          .firstWhereOrNull((e) => e.os == ApplicationInfo.os);
    } catch (e) {
      Log.info('Failed to check for updates: $e');
    }

    return null;
  }

  /// For Windows and macOS, calling this API will trigger the auto updater to check for updates
  /// For Linux, it will open the official website in the browser if there is a new version

  Future<void> checkForUpdate() async {
    if (UniversalPlatform.isLinux) {
      // open the official website in the browser
      await afLaunchUrlString('https://appflowy.com/download');
    } else {
      await autoUpdater.checkForUpdates();
    }
  }

  AppcastItem? _parseAppcastItem(xml.XmlElement item) {
    final enclosure = item.findElements('enclosure').firstOrNull;
    return AppcastItem.fromJson({
      'title': item.findElements('title').firstOrNull?.innerText,
      'versionString': item
          .findElements('sparkle:shortVersionString')
          .firstOrNull
          ?.innerText,
      'displayVersionString': item
          .findElements('sparkle:shortVersionString')
          .firstOrNull
          ?.innerText,
      'releaseNotesUrl':
          item.findElements('releaseNotesLink').firstOrNull?.innerText,
      'pubDate': item.findElements('pubDate').firstOrNull?.innerText,
      'fileURL': enclosure?.getAttribute('url') ?? '',
      'os': enclosure?.getAttribute('sparkle:os') ?? '',
      'criticalUpdate':
          enclosure?.getAttribute('sparkle:criticalUpdate') ?? false,
    });
  }
}
