import 'package:collection/collection.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

import 'app.dart';
import 'clash_config.dart';
import 'common.dart';
import 'config.dart';
import 'core.dart';
import 'profile.dart';

part 'generated/state.freezed.dart';
part 'generated/state.g.dart';

@freezed
abstract class VM<A> with _$VM<A> {
  const factory VM(A a) = _VM;
}

@freezed
abstract class VM2<A, B> with _$VM2<A, B> {
  const factory VM2(A a, B b) = _VM2;
}

@freezed
abstract class VM3<A, B, C> with _$VM3<A, B, C> {
  const factory VM3(A a, B b, C c) = _VM3;
}

@freezed
abstract class VM4<A, B, C, D> with _$VM4<A, B, C, D> {
  const factory VM4(A a, B b, C c, D d) = _VM4;
}

@freezed
abstract class VM5<A, B, C, D, E> with _$VM5<A, B, C, D, E> {
  const factory VM5(A a, B b, C c, D d, E e) = _VM5;
}

@freezed
abstract class ActivateState with _$ActivateState {
  const factory ActivateState({required bool active}) = _ActivateState;
}

@freezed
abstract class InitState with _$InitState {
  const factory InitState({
    required Config config,
    required List<Profile> profiles,
  }) = _InitState;
}

@freezed
abstract class CommonMessage with _$CommonMessage {
  const factory CommonMessage({
    required String id,
    required String text,
    @Default(Duration(seconds: 3)) Duration duration,
    MessageActionState? actionState,
  }) = _CommonMessage;
}

@freezed
abstract class MessageActionState with _$MessageActionState {
  const factory MessageActionState({
    required String actionText,
    required VoidCallback action,
  }) = _MessageActionState;
}

@freezed
abstract class AppBarState with _$AppBarState {
  const factory AppBarState({
    @Default([]) List<Widget> actions,
    AppBarSearchState? searchState,
    AppBarEditState? editState,
  }) = _AppBarState;
}

@freezed
abstract class AppBarSearchState with _$AppBarSearchState {
  const factory AppBarSearchState({
    required Function(String) onSearch,
    @Default(true) bool autoAddSearch,
    @Default(null) String? query,
  }) = _AppBarSearchState;
}

@freezed
abstract class AppBarEditState with _$AppBarEditState {
  const factory AppBarEditState({
    @Default(0) int editCount,
    required Function() onExit,
  }) = _AppBarEditState;
}

@freezed
abstract class StartButtonState with _$StartButtonState {
  const factory StartButtonState({
    required bool isPreload,
    required bool hasProfile,
  }) = _StartButtonState;
}

@freezed
abstract class ProfilesState with _$ProfilesState {
  const factory ProfilesState({
    required List<Profile> profiles,
    required int? currentProfileId,
    required int columns,
  }) = _ProfilesState;
}

@freezed
abstract class NetworkDetectionState with _$NetworkDetectionState {
  const factory NetworkDetectionState({
    required bool isLoading,
    required IpInfo? ipInfo,
  }) = _NetworkDetectionState;
}

@freezed
abstract class TrayState with _$TrayState {
  const factory TrayState({
    required Mode mode,
    required int port,
    required bool autoLaunch,
    required bool systemProxy,
    required bool tunEnable,
    required bool isStart,
    required String? locale,
    required Brightness? brightness,
    required List<Group> groups,
    required Map<String, String> selectedMap,
    required bool showTrayTitle,
  }) = _TrayState;
}

@freezed
abstract class TrayTitleState with _$TrayTitleState {
  const factory TrayTitleState({
    required Traffic traffic,
    required bool showTrayTitle,
  }) = _TrayTitleState;
}

@freezed
abstract class NavigationState with _$NavigationState {
  const factory NavigationState({
    required PageLabel pageLabel,
    required List<NavigationItem> navigationItems,
    required ViewMode viewMode,
    required String? locale,
    required int currentIndex,
  }) = _NavigationState;
}

@freezed
abstract class GroupsState with _$GroupsState {
  const factory GroupsState({required List<Group> value}) = _GroupsState;
}

@freezed
abstract class NavigationItemsState with _$NavigationItemsState {
  const factory NavigationItemsState({required List<NavigationItem> value}) =
      _NavigationItemsState;
}

@freezed
abstract class ProxiesListState with _$ProxiesListState {
  const factory ProxiesListState({
    required List<Group> groups,
    required Set<String> currentUnfoldSet,
    required ProxyCardType proxyCardType,
    required int columns,
  }) = _ProxiesListState;
}

@freezed
abstract class ProxiesTabState with _$ProxiesTabState {
  const factory ProxiesTabState({
    required List<Group> groups,
    required String? currentGroupName,
    required ProxyCardType proxyCardType,
    required int columns,
  }) = _ProxiesTabState;
}

@freezed
abstract class ProxyGroupSelectorState with _$ProxyGroupSelectorState {
  const factory ProxyGroupSelectorState({
    required String? testUrl,
    required ProxiesSortType proxiesSortType,
    required ProxyCardType proxyCardType,
    required num sortNum,
    required GroupType groupType,
    required List<Proxy> proxies,
    required int columns,
  }) = _ProxyGroupSelectorState;
}

@freezed
abstract class MoreToolsSelectorState with _$MoreToolsSelectorState {
  const factory MoreToolsSelectorState({
    required List<NavigationItem> navigationItems,
  }) = _MoreToolsSelectorState;
}

@freezed
abstract class PackageListSelectorState with _$PackageListSelectorState {
  const factory PackageListSelectorState({
    required List<Package> packages,
    required AccessControlProps accessControlProps,
  }) = _PackageListSelectorState;
}

extension PackageListSelectorStateExt on PackageListSelectorState {
  List<Package> get list {
    final isFilterSystemApp = accessControlProps.isFilterSystemApp;
    final isFilterNonInternetApp = accessControlProps.isFilterNonInternetApp;
    return packages
        .where(
          (item) =>
              (isFilterSystemApp ? item.system == false : true) &&
              (isFilterNonInternetApp ? item.internet == true : true),
        )
        .toList();
  }

  List<Package> getSortList(List<String> selectedList) {
    final sort = accessControlProps.sort;

    return list.sorted((a, b) {
      final isSelectA = selectedList.contains(a.packageName);
      final isSelectB = selectedList.contains(b.packageName);

      if (isSelectA != isSelectB) {
        return isSelectA ? -1 : 1;
      }
      return switch (sort) {
        AccessSortType.none => 0,
        AccessSortType.name => a.label.compareTo(b.label),
        AccessSortType.time => b.lastUpdateTime.compareTo(a.lastUpdateTime),
      };
    });
  }
}

@freezed
abstract class ProxiesListHeaderSelectorState
    with _$ProxiesListHeaderSelectorState {
  const factory ProxiesListHeaderSelectorState({
    required double offset,
    required int currentIndex,
  }) = _ProxiesListHeaderSelectorState;
}

@freezed
abstract class ProxiesActionsState with _$ProxiesActionsState {
  const factory ProxiesActionsState({
    required PageLabel pageLabel,
    required ProxiesType type,
    required bool hasProviders,
  }) = _ProxiesActionsState;
}

@freezed
abstract class ProxyState with _$ProxyState {
  const factory ProxyState({
    required bool isStart,
    required bool systemProxy,
    required List<String> bassDomain,
    required int port,
  }) = _ProxyState;
}

@freezed
abstract class DashboardState with _$DashboardState {
  const factory DashboardState({
    required List<DashboardWidget> dashboardWidgets,
    required double contentWidth,
  }) = _DashboardState;
}

@freezed
abstract class SelectedProxyState with _$SelectedProxyState {
  const factory SelectedProxyState({
    required String proxyName,
    @Default(false) bool group,
    String? testUrl,
  }) = _SelectedProxyState;
}

@freezed
abstract class VpnState with _$VpnState {
  const factory VpnState({
    required TunStack stack,
    required VpnProps vpnProps,
  }) = _VpnState;
}

@freezed
abstract class SharedState with _$SharedState {
  const factory SharedState({
    SetupParams? setupParams,
    VpnOptions? vpnOptions,
    required String stopTip,
    required String startTip,
    required String currentProfileName,
    required String stopText,
    required bool onlyStatisticsProxy,
    required bool crashlytics,
  }) = _SharedState;

  factory SharedState.fromJson(Map<String, Object?> json) =>
      _$SharedStateFromJson(json);
}

extension SharedStateExt on SharedState {
  SharedState get needSyncSharedState => copyWith(setupParams: null);
}

@freezed
abstract class ComputeGroupsState with _$ComputeGroupsState {
  const factory ComputeGroupsState({
    required ProxiesData proxiesData,
    required ProxiesSortType sortType,
    required DelayMap delayMap,
    required Map<String, String> selectedMap,
    required String defaultTestUrl,
  }) = _ComputeGroupsState;
}

@freezed
abstract class MakeRealProfileState with _$MakeRealProfileState {
  const factory MakeRealProfileState({
    required String profilesPath,
    required int profileId,
    required Map<String, dynamic> rawConfig,
    required PatchClashConfig realPatchConfig,
    required bool overrideDns,
    required bool appendSystemDns,
    required List<ProxyGroup> proxyGroups,
    required List<Rule> rules,
    required List<Rule> addedRules,
    required String defaultUA,
  }) = _MakeRealProfileState;
}

@freezed
abstract class MigrationData with _$MigrationData {
  const factory MigrationData({
    Map<String, Object?>? configMap,
    @Default([]) List<Rule> rules,
    @Default([]) List<Script> scripts,
    @Default([]) List<Profile> profiles,
    @Default([]) List<ProfileRuleLink> links,
    @Default([]) List<ProxyGroup> proxyGroups,
  }) = _MigrationData;
}

@freezed
abstract class SetupState with _$SetupState {
  const factory SetupState({
    required int? profileId,
    required int? profileLastUpdateDate,
    required OverwriteType overwriteType,
    required List<Rule> rules,
    required List<ProxyGroup> proxyGroups,
    required List<Rule> addedRules,
    required Script? script,
    required bool overrideDns,
    required Dns dns,
  }) = _SetupState;
}

// extension SetupStateExt on SetupState {
//   bool needSetup(SetupState? lastSetupState) {
//     if (lastSetupState == null) {
//       return false;
//     }
//     if (profileId != lastSetupState.profileId) {
//       return true;
//     }
//     if (profileLastUpdateDate != lastSetupState.profileLastUpdateDate) {
//       return true;
//     }
//     final scriptIsChange = script != lastSetupState.script;
//     if (overwriteType != lastSetupState.overwriteType) {
//       if (!ruleListEquality.equals(addedRules, lastSetupState.addedRules) ||
//           scriptIsChange) {
//         return true;
//       }
//     } else {
//       if (overwriteType == OverwriteType.script) {
//         if (scriptIsChange) {
//           return true;
//         }
//       }
//       if (overwriteType == OverwriteType.standard) {
//         if (!ruleListEquality.equals(addedRules, lastSetupState.addedRules)) {
//           return true;
//         }
//       }
//     }
//     if (overrideDns != lastSetupState.overrideDns) {
//       return true;
//     }
//     if (overrideDns == true && dns != lastSetupState.dns) {
//       return true;
//     }
//     return false;
//   }
// }
