content: import 'dart:async';
  import 'package:animations/animations.dart';
  import 'package:dynamic_color/dynamic_color.dart';
  import 'package:fl_clash/clash/clash.dart';
  import 'package:fl_clash/common/theme.dart';
  import 'package:fl_clash/enum/enum.dart';
  import 'package:fl_clash/l10n/l10n.dart';
  import 'package:fl_clash/plugins/service.dart';
  import 'package:fl_clash/widgets/dialog.dart';
  import 'package:fl_clash/widgets/scaffold.dart';
  import 'package:flutter/material.dart';
  import 'package:material_color_utilities/palettes/core_palette.dart';
  import 'package:package_info_plus/package_info_plus.dart';
  import 'package:url_launcher/url_launcher.dart';
  
  import 'common/common.dart';
  import 'controller.dart';
  import 'models/models.dart';
  
  typedef UpdateTasks = List<FutureOr Function()>;
  
  class GlobalState {
    static GlobalState? _instance;
    Map<CacheTag, double> cacheScrollPosition = {};
    Map<CacheTag, FixedMap<String, double>> cacheHeightMap = {};
    bool isService = false;
    Timer? timer;
    Timer? groupsUpdateTimer;
    late Config config;
    late AppState appState;
    bool isPre = true;
    String? coreSHA256;
    late PackageInfo packageInfo;
    Function? updateCurrentDelayDebounce;
    late Measure measure;
    late CommonTheme theme;
    late Color accentColor;
    CorePalette? corePalette;
    DateTime? startTime;
    UpdateTasks tasks = [];
    final navigatorKey = GlobalKey<NavigatorState>();
    late AppController appController;
    GlobalKey<CommonScaffoldState> homeScaffoldKey = GlobalKey();
  
    bool get isStart => startTime != null && startTime!.isBeforeNow;
  
    GlobalState._internal();
  
    factory GlobalState() {
      _instance ??= GlobalState._internal();
      return _instance!;
    }
  
    initApp(int version) async {
      coreSHA256 = const String.fromEnvironment("CORE_SHA256");
      isPre = const String.fromEnvironment("APP_ENV") != 'stable';
      appState = AppState(
        version: version,
        viewSize: Size.zero,
        requests: FixedList(maxLength),
        logs: FixedList(maxLength),
        traffics: FixedList(30),
        totalTraffic: Traffic(),
      );
      await _initDynamicColor();
      await init();
    }
  
    _initDynamicColor() async {
      try {
        corePalette = await DynamicColorPlugin.getCorePalette();
        accentColor = await DynamicColorPlugin.getAccentColor() ??
            Color(defaultPrimaryColor);
      } catch (_) {}
    }
  
    init() async {
      packageInfo = await PackageInfo.fromPlatform();
      config = await preferences.getConfig() ??
          Config(
            themeProps: defaultThemeProps,
          );
      await globalState.migrateOldData(config);
      await AppLocalizations.load(
        utils.getLocaleForString(config.appSetting.locale) ??
            WidgetsBinding.instance.platformDispatcher.locale,
      );
    }
  
    String get ua => config.patchClashConfig.globalUa ?? packageInfo.ua;
  
    startUpdateTasks([UpdateTasks? tasks]) async {
      if (timer != null && timer!.isActive == true) return;
      if (tasks != null) {
        this.tasks = tasks;
      }
      await executorUpdateTask();
      timer = Timer(const Duration(seconds: 1), () async {
        startUpdateTasks();
      });
    }
  
    executorUpdateTask() async {
      for (final task in tasks) {
        await task();
      }
      timer = null;
    }
  
    stopUpdateTasks() {
      if (timer == null || timer?.isActive == false) return;
      timer?.cancel();
      timer = null;
    }
  
    handleStart([UpdateTasks? tasks]) async {
      startTime ??= DateTime.now();
      await clashCore.startListener();
      await service?.startVpn();
      startUpdateTasks(tasks);
    }
  
    Future updateStartTime() async {
      startTime = await clashLib?.getRunTime();
    }
  
    Future handleStop() async {
      startTime = null;
      await clashCore.stopListener();
      await service?.stopVpn();
      stopUpdateTasks();
    }
  
    Future<bool?> showMessage({
      String? title,
      required InlineSpan message,
      String? confirmText,
      bool cancelable = true,
    }) async {
      return await showCommonDialog<bool>(
        child: Builder(
          builder: (context) {
            return CommonDialog(
              title: title ?? appLocalizations.tip,
              actions: [
                if (cancelable)
                  TextButton(
                    onPressed: () {
                      Navigator.of(context).pop(false);
                    },
                    child: Text(appLocalizations.cancel),
                  ),
                TextButton(
                  onPressed: () {
                    Navigator.of(context).pop(true);
                  },
                  child: Text(confirmText ?? appLocalizations.confirm),
                )
              ],
              child: Container(
                width: 300,
                constraints: const BoxConstraints(maxHeight: 200),
                child: SingleChildScrollView(
                  child: SelectableText.rich(
                    TextSpan(
                      style: Theme.of(context).textTheme.labelLarge,
                      children: [message],
                    ),
                    style: const TextStyle(
                      overflow: TextOverflow.visible,
                    ),
                  ),
                ),
              ),
            );
          },
        ),
      );
    }
  
    Future<T?> showCommonDialog<T>({
      required Widget child,
      bool dismissible = true,
    }) async {
      return await showModal<T>(
        context: navigatorKey.currentState!.context,
        configuration: FadeScaleTransitionConfiguration(
          barrierColor: Colors.black38,
          barrierDismissible: dismissible,
        ),
        builder: (_) => child,
        filter: commonFilter,
      );
    }
  
    Future<T?> safeRun<T>(
      FutureOr<T> Function() futureFunction, {
      String? title,
      bool silence = true,
    }) async {
      try {
        final res = await futureFunction();
        return res;
      } catch (e) {
        commonPrint.log("$e");
        if (silence) {
          showNotifier(e.toString());
        } else {
          showMessage(
            title: title ?? appLocalizations.tip,
            message: TextSpan(
              text: e.toString(),
            ),
          );
        }
        return null;
      }
    }
  
    showNotifier(String text) {
      if (text.isEmpty) {
        return;
      }
      navigatorKey.currentContext?.showNotifier(text);
    }
  
    openUrl(String url) async {
      final res = await showMessage(
        message: TextSpan(text: url),
        title: appLocalizations.externalLink,
        confirmText: appLocalizations.go,
      );
      if (res != true) {
        return;
      }
      launchUrl(Uri.parse(url));
    }
  
    Future<void> migrateOldData(Config config) async {
      final clashConfig = await preferences.getClashConfig();
      if (clashConfig != null) {
        config = config.copyWith(
          patchClashConfig: clashConfig,
        );
        preferences.clearClashConfig();
        preferences.saveConfig(config);
      }
    }
  
    CoreState getCoreState() {
      final currentProfile = config.currentProfile;
      return CoreState(
        vpnProps: config.vpnProps,
        onlyStatisticsProxy: config.appSetting.onlyStatisticsProxy,
        currentProfileName: currentProfile?.label ?? currentProfile?.id ?? "",
        bypassDomain: config.networkProps.bypassDomain,
      );
    }
  
    getUpdateConfigParams([bool? isPatch]) {
      final currentProfile = config.currentProfile;
      final clashConfig = config.patchClashConfig;
      final routeAddress =
          config.networkProps.routeMode == RouteMode.bypassPrivate
              ? defaultBypassPrivateRouteAddress
              : clashConfig.tun.routeAddress;
      return UpdateConfigParams(
        profileId: config.currentProfileId ?? "",
        config: clashConfig.copyWith(
          globalUa: ua,
          tun: clashConfig.tun.copyWith(
            autoRoute: routeAddress.isEmpty ? true : false,
            routeAddress: routeAddress,
          ),
          rule: currentProfile?.overrideData.runningRule ?? [],
        ),
        params: ConfigExtendedParams(
          isPatch: isPatch ?? false,
          selectedMap: currentProfile?.selectedMap ?? {},
          overrideDns: config.overrideDns,
          testUrl: config.appSetting.testUrl,
          overrideRule:
              currentProfile?.overrideData.rule.type == OverrideRuleType.override
                  ? true
                  : false,
        ),
      );
    }
  }
  
  final globalState = GlobalState();
  