content: import 'package:dynamic_color/dynamic_color.dart';
  import 'package:fl_clash/common/common.dart';
  import 'package:fl_clash/enum/enum.dart';
  import 'package:fl_clash/models/models.dart';
  import 'package:fl_clash/state.dart';
  import 'package:flutter/material.dart';
  import 'package:flutter_riverpod/flutter_riverpod.dart';
  import 'package:riverpod_annotation/riverpod_annotation.dart';
  
  import 'app.dart';
  import 'config.dart';
  
  part 'generated/state.g.dart';
  
  @riverpod
  GroupsState currentGroupsState(Ref ref) {
    final mode =
        ref.watch(patchClashConfigProvider.select((state) => state.mode));
    final groups = ref.watch(groupsProvider);
    return GroupsState(
      value: switch (mode) {
        Mode.direct => [],
        Mode.global => groups.toList(),
        Mode.rule => groups
            .where((item) => item.hidden == false)
            .where((element) => element.name != GroupName.GLOBAL.name)
            .toList(),
      },
    );
  }
  
  @riverpod
  NavigationItemsState navigationsState(Ref ref) {
    final openLogs = ref.watch(appSettingProvider).openLogs;
    final hasProxies = ref.watch(
        currentGroupsStateProvider.select((state) => state.value.isNotEmpty));
    return NavigationItemsState(
      value: navigation.getItems(
        openLogs: openLogs,
        hasProxies: hasProxies,
      ),
    );
  }
  
  @riverpod
  NavigationItemsState currentNavigationsState(Ref ref) {
    final viewWidth = ref.watch(viewWidthProvider);
    final navigationItemsState = ref.watch(navigationsStateProvider);
    final navigationItemMode = switch (viewWidth <= maxMobileWidth) {
      true => NavigationItemMode.mobile,
      false => NavigationItemMode.desktop,
    };
    return NavigationItemsState(
      value: navigationItemsState.value
          .where(
            (element) => element.modes.contains(navigationItemMode),
          )
          .toList(),
    );
  }
  
  @riverpod
  CoreState coreState(Ref ref) {
    final vpnProps = ref.watch(vpnSettingProvider);
    final currentProfile = ref.watch(currentProfileProvider);
    final onlyStatisticsProxy = ref.watch(appSettingProvider).onlyStatisticsProxy;
    return CoreState(
      vpnProps: vpnProps,
      onlyStatisticsProxy: onlyStatisticsProxy,
      currentProfileName: currentProfile?.label ?? currentProfile?.id ?? "",
    );
  }
  
  @riverpod
  ClashConfigState clashConfigState(Ref ref) {
    final clashConfig = ref.watch(patchClashConfigProvider);
    final overrideDns = ref.watch(overrideDnsProvider);
    final overrideData =
        ref.watch(currentProfileProvider.select((state) => state?.overrideData));
    final routeMode =
        ref.watch(networkSettingProvider.select((state) => state.routeMode));
    return ClashConfigState(
      overrideDns: overrideDns,
      clashConfig: clashConfig,
      overrideData: overrideData ?? OverrideData(),
      routeMode: routeMode,
    );
  }
  
  @riverpod
  ProxyState proxyState(Ref ref) {
    final isStart = ref.watch(runTimeProvider.select((state) => state != null));
    final vm2 = ref.watch(networkSettingProvider.select(
      (state) => VM2(
        a: state.systemProxy,
        b: state.bypassDomain,
      ),
    ));
    final mixedPort = ref.watch(
      patchClashConfigProvider.select((state) => state.mixedPort),
    );
    return ProxyState(
      isStart: isStart,
      systemProxy: vm2.a,
      bassDomain: vm2.b,
      port: mixedPort,
    );
  }
  
  @riverpod
  TrayState trayState(Ref ref) {
    final isStart = ref.watch(runTimeProvider.select((state) => state != null));
    final networkProps = ref.watch(networkSettingProvider);
    final clashConfig = ref.watch(
      patchClashConfigProvider,
    );
    final appSetting = ref.watch(
      appSettingProvider,
    );
    final groups = ref.watch(
      groupsProvider,
    );
    final brightness = ref.watch(
      appBrightnessProvider,
    );
  
    final selectedMap = ref.watch(selectedMapProvider);
  
    return TrayState(
      mode: clashConfig.mode,
      port: clashConfig.mixedPort,
      autoLaunch: appSetting.autoLaunch,
      systemProxy: networkProps.systemProxy,
      tunEnable: clashConfig.tun.enable,
      isStart: isStart,
      locale: appSetting.locale,
      brightness: brightness,
      groups: groups,
      selectedMap: selectedMap,
    );
  }
  
  @riverpod
  VpnState vpnState(Ref ref) {
    final vpnProps = ref.watch(vpnSettingProvider);
    final stack = ref.watch(
      patchClashConfigProvider.select((state) => state.tun.stack),
    );
  
    return VpnState(
      stack: stack,
      vpnProps: vpnProps,
    );
  }
  
  @riverpod
  HomeState homeState(Ref ref) {
    final pageLabel = ref.watch(currentPageLabelProvider);
    final navigationItems = ref.watch(currentNavigationsStateProvider).value;
    final viewMode = ref.watch(viewModeProvider);
    final locale = ref.watch(appSettingProvider).locale;
    return HomeState(
      pageLabel: pageLabel,
      navigationItems: navigationItems,
      viewMode: viewMode,
      locale: locale,
    );
  }
  
  @riverpod
  DashboardState dashboardState(Ref ref) {
    final dashboardWidgets =
        ref.watch(appSettingProvider.select((state) => state.dashboardWidgets));
    final viewWidth = ref.watch(viewWidthProvider);
    return DashboardState(
      dashboardWidgets: dashboardWidgets,
      viewWidth: viewWidth,
    );
  }
  
  @riverpod
  ProxiesActionsState proxiesActionsState(Ref ref) {
    final pageLabel = ref.watch(currentPageLabelProvider);
    final hasProviders = ref.watch(providersProvider.select(
      (state) => state.isNotEmpty,
    ));
    final type = ref.watch(proxiesStyleSettingProvider.select(
      (state) => state.type,
    ));
    return ProxiesActionsState(
      pageLabel: pageLabel,
      hasProviders: hasProviders,
      type: type,
    );
  }
  
  @riverpod
  StartButtonSelectorState startButtonSelectorState(Ref ref) {
    final isInit = ref.watch(initProvider);
    final hasProfile =
        ref.watch(profilesProvider.select((state) => state.isNotEmpty));
    return StartButtonSelectorState(
      isInit: isInit,
      hasProfile: hasProfile,
    );
  }
  
  @riverpod
  ProfilesSelectorState profilesSelectorState(Ref ref) {
    final currentProfileId = ref.watch(currentProfileIdProvider);
    final profiles = ref.watch(profilesProvider);
    final columns = ref.watch(
        viewWidthProvider.select((state) => utils.getProfilesColumns(state)));
    return ProfilesSelectorState(
      profiles: profiles,
      currentProfileId: currentProfileId,
      columns: columns,
    );
  }
  
  @riverpod
  ProxiesListSelectorState proxiesListSelectorState(Ref ref) {
    final groupNames = ref.watch(currentGroupsStateProvider.select((state) {
      return state.value.map((e) => e.name).toList();
    }));
    final currentUnfoldSet = ref.watch(unfoldSetProvider);
    final proxiesStyle = ref.watch(proxiesStyleSettingProvider);
    final sortNum = ref.watch(sortNumProvider);
    final columns = ref.watch(getProxiesColumnsProvider);
    return ProxiesListSelectorState(
      groupNames: groupNames,
      currentUnfoldSet: currentUnfoldSet,
      proxiesSortType: proxiesStyle.sortType,
      proxyCardType: proxiesStyle.cardType,
      sortNum: sortNum,
      columns: columns,
    );
  }
  
  @riverpod
  ProxiesSelectorState proxiesSelectorState(Ref ref) {
    final groupNames = ref.watch(
      currentGroupsStateProvider.select(
        (state) {
          return state.value.map((e) => e.name).toList();
        },
      ),
    );
    final currentGroupName = ref.watch(currentProfileProvider.select(
      (state) => state?.currentGroupName,
    ));
    return ProxiesSelectorState(
      groupNames: groupNames,
      currentGroupName: currentGroupName,
    );
  }
  
  @riverpod
  GroupNamesState groupNamesState(Ref ref) {
    return GroupNamesState(
      groupNames: ref.watch(
        currentGroupsStateProvider.select(
          (state) {
            return state.value.map((e) => e.name).toList();
          },
        ),
      ),
    );
  }
  
  @riverpod
  ProxyGroupSelectorState proxyGroupSelectorState(Ref ref, String groupName) {
    final proxiesStyle = ref.watch(
      proxiesStyleSettingProvider,
    );
    final group = ref.watch(
      currentGroupsStateProvider.select(
        (state) => state.value.getGroup(groupName),
      ),
    );
    final sortNum = ref.watch(sortNumProvider);
    final columns = ref.watch(getProxiesColumnsProvider);
    return ProxyGroupSelectorState(
      testUrl: group?.testUrl,
      proxiesSortType: proxiesStyle.sortType,
      proxyCardType: proxiesStyle.cardType,
      sortNum: sortNum,
      groupType: group?.type ?? GroupType.Selector,
      proxies: group?.all ?? [],
      columns: columns,
    );
  }
  
  @riverpod
  PackageListSelectorState packageListSelectorState(Ref ref) {
    final packages = ref.watch(packagesProvider);
    final accessControl =
        ref.watch(vpnSettingProvider.select((state) => state.accessControl));
    return PackageListSelectorState(
      packages: packages,
      accessControl: accessControl,
    );
  }
  
  @riverpod
  MoreToolsSelectorState moreToolsSelectorState(Ref ref) {
    final viewMode = ref.watch(viewModeProvider);
    final navigationItems = ref.watch(navigationsStateProvider.select((state) {
      return state.value.where((element) {
        final isMore = element.modes.contains(NavigationItemMode.more);
        final isDesktop = element.modes.contains(NavigationItemMode.desktop);
        if (isMore && !isDesktop) return true;
        if (viewMode != ViewMode.mobile || !isMore) {
          return false;
        }
        return true;
      }).toList();
    }));
  
    return MoreToolsSelectorState(navigationItems: navigationItems);
  }
  
  @riverpod
  bool isCurrentPage(
    Ref ref,
    PageLabel pageLabel, {
    bool Function(PageLabel pageLabel, ViewMode viewMode)? handler,
  }) {
    final currentPageLabel = ref.watch(currentPageLabelProvider);
    if (pageLabel == currentPageLabel) {
      return true;
    }
    if (handler != null) {
      final viewMode = ref.watch(viewModeProvider);
      return handler(currentPageLabel, viewMode);
    }
    return false;
  }
  
  @riverpod
  String getRealTestUrl(Ref ref, [String? testUrl]) {
    final currentTestUrl = ref.watch(appSettingProvider).testUrl;
    return testUrl.getSafeValue(currentTestUrl);
  }
  
  @riverpod
  int? getDelay(
    Ref ref, {
    required String proxyName,
    String? testUrl,
  }) {
    final currentTestUrl = ref.watch(getRealTestUrlProvider(testUrl));
    final proxyCardState = ref.watch(
      getProxyCardStateProvider(
        proxyName,
      ),
    );
    final delay = ref.watch(
      delayDataSourceProvider.select(
        (state) {
          final delayMap =
              state[proxyCardState.testUrl.getSafeValue(currentTestUrl)];
          return delayMap?[proxyCardState.proxyName];
        },
      ),
    );
    return delay;
  }
  
  @riverpod
  SelectedMap selectedMap(Ref ref) {
    final selectedMap = ref.watch(
      currentProfileProvider.select((state) => state?.selectedMap ?? {}),
    );
    return selectedMap;
  }
  
  @riverpod
  Set<String> unfoldSet(Ref ref) {
    final unfoldSet = ref.watch(
      currentProfileProvider.select((state) => state?.unfoldSet ?? {}),
    );
    return unfoldSet;
  }
  
  @riverpod
  HotKeyAction getHotKeyAction(Ref ref, HotAction hotAction) {
    return ref.watch(
      hotKeyActionsProvider.select(
        (state) {
          final index = state.indexWhere((item) => item.action == hotAction);
          return index != -1
              ? state[index]
              : HotKeyAction(
                  action: hotAction,
                );
        },
      ),
    );
  }
  
  @riverpod
  Profile? currentProfile(Ref ref) {
    final profileId = ref.watch(currentProfileIdProvider);
    return ref
        .watch(profilesProvider.select((state) => state.getProfile(profileId)));
  }
  
  @riverpod
  int getProxiesColumns(Ref ref) {
    final viewWidth = ref.watch(viewWidthProvider);
    final proxiesLayout =
        ref.watch(proxiesStyleSettingProvider.select((state) => state.layout));
    return utils.getProxiesColumns(viewWidth, proxiesLayout);
  }
  
  ProxyCardState _getProxyCardState(
    List<Group> groups,
    SelectedMap selectedMap,
    ProxyCardState proxyDelayState,
  ) {
    if (proxyDelayState.proxyName.isEmpty) return proxyDelayState;
    final index =
        groups.indexWhere((element) => element.name == proxyDelayState.proxyName);
    if (index == -1) return proxyDelayState;
    final group = groups[index];
    final currentSelectedName = group
        .getCurrentSelectedName(selectedMap[proxyDelayState.proxyName] ?? '');
    if (currentSelectedName.isEmpty) {
      return proxyDelayState;
    }
    return _getProxyCardState(
      groups,
      selectedMap,
      proxyDelayState.copyWith(
        proxyName: currentSelectedName,
        testUrl: group.testUrl,
      ),
    );
  }
  
  @riverpod
  ProxyCardState getProxyCardState(Ref ref, String proxyName) {
    final groups = ref.watch(groupsProvider);
    final selectedMap = ref.watch(selectedMapProvider);
    return _getProxyCardState(
        groups, selectedMap, ProxyCardState(proxyName: proxyName));
  }
  
  @riverpod
  String? getProxyName(Ref ref, String groupName) {
    final proxyName =
        ref.watch(selectedMapProvider.select((state) => state[groupName]));
    return proxyName;
  }
  
  @riverpod
  String? getSelectedProxyName(Ref ref, String groupName) {
    final proxyName = ref.watch(getProxyNameProvider(groupName));
    final group = ref.watch(
      groupsProvider.select(
        (state) => state.getGroup(groupName),
      ),
    );
    return group?.getCurrentSelectedName(proxyName ?? '');
  }
  
  @riverpod
  String getProxyDesc(Ref ref, Proxy proxy) {
    final groupTypeNamesList = GroupType.values.map((e) => e.name).toList();
    if (!groupTypeNamesList.contains(proxy.type)) {
      return proxy.type;
    } else {
      final groups = ref.watch(groupsProvider);
      final index = groups.indexWhere((element) => element.name == proxy.name);
      if (index == -1) return proxy.type;
      final state = ref.watch(getProxyCardStateProvider(proxy.name));
      return "${proxy.type}(${state.proxyName.isNotEmpty ? state.proxyName : '*'})";
    }
  }
  
  @riverpod
  class ProfileOverrideState extends _$ProfileOverrideState {
    @override
    ProfileOverrideStateModel build() {
      return ProfileOverrideStateModel(
        isEdit: false,
        selectedRules: {},
      );
    }
  
    updateState(
      ProfileOverrideStateModel? Function(ProfileOverrideStateModel state)
          builder,
    ) {
      final value = builder(state);
      if (value == null) {
        return;
      }
      state = value;
    }
  }
  
  @riverpod
  OverrideData? getProfileOverrideData(Ref ref, String profileId) {
    return ref.watch(
      profilesProvider.select(
        (state) => state.getProfile(profileId)?.overrideData,
      ),
    );
  }
  
  @riverpod
  VM2? layoutChange(Ref ref) {
    final viewWidth = ref.watch(viewWidthProvider);
    final textScale =
        ref.watch(themeSettingProvider.select((state) => state.textScale));
    return VM2(
      a: viewWidth,
      b: textScale,
    );
  }
  
  @riverpod
  ColorScheme genColorScheme(
    Ref ref,
    Brightness brightness, {
    Color? color,
    bool ignoreConfig = false,
  }) {
    final vm2 = ref.watch(
      themeSettingProvider.select(
        (state) => VM2(
          a: state.primaryColor,
          b: state.schemeVariant,
        ),
      ),
    );
    if (color == null && (ignoreConfig == true || vm2.a == null)) {
      // if (globalState.corePalette != null) {
      //   return globalState.corePalette!.toColorScheme(brightness: brightness);
      // }
      return ColorScheme.fromSeed(
        seedColor: globalState.corePalette
                ?.toColorScheme(brightness: brightness)
                .primary ??
            globalState.accentColor,
        brightness: brightness,
        dynamicSchemeVariant: vm2.b,
      );
    }
    return ColorScheme.fromSeed(
      seedColor: color ?? Color(vm2.a!),
      brightness: brightness,
      dynamicSchemeVariant: vm2.b,
    );
  }
  