content: import 'dart:async';
  import 'dart:convert';
  import 'dart:io';
  import 'dart:isolate';
  import 'dart:typed_data';
  
  import 'package:archive/archive.dart';
  import 'package:fl_clash/clash/clash.dart';
  import 'package:fl_clash/common/archive.dart';
  import 'package:fl_clash/common/link_parser.dart';
  import 'package:fl_clash/enum/enum.dart';
  import 'package:fl_clash/manager/clipboard_manager.dart';
  import 'package:fl_clash/providers/providers.dart';
  import 'package:fl_clash/state.dart';
  import 'package:fl_clash/widgets/dialog.dart';
  import 'package:flutter/material.dart';
  import 'package:flutter/services.dart';
  import 'package:flutter_riverpod/flutter_riverpod.dart';
  import 'package:path/path.dart';
  import 'package:url_launcher/url_launcher.dart';
  
  import 'common/common.dart';
  import 'fragments/profiles/override_profile.dart';
  import 'models/models.dart';
  
  class AppController {
    bool lastTunEnable = false;
    int? lastProfileModified;
  
    final BuildContext context;
    final WidgetRef _ref;
  
    AppController(this.context, WidgetRef ref) : _ref = ref;
  
    updateClashConfigDebounce() {
      debouncer.call(DebounceTag.updateClashConfig, () async {
        final isPatch = globalState.appState.needApply ? false : true;
        await updateClashConfig(isPatch);
      });
    }
  
    updateGroupsDebounce() {
      debouncer.call(DebounceTag.updateGroups, updateGroups);
    }
  
    addCheckIpNumDebounce() {
      debouncer.call(DebounceTag.addCheckIpNum, () {
        _ref.read(checkIpNumProvider.notifier).add();
      });
    }
  
    applyProfileDebounce({
      bool silence = false,
    }) {
      debouncer.call(DebounceTag.applyProfile, (silence) {
        applyProfile(silence: silence);
      }, args: [silence]);
    }
  
    savePreferencesDebounce() {
      debouncer.call(DebounceTag.savePreferences, savePreferences);
    }
  
    changeProxyDebounce(String groupName, String proxyName) {
      debouncer.call(DebounceTag.changeProxy,
          (String groupName, String proxyName) async {
        await changeProxy(
          groupName: groupName,
          proxyName: proxyName,
        );
        await updateGroups();
      }, args: [groupName, proxyName]);
    }
  
    restartCore() async {
      await clashService?.reStart();
      await _initCore();
  
      if (_ref.read(runTimeProvider.notifier).isStart) {
        await globalState.handleStart();
      }
    }
  
    updateStatus(bool isStart) async {
      if (isStart) {
        await globalState.handleStart([
          updateRunTime,
          updateTraffic,
        ]);
        final currentLastModified =
            await _ref.read(currentProfileProvider)?.profileLastModified;
        if (currentLastModified == null || lastProfileModified == null) {
          addCheckIpNumDebounce();
          return;
        }
        if (currentLastModified <= (lastProfileModified ?? 0)) {
          addCheckIpNumDebounce();
          return;
        }
        applyProfileDebounce();
      } else {
        await globalState.handleStop();
        await clashCore.resetTraffic();
        _ref.read(trafficsProvider.notifier).clear();
        _ref.read(totalTrafficProvider.notifier).value = Traffic();
        _ref.read(runTimeProvider.notifier).value = null;
        addCheckIpNumDebounce();
      }
    }
  
    updateRunTime() {
      final startTime = globalState.startTime;
      if (startTime != null) {
        final startTimeStamp = startTime.millisecondsSinceEpoch;
        final nowTimeStamp = DateTime.now().millisecondsSinceEpoch;
        _ref.read(runTimeProvider.notifier).value = nowTimeStamp - startTimeStamp;
      } else {
        _ref.read(runTimeProvider.notifier).value = null;
      }
    }
  
    updateTraffic() async {
      final traffic = await clashCore.getTraffic();
      _ref.read(trafficsProvider.notifier).addTraffic(traffic);
      _ref.read(totalTrafficProvider.notifier).value =
          await clashCore.getTotalTraffic();
    }
  
    addProfile(Profile profile) async {
      _ref.read(profilesProvider.notifier).setProfile(profile);
      if (_ref.read(currentProfileIdProvider) != null) return;
      _ref.read(currentProfileIdProvider.notifier).value = profile.id;
    }
  
    deleteProfile(String id) async {
      _ref.read(profilesProvider.notifier).deleteProfileById(id);
      clearEffect(id);
      if (globalState.config.currentProfileId == id) {
        final profiles = globalState.config.profiles;
        final currentProfileId = _ref.read(currentProfileIdProvider.notifier);
        if (profiles.isNotEmpty) {
          final updateId = profiles.first.id;
          currentProfileId.value = updateId;
        } else {
          currentProfileId.value = null;
          updateStatus(false);
        }
      }
    }
  
    updateProviders() async {
      _ref.read(providersProvider.notifier).value =
          await clashCore.getExternalProviders();
    }
  
    updateLocalIp() async {
      _ref.read(localIpProvider.notifier).value = null;
      await Future.delayed(commonDuration);
      _ref.read(localIpProvider.notifier).value = await utils.getLocalIpAddress();
    }
  
    Future<void> updateProfile(Profile profile) async {
      final newProfile = await profile.update();
      _ref
          .read(profilesProvider.notifier)
          .setProfile(newProfile.copyWith(isUpdating: false));
      if (profile.id == _ref.read(currentProfileIdProvider)) {
        applyProfileDebounce(silence: true);
      }
    }
  
    setProfile(Profile profile) {
      _ref.read(profilesProvider.notifier).setProfile(profile);
    }
  
    setProfileAndAutoApply(Profile profile) {
      _ref.read(profilesProvider.notifier).setProfile(profile);
      if (profile.id == _ref.read(currentProfileIdProvider)) {
        applyProfileDebounce(silence: true);
      }
    }
  
    setProfiles(List<Profile> profiles) {
      _ref.read(profilesProvider.notifier).value = profiles;
    }
  
    addLog(Log log) {
      _ref.read(logsProvider).add(log);
    }
  
    updateOrAddHotKeyAction(HotKeyAction hotKeyAction) {
      final hotKeyActions = _ref.read(hotKeyActionsProvider);
      final index =
          hotKeyActions.indexWhere((item) => item.action == hotKeyAction.action);
      if (index == -1) {
        _ref.read(hotKeyActionsProvider.notifier).value = List.from(hotKeyActions)
          ..add(hotKeyAction);
      } else {
        _ref.read(hotKeyActionsProvider.notifier).value = List.from(hotKeyActions)
          ..[index] = hotKeyAction;
      }
  
      _ref.read(hotKeyActionsProvider.notifier).value = index == -1
          ? (List.from(hotKeyActions)..add(hotKeyAction))
          : (List.from(hotKeyActions)..[index] = hotKeyAction);
    }
  
    List<Group> getCurrentGroups() {
      return _ref.read(currentGroupsStateProvider.select((state) => state.value));
    }
  
    String getRealTestUrl(String? url) {
      return _ref.read(getRealTestUrlProvider(url));
    }
  
    int getProxiesColumns() {
      return _ref.read(getProxiesColumnsProvider);
    }
  
    addSortNum() {
      return _ref.read(sortNumProvider.notifier).add();
    }
  
    getCurrentGroupName() {
      final currentGroupName = _ref.read(currentProfileProvider.select(
        (state) => state?.currentGroupName,
      ));
      return currentGroupName;
    }
  
    ProxyCardState getProxyCardState(proxyName) {
      return _ref.read(getProxyCardStateProvider(proxyName));
    }
  
    getSelectedProxyName(groupName) {
      return _ref.read(getSelectedProxyNameProvider(groupName));
    }
  
    updateCurrentGroupName(String groupName) {
      final profile = _ref.read(currentProfileProvider);
      if (profile == null || profile.currentGroupName == groupName) {
        return;
      }
      setProfile(
        profile.copyWith(currentGroupName: groupName),
      );
    }
  
    Future<void> updateClashConfig([bool? isPatch]) async {
      commonPrint.log("update clash patch: ${isPatch ?? false}");
      final commonScaffoldState = globalState.homeScaffoldKey.currentState;
      if (commonScaffoldState?.mounted != true) return;
      await commonScaffoldState?.loadingRun(() async {
        await _updateClashConfig(
          isPatch,
        );
      });
    }
  
    Future<void> _updateClashConfig([bool? isPatch]) async {
      final profile = _ref.watch(currentProfileProvider);
      await _ref.read(currentProfileProvider)?.checkAndUpdate();
      final patchConfig = _ref.read(patchClashConfigProvider);
      final appSetting = _ref.read(appSettingProvider);
      bool enableTun = patchConfig.tun.enable;
      if (enableTun != lastTunEnable && lastTunEnable == false) {
        final code = await system.authorizeCore();
        switch (code) {
          case AuthorizeCode.none:
            break;
          case AuthorizeCode.success:
            lastTunEnable = enableTun;
            await restartCore();
            return;
          case AuthorizeCode.error:
            enableTun = false;
        }
      }
      if (appSetting.openLogs) {
        clashCore.startLog();
      } else {
        clashCore.stopLog();
      }
      final res = await clashCore.updateConfig(
        globalState.getUpdateConfigParams(isPatch),
      );
      if (isPatch == false) {
        _ref.read(needApplyProvider.notifier).value = false;
      }
      if (res.isNotEmpty) throw res;
      lastTunEnable = enableTun;
      lastProfileModified = await profile?.profileLastModified;
    }
  
    Future _applyProfile() async {
      await clashCore.requestGc();
      await updateClashConfig();
      await updateGroups();
      await updateProviders();
    }
  
    Future applyProfile({bool silence = false}) async {
      if (silence) {
        await _applyProfile();
      } else {
        final commonScaffoldState = globalState.homeScaffoldKey.currentState;
        if (commonScaffoldState?.mounted != true) return;
        await commonScaffoldState?.loadingRun(() async {
          await _applyProfile();
        });
      }
      addCheckIpNumDebounce();
    }
  
    handleChangeProfile() {
      _ref.read(delayDataSourceProvider.notifier).value = {};
      applyProfile();
      _ref.read(logsProvider.notifier).value = FixedList(500);
      _ref.read(requestsProvider.notifier).value = FixedList(500);
      globalState.cacheHeightMap = {};
      globalState.cacheScrollPosition = {};
    }
  
    updateBrightness(Brightness brightness) {
      _ref.read(appBrightnessProvider.notifier).value = brightness;
    }
  
    autoUpdateProfiles() async {
      for (final profile in _ref.read(profilesProvider)) {
        if (!profile.autoUpdate) continue;
        final isNotNeedUpdate = profile.lastUpdateDate
            ?.add(
              profile.autoUpdateDuration,
            )
            .isBeforeNow;
        if (isNotNeedUpdate == false || profile.type == ProfileType.file) {
          continue;
        }
        try {
          await updateProfile(profile);
        } catch (e) {
          commonPrint.log(e.toString());
        }
      }
    }
  
    Future<void> updateGroups() async {
      try {
        _ref.read(groupsProvider.notifier).value = await retry(
          task: () async {
            return await clashCore.getProxiesGroups();
          },
          retryIf: (res) => res.isEmpty,
        );
      } catch (_) {
        _ref.read(groupsProvider.notifier).value = [];
      }
    }
  
    updateProfiles() async {
      for (final profile in _ref.read(profilesProvider)) {
        if (profile.type == ProfileType.file) {
          continue;
        }
        await updateProfile(profile);
      }
    }
  
    savePreferences() async {
      commonPrint.log("save preferences");
      await preferences.saveConfig(globalState.config);
    }
  
    changeProxy({
      required String groupName,
      required String proxyName,
    }) async {
      await clashCore.changeProxy(
        ChangeProxyParams(
          groupName: groupName,
          proxyName: proxyName,
        ),
      );
      if (_ref.read(appSettingProvider).closeConnections) {
        clashCore.closeConnections();
      }
      addCheckIpNumDebounce();
    }
  
    handleBackOrExit() async {
      if (_ref.read(appSettingProvider).minimizeOnExit) {
        if (system.isDesktop) {
          await savePreferencesDebounce();
        }
        await system.back();
      } else {
        await handleExit();
      }
    }
  
    handleExit() async {
      try {
        await updateStatus(false);
        await proxy?.stopProxy();
        await clashCore.shutdown();
        await clashService?.destroy();
        await savePreferences();
      } finally {
        system.exit();
      }
    }
  
    Future handleClear() async {
      await preferences.clearPreferences();
      commonPrint.log("clear preferences");
      globalState.config = Config(
        themeProps: defaultThemeProps,
      );
    }
  
    autoCheckUpdate() async {
      if (!_ref.read(appSettingProvider).autoCheckUpdate) return;
      final res = await request.checkForUpdate();
      checkUpdateResultHandle(data: res);
    }
  
    checkUpdateResultHandle({
      Map<String, dynamic>? data,
      bool handleError = false,
    }) async {
      if (globalState.isPre) {
        return;
      }
      if (data != null) {
        final tagName = data['tag_name'];
        final body = data['body'];
        final submits = utils.parseReleaseBody(body);
        final textTheme = context.textTheme;
        final res = await globalState.showMessage(
          title: appLocalizations.discoverNewVersion,
          message: TextSpan(
            text: "$tagName \n",
            style: textTheme.headlineSmall,
            children: [
              TextSpan(
                text: "\n",
                style: textTheme.bodyMedium,
              ),
              for (final submit in submits)
                TextSpan(
                  text: "- $submit \n",
                  style: textTheme.bodyMedium,
                ),
            ],
          ),
          confirmText: appLocalizations.goDownload,
        );
        if (res != true) {
          return;
        }
        launchUrl(
          Uri.parse("https://github.com/$repository/releases/latest"),
        );
      } else if (handleError) {
        globalState.showMessage(
          title: appLocalizations.checkUpdate,
          message: TextSpan(
            text: appLocalizations.checkUpdateError,
          ),
        );
      }
    }
  
    _handlePreference() async {
      if (await preferences.isInit) {
        return;
      }
      final res = await globalState.showMessage(
        title: appLocalizations.tip,
        message: TextSpan(text: appLocalizations.cacheCorrupt),
      );
      if (res == true) {
        final file = File(await appPath.sharedPreferencesPath);
        final isExists = await file.exists();
        if (isExists) {
          await file.delete();
        }
      }
      await handleExit();
    }
  
    Future<void> _initCore() async {
      final isInit = await clashCore.isInit;
      if (!isInit) {
        await clashCore.init();
        await clashCore.setState(
          globalState.getCoreState(),
        );
      }
      await applyProfile();
    }
  
    init() async {
      await _handlePreference();
      await _handlerDisclaimer();
      await _initCore();
      await _initStatus();
      updateTray(true);
      autoLaunch?.updateStatus(
        _ref.read(appSettingProvider).autoLaunch,
      );
      autoUpdateProfiles();
      autoCheckUpdate();
      if (!_ref.read(appSettingProvider).silentLaunch) {
        window?.show();
      } else {
        window?.hide();
      }
      _ref.read(initProvider.notifier).value = true;
    }
  
    _initStatus() async {
      if (Platform.isAndroid) {
        await globalState.updateStartTime();
      }
      final status = globalState.isStart == true
          ? true
          : _ref.read(appSettingProvider).autoRun;
  
      await updateStatus(status);
      if (!status) {
        addCheckIpNumDebounce();
      }
    }
  
    setDelay(Delay delay) {
      _ref.read(delayDataSourceProvider.notifier).setDelay(delay);
    }
  
    toPage(PageLabel pageLabel) {
      _ref.read(currentPageLabelProvider.notifier).value = pageLabel;
    }
  
    toProfiles() {
      toPage(PageLabel.profiles);
    }
  
    initLink() {
      linkManager.initAppLinksListen(
        (url) async {
          final res = await globalState.showMessage(
            title: "${appLocalizations.add}${appLocalizations.profile}",
            message: TextSpan(
              children: [
                TextSpan(text: appLocalizations.doYouWantToPass),
                TextSpan(
                  text: " $url ",
                  style: TextStyle(
                    color: Theme.of(context).colorScheme.primary,
                    decoration: TextDecoration.underline,
                    decorationColor: Theme.of(context).colorScheme.primary,
                  ),
                ),
                TextSpan(
                    text:
                        "${appLocalizations.create}${appLocalizations.profile}"),
              ],
            ),
          );
  
          if (res != true) {
            return;
          }
          addProfileFormURL(url);
        },
      );
    }
  
    Future<bool> showDisclaimer() async {
      return await globalState.showCommonDialog<bool>(
            dismissible: false,
            child: CommonDialog(
              title: appLocalizations.disclaimer,
              actions: [
                TextButton(
                  onPressed: () {
                    Navigator.of(context).pop<bool>(false);
                  },
                  child: Text(appLocalizations.exit),
                ),
                TextButton(
                  onPressed: () {
                    _ref.read(appSettingProvider.notifier).updateState(
                          (state) => state.copyWith(disclaimerAccepted: true),
                        );
                    Navigator.of(context).pop<bool>(true);
                  },
                  child: Text(appLocalizations.agree),
                )
              ],
              child: SelectableText(
                appLocalizations.disclaimerDesc,
              ),
            ),
          ) ??
          false;
    }
  
    _handlerDisclaimer() async {
      if (_ref.read(appSettingProvider).disclaimerAccepted) {
        return;
      }
      final isDisclaimerAccepted = await showDisclaimer();
      if (!isDisclaimerAccepted) {
        await handleExit();
      }
      return;
    }
  
    addProfileFormURL(String url) async {
      if (globalState.navigatorKey.currentState?.canPop() ?? false) {
        globalState.navigatorKey.currentState?.popUntil((route) => route.isFirst);
      }
      toProfiles();
      final commonScaffoldState = globalState.homeScaffoldKey.currentState;
      if (commonScaffoldState?.mounted != true) return;
      
      // 检查是否为代理链接
      if (_isProxyLink(url)) {
        await _addProfileFromProxyLink(url);
        return;
      }
      
      final profile = await commonScaffoldState?.loadingRun<Profile>(
        () async {
          return await Profile.normal(
            url: url,
          ).update();
        },
        title: "${appLocalizations.add}${appLocalizations.profile}",
      );
      if (profile != null) {
        await addProfile(profile);
      }
    }
  
    /// 检查是否为代理链接
    bool _isProxyLink(String link) {
      return link.startsWith('vmess://') ||
             link.startsWith('v2ray://') ||
             link.startsWith('v2://') ||
             link.startsWith('ssr://') ||
             link.startsWith('ss://') ||
             link.startsWith('vless://') ||
             link.startsWith('trojan://');
    }
  
    /// 从代理链接添加配置文件
    Future<void> _addProfileFromProxyLink(String link) async {
      final commonScaffoldState = globalState.homeScaffoldKey.currentState;
      if (commonScaffoldState?.mounted != true) return;
      
      final profile = await commonScaffoldState?.loadingRun<Profile>(
        () async {
          // 解析代理链接
          final proxies = LinkParser.parseProxyLinks(link);
          if (proxies.isEmpty) {
            throw Exception('无法解析代理链接');
          }
          
          // 生成 Clash 配置
          final clashConfig = LinkParser.generateClashConfig(proxies);
          final configString = json.encode(clashConfig);
          
          // 创建配置文件
          final label = proxies.length == 1 
              ? proxies.first['name'] as String
              : '导入的节点 (${proxies.length}个)';
              
          return await Profile.normal(label: label).saveFileWithString(configString);
        },
        title: "${appLocalizations.add}${appLocalizations.profile}",
      );
      
      if (profile != null) {
        await addProfile(profile);
      }
    }
  
    /// 从解析好的代理链接列表添加配置文件
    Future<void> addProfileFromProxyLinks(List<String> proxyLinks) async {
      if (globalState.navigatorKey.currentState?.canPop() ?? false) {
        globalState.navigatorKey.currentState?.popUntil((route) => route.isFirst);
      }
      toProfiles();
      
      final commonScaffoldState = globalState.homeScaffoldKey.currentState;
      if (commonScaffoldState?.mounted != true) return;
      
      final profile = await commonScaffoldState?.loadingRun<Profile>(
        () async {
          // 解析所有代理链接
          final allProxies = <Map<String, dynamic>>[];
          final existingNames = <String>{};
          
          for (final link in proxyLinks) {
            final proxy = LinkParser.parseProxyLink(link);
            if (proxy != null) {
              // 确保节点名称唯一
              final originalName = proxy['name'] as String;
              final uniqueName = LinkParser.generateUniqueName(originalName, existingNames);
              proxy['name'] = uniqueName;
              existingNames.add(uniqueName);
              
              allProxies.add(proxy);
            }
          }
          
          if (allProxies.isEmpty) {
            throw Exception('无法解析任何代理链接');
          }
          
          // 生成 Clash 配置
          final clashConfig = LinkParser.generateClashConfig(allProxies);
          final configString = json.encode(clashConfig);
          
          // 创建配置文件
          final label = allProxies.length == 1 
              ? allProxies.first['name'] as String
              : '导入的节点 (${allProxies.length}个)';
              
          return await Profile.normal(label: label).saveFileWithString(configString);
        },
        title: "${appLocalizations.add}${appLocalizations.profile}",
      );
      
      if (profile != null) {
        await addProfile(profile);
      }
    }
  
    /// 从剪贴板导入代理链接
    Future<void> addProfileFromClipboard() async {
      try {
        final data = await Clipboard.getData('text/plain');
        final text = data?.text?.trim();
        
        if (text == null || text.isEmpty) {
          globalState.showMessage(
            title: "错误",
            message: const TextSpan(text: "剪贴板为空"),
          );
          return;
        }
        
        // 使用ClipboardManager的解析逻辑来处理多种分隔符
        final clipboardManager = ClipboardManager.instance;
        final proxyLinks = clipboardManager.parseProxyLinks(text);
        
        if (proxyLinks.isEmpty) {
          // 尝试作为订阅链接处理
          if (Uri.tryParse(text) != null) {
            await addProfileFormURL(text);
          } else {
            globalState.showMessage(
              title: "错误", 
              message: const TextSpan(text: "剪贴板中没有找到有效的代理链接或订阅链接"),
            );
          }
          return;
        }
        
        // 记录到导入历史（手动导入）
        ClipboardManager.instance.addToHistory(text, proxyLinks.length, true);
        
        // 处理代理链接 - 直接调用新的方法
        await addProfileFromProxyLinks(proxyLinks);
        
      } catch (e) {
        globalState.showMessage(
          title: "错误",
          message: TextSpan(text: "导入失败: $e"),
        );
      }
    }
  
    addProfileFormFile() async {
      final platformFile = await globalState.safeRun(picker.pickerFile);
      final bytes = platformFile?.bytes;
      if (bytes == null) {
        return null;
      }
      if (!context.mounted) return;
      globalState.navigatorKey.currentState?.popUntil((route) => route.isFirst);
      toProfiles();
      final commonScaffoldState = globalState.homeScaffoldKey.currentState;
      if (commonScaffoldState?.mounted != true) return;
      final profile = await commonScaffoldState?.loadingRun<Profile?>(
        () async {
          await Future.delayed(const Duration(milliseconds: 300));
          return await Profile.normal(label: platformFile?.name).saveFile(bytes);
        },
        title: "${appLocalizations.add}${appLocalizations.profile}",
      );
      if (profile != null) {
        await addProfile(profile);
      }
    }
  
    addProfileFormQrCode() async {
      final url = await globalState.safeRun(picker.pickerConfigQRCode);
      if (url == null) return;
      addProfileFormURL(url);
    }
  
    updateViewSize(Size size) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        _ref.read(viewSizeProvider.notifier).value = size;
      });
    }
  
    setProvider(ExternalProvider? provider) {
      _ref.read(providersProvider.notifier).setProvider(provider);
    }
  
    List<Proxy> _sortOfName(List<Proxy> proxies) {
      return List.of(proxies)
        ..sort(
          (a, b) => utils.sortByChar(
            utils.getPinyin(a.name),
            utils.getPinyin(b.name),
          ),
        );
    }
  
    List<Proxy> _sortOfDelay({
      required List<Proxy> proxies,
      String? testUrl,
    }) {
      return List.of(proxies)
        ..sort(
          (a, b) {
            final aDelay =
                _ref.read(getDelayProvider(proxyName: a.name, testUrl: testUrl));
            final bDelay =
                _ref.read(getDelayProvider(proxyName: b.name, testUrl: testUrl));
            if (aDelay == null && bDelay == null) {
              return 0;
            }
            if (aDelay == null || aDelay == -1) {
              return 1;
            }
            if (bDelay == null || bDelay == -1) {
              return -1;
            }
            return aDelay.compareTo(bDelay);
          },
        );
    }
  
    List<Proxy> getSortProxies(List<Proxy> proxies, [String? url]) {
      return switch (_ref.read(proxiesStyleSettingProvider).sortType) {
        ProxiesSortType.none => proxies,
        ProxiesSortType.delay => _sortOfDelay(
            proxies: proxies,
            testUrl: url,
          ),
        ProxiesSortType.name => _sortOfName(proxies),
      };
    }
  
    clearEffect(String profileId) async {
      final profilePath = await appPath.getProfilePath(profileId);
      final providersPath = await appPath.getProvidersPath(profileId);
      return await Isolate.run(() async {
        if (profilePath != null) {
          final profileFile = File(profilePath);
          final isExists = await profileFile.exists();
          if (isExists) {
            profileFile.delete(recursive: true);
          }
        }
        if (providersPath != null) {
          final providersFileDir = File(providersPath);
          final isExists = await providersFileDir.exists();
          if (isExists) {
            providersFileDir.delete(recursive: true);
          }
        }
      });
    }
  
    updateTun() {
      _ref.read(patchClashConfigProvider.notifier).updateState(
            (state) => state.copyWith.tun(enable: !state.tun.enable),
          );
    }
  
    updateSystemProxy() {
      _ref.read(networkSettingProvider.notifier).updateState(
            (state) => state.copyWith(
              systemProxy: !state.systemProxy,
            ),
          );
    }
  
    updateStart() {
      updateStatus(!_ref.read(runTimeProvider.notifier).isStart);
    }
  
    updateCurrentSelectedMap(String groupName, String proxyName) {
      final currentProfile = _ref.read(currentProfileProvider);
      if (currentProfile != null &&
          currentProfile.selectedMap[groupName] != proxyName) {
        final SelectedMap selectedMap = Map.from(
          currentProfile.selectedMap,
        )..[groupName] = proxyName;
        _ref.read(profilesProvider.notifier).setProfile(
              currentProfile.copyWith(
                selectedMap: selectedMap,
              ),
            );
      }
    }
  
    updateCurrentUnfoldSet(Set<String> value) {
      final currentProfile = _ref.read(currentProfileProvider);
      if (currentProfile == null) {
        return;
      }
      _ref.read(profilesProvider.notifier).setProfile(
            currentProfile.copyWith(
              unfoldSet: value,
            ),
          );
    }
  
    changeMode(Mode mode) {
      _ref.read(patchClashConfigProvider.notifier).updateState(
            (state) => state.copyWith(mode: mode),
          );
      if (mode == Mode.global) {
        updateCurrentGroupName(GroupName.GLOBAL.name);
      }
      addCheckIpNumDebounce();
    }
  
    updateAutoLaunch() {
      _ref.read(appSettingProvider.notifier).updateState(
            (state) => state.copyWith(
              autoLaunch: !state.autoLaunch,
            ),
          );
    }
  
    updateVisible() async {
      final visible = await window?.isVisible;
      if (visible != null && !visible) {
        window?.show();
      } else {
        window?.hide();
      }
    }
  
    updateMode() {
      _ref.read(patchClashConfigProvider.notifier).updateState(
        (state) {
          final index = Mode.values.indexWhere((item) => item == state.mode);
          if (index == -1) {
            return null;
          }
          final nextIndex = index + 1 > Mode.values.length - 1 ? 0 : index + 1;
          return state.copyWith(
            mode: Mode.values[nextIndex],
          );
        },
      );
    }
  
    handleAddOrUpdate(WidgetRef ref, [Rule? rule]) async {
      final res = await globalState.showCommonDialog<Rule>(
        child: AddRuleDialog(
          rule: rule,
          snippet: ref.read(
            profileOverrideStateProvider.select(
              (state) => state.snippet!,
            ),
          ),
        ),
      );
      if (res == null) {
        return;
      }
      ref.read(profileOverrideStateProvider.notifier).updateState(
        (state) {
          final model = state.copyWith.overrideData!(
            rule: state.overrideData!.rule.updateRules(
              (rules) {
                final index = rules.indexWhere((item) => item.id == res.id);
                if (index == -1) {
                  return List.from([res, ...rules]);
                }
                return List.from(rules)..[index] = res;
              },
            ),
          );
          return model;
        },
      );
    }
  
    Future<bool> exportLogs() async {
      final logsRaw = _ref.read(logsProvider).list.map(
            (item) => item.toString(),
          );
      final data = await Isolate.run<List<int>>(() async {
        final logsRawString = logsRaw.join("\n");
        return utf8.encode(logsRawString);
      });
      return await picker.saveFile(
            utils.logFile,
            Uint8List.fromList(data),
          ) !=
          null;
    }
  
    Future<List<int>> backupData() async {
      final homeDirPath = await appPath.homeDirPath;
      final profilesPath = await appPath.profilesPath;
      final configJson = globalState.config.toJson();
      return Isolate.run<List<int>>(() async {
        final archive = Archive();
        archive.add("config.json", configJson);
        await archive.addDirectoryToArchive(profilesPath, homeDirPath);
        final zipEncoder = ZipEncoder();
        return zipEncoder.encode(archive) ?? [];
      });
    }
  
    updateTray([bool focus = false]) async {
      tray.update(
        trayState: _ref.read(trayStateProvider),
      );
    }
  
    recoveryData(
      List<int> data,
      RecoveryOption recoveryOption,
    ) async {
      final archive = await Isolate.run<Archive>(() {
        final zipDecoder = ZipDecoder();
        return zipDecoder.decodeBytes(data);
      });
      final homeDirPath = await appPath.homeDirPath;
      final configs =
          archive.files.where((item) => item.name.endsWith(".json")).toList();
      final profiles =
          archive.files.where((item) => !item.name.endsWith(".json"));
      final configIndex =
          configs.indexWhere((config) => config.name == "config.json");
      if (configIndex == -1) throw "invalid backup file";
      final configFile = configs[configIndex];
      var tempConfig = Config.compatibleFromJson(
        json.decode(
          utf8.decode(configFile.content),
        ),
      );
      for (final profile in profiles) {
        final filePath = join(homeDirPath, profile.name);
        final file = File(filePath);
        await file.create(recursive: true);
        await file.writeAsBytes(profile.content);
      }
      final clashConfigIndex =
          configs.indexWhere((config) => config.name == "clashConfig.json");
      if (clashConfigIndex != -1) {
        final clashConfigFile = configs[clashConfigIndex];
        tempConfig = tempConfig.copyWith(
          patchClashConfig: ClashConfig.fromJson(
            json.decode(
              utf8.decode(
                clashConfigFile.content,
              ),
            ),
          ),
        );
      }
      _recovery(
        tempConfig,
        recoveryOption,
      );
    }
  
    _recovery(Config config, RecoveryOption recoveryOption) {
      final recoveryStrategy = _ref.read(appSettingProvider.select(
        (state) => state.recoveryStrategy,
      ));
      final profiles = config.profiles;
      if (recoveryStrategy == RecoveryStrategy.override) {
        _ref.read(profilesProvider.notifier).value = profiles;
      } else {
        for (final profile in profiles) {
          _ref.read(profilesProvider.notifier).setProfile(
                profile,
              );
        }
      }
      final onlyProfiles = recoveryOption == RecoveryOption.onlyProfiles;
      if (!onlyProfiles) {
        _ref.read(patchClashConfigProvider.notifier).value =
            config.patchClashConfig;
        _ref.read(appSettingProvider.notifier).value = config.appSetting;
        _ref.read(currentProfileIdProvider.notifier).value =
            config.currentProfileId;
        _ref.read(appDAVSettingProvider.notifier).value = config.dav;
        _ref.read(themeSettingProvider.notifier).value = config.themeProps;
        _ref.read(windowSettingProvider.notifier).value = config.windowProps;
        _ref.read(vpnSettingProvider.notifier).value = config.vpnProps;
        _ref.read(proxiesStyleSettingProvider.notifier).value =
            config.proxiesStyle;
        _ref.read(overrideDnsProvider.notifier).value = config.overrideDns;
        _ref.read(networkSettingProvider.notifier).value = config.networkProps;
        _ref.read(hotKeyActionsProvider.notifier).value = config.hotKeyActions;
      }
      final currentProfile = _ref.read(currentProfileProvider);
      if (currentProfile == null) {
        _ref.read(currentProfileIdProvider.notifier).value = profiles.first.id;
      }
    }
  }
  