content: import 'dart:convert';
  
  import 'package:fl_clash/enum/enum.dart';
  import 'package:fl_clash/models/models.dart';
  import 'package:fl_clash/plugins/app.dart';
  import 'package:fl_clash/common/common.dart';
  import 'package:fl_clash/providers/providers.dart';
  import 'package:fl_clash/state.dart';
  import 'package:fl_clash/widgets/widgets.dart';
  import 'package:flutter/material.dart';
  import 'package:flutter/services.dart';
  import 'package:flutter_riverpod/flutter_riverpod.dart';
  
  class AccessFragment extends ConsumerStatefulWidget {
    const AccessFragment({super.key});
  
    @override
    ConsumerState<AccessFragment> createState() => _AccessFragmentState();
  }
  
  class _AccessFragmentState extends ConsumerState<AccessFragment> {
    List<String> acceptList = [];
    List<String> rejectList = [];
    late ScrollController _controller;
  
    @override
    void initState() {
      super.initState();
      _updateInitList();
      _controller = ScrollController();
      WidgetsBinding.instance.addPostFrameCallback((_) {
        final appState = globalState.appState;
        if (appState.packages.isEmpty) {
          Future.delayed(const Duration(milliseconds: 300), () async {
            ref.read(packagesProvider.notifier).value =
                await app?.getPackages() ?? [];
          });
        }
      });
    }
  
    @override
    void dispose() {
      _controller.dispose();
      super.dispose();
    }
  
    _updateInitList() {
      acceptList = globalState.config.vpnProps.accessControl.acceptList;
      rejectList = globalState.config.vpnProps.accessControl.rejectList;
    }
  
    Widget _buildSearchButton() {
      return IconButton(
        tooltip: appLocalizations.search,
        onPressed: () {
          showSearch(
            context: context,
            delegate: AccessControlSearchDelegate(
              acceptList: acceptList,
              rejectList: rejectList,
            ),
          ).then(
            (_) => setState(
              () {
                _updateInitList();
              },
            ),
          );
        },
        icon: const Icon(Icons.search),
      );
    }
  
    Widget _buildSelectedAllButton({
      required bool isSelectedAll,
      required List<String> allValueList,
    }) {
      final tooltip = isSelectedAll
          ? appLocalizations.cancelSelectAll
          : appLocalizations.selectAll;
      return IconButton(
        tooltip: tooltip,
        onPressed: () {
          ref.read(vpnSettingProvider.notifier).updateState((state) {
            final isAccept =
                state.accessControl.mode == AccessControlMode.acceptSelected;
            if (isSelectedAll) {
              return switch (isAccept) {
                true => state.copyWith.accessControl(
                    acceptList: [],
                  ),
                false => state.copyWith.accessControl(
                    rejectList: [],
                  ),
              };
            } else {
              return switch (isAccept) {
                true => state.copyWith.accessControl(
                    acceptList: allValueList,
                  ),
                false => state.copyWith.accessControl(
                    rejectList: allValueList,
                  ),
              };
            }
          });
        },
        icon: isSelectedAll
            ? const Icon(Icons.deselect)
            : const Icon(Icons.select_all),
      );
    }
  
    _intelligentSelected() async {
      final packageNames = ref.read(
        packageListSelectorStateProvider.select(
          (state) => state.list.map((item) => item.packageName),
        ),
      );
      final commonScaffoldState = context.commonScaffoldState;
      if (commonScaffoldState?.mounted != true) return;
      final selectedPackageNames =
          (await commonScaffoldState?.loadingRun<List<String>>(
                () async {
                  return await app?.getChinaPackageNames() ?? [];
                },
              ))
                  ?.toSet() ??
              {};
      final acceptList = packageNames
          .where((item) => !selectedPackageNames.contains(item))
          .toList();
      final rejectList = packageNames
          .where((item) => selectedPackageNames.contains(item))
          .toList();
      ref.read(vpnSettingProvider.notifier).updateState(
            (state) => state.copyWith.accessControl(
              acceptList: acceptList,
              rejectList: rejectList,
            ),
          );
    }
  
    Widget _buildSettingButton() {
      return IconButton(
        onPressed: () async {
          final res = await showSheet<int>(
            context: context,
            props: SheetProps(
              isScrollControlled: true,
            ),
            builder: (_, type) {
              return AdaptiveSheetScaffold(
                type: type,
                body: AccessControlPanel(),
                title: appLocalizations.proxiesSetting,
              );
            },
          );
          if (res == 1) {
            _intelligentSelected();
          }
        },
        icon: const Icon(Icons.tune),
      );
    }
  
    _handleSelected(List<String> valueList, Package package, bool? value) {
      if (value == true) {
        valueList.add(package.packageName);
      } else {
        valueList.remove(package.packageName);
      }
      ref.read(vpnSettingProvider.notifier).updateState((state) {
        return switch (
            state.accessControl.mode == AccessControlMode.acceptSelected) {
          true => state.copyWith.accessControl(
              acceptList: valueList,
            ),
          false => state.copyWith.accessControl(
              rejectList: valueList,
            ),
        };
      });
    }
  
    @override
    Widget build(BuildContext context) {
      final state = ref.watch(packageListSelectorStateProvider);
      final accessControl = state.accessControl;
      final accessControlMode = accessControl.mode;
      final packages = state.getSortList(
        accessControlMode == AccessControlMode.acceptSelected
            ? acceptList
            : rejectList,
      );
      final currentList = accessControl.currentList;
      final packageNameList = packages.map((e) => e.packageName).toList();
      final valueList = currentList.intersection(packageNameList);
      final describe = accessControlMode == AccessControlMode.acceptSelected
          ? appLocalizations.accessControlAllowDesc
          : appLocalizations.accessControlNotAllowDesc;
      return Column(
        mainAxisSize: MainAxisSize.max,
        children: [
          Flexible(
            flex: 0,
            child: ListItem.switchItem(
              title: Text(appLocalizations.appAccessControl),
              delegate: SwitchDelegate(
                value: accessControl.enable,
                onChanged: (enable) {
                  ref.read(vpnSettingProvider.notifier).updateState(
                        (state) => state.copyWith.accessControl(
                          enable: enable,
                        ),
                      );
                },
              ),
            ),
          ),
          const Padding(
            padding: EdgeInsets.symmetric(horizontal: 16),
            child: Divider(
              height: 12,
            ),
          ),
          Flexible(
            child: DisabledMask(
              status: !accessControl.enable,
              child: Column(
                children: [
                  ActivateBox(
                    active: accessControl.enable,
                    child: Padding(
                      padding: const EdgeInsets.only(
                        top: 4,
                        bottom: 4,
                        left: 16,
                        right: 8,
                      ),
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        mainAxisSize: MainAxisSize.max,
                        children: [
                          Expanded(
                            child: IntrinsicHeight(
                              child: Column(
                                mainAxisSize: MainAxisSize.max,
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  Expanded(
                                    child: Row(
                                      children: [
                                        Flexible(
                                          child: Text(
                                            appLocalizations.selected,
                                            style: Theme.of(context)
                                                .textTheme
                                                .labelLarge
                                                ?.copyWith(
                                                  color: Theme.of(context)
                                                      .colorScheme
                                                      .primary,
                                                ),
                                          ),
                                        ),
                                        const Flexible(
                                          child: SizedBox(
                                            width: 8,
                                          ),
                                        ),
                                        Flexible(
                                          child: Text(
                                            "${valueList.length}",
                                            style: Theme.of(context)
                                                .textTheme
                                                .labelLarge
                                                ?.copyWith(
                                                  color: Theme.of(context)
                                                      .colorScheme
                                                      .primary,
                                                ),
                                          ),
                                        ),
                                      ],
                                    ),
                                  ),
                                  Flexible(
                                    child: Text(describe),
                                  )
                                ],
                              ),
                            ),
                          ),
                          Row(
                            mainAxisSize: MainAxisSize.min,
                            mainAxisAlignment: MainAxisAlignment.end,
                            children: [
                              Flexible(
                                child: _buildSearchButton(),
                              ),
                              Flexible(
                                child: _buildSelectedAllButton(
                                  isSelectedAll:
                                      valueList.length == packageNameList.length,
                                  allValueList: packageNameList,
                                ),
                              ),
                              Flexible(
                                child: _buildSettingButton(),
                              ),
                            ],
                          ),
                        ],
                      ),
                    ),
                  ),
                  Expanded(
                    flex: 1,
                    child: packages.isEmpty
                        ? const Center(
                            child: CircularProgressIndicator(),
                          )
                        : CommonScrollBar(
                            controller: _controller,
                            child: ListView.builder(
                              controller: _controller,
                              itemCount: packages.length,
                              itemExtent: 72,
                              itemBuilder: (_, index) {
                                final package = packages[index];
                                return PackageListItem(
                                  key: Key(package.packageName),
                                  package: package,
                                  value: valueList.contains(package.packageName),
                                  isActive: accessControl.enable,
                                  onChanged: (value) {
                                    _handleSelected(valueList, package, value);
                                  },
                                );
                              },
                            ),
                          ),
                  ),
                ],
              ),
            ),
          ),
        ],
      );
    }
  }
  
  class PackageListItem extends StatelessWidget {
    final Package package;
    final bool value;
    final bool isActive;
    final void Function(bool?) onChanged;
  
    const PackageListItem({
      super.key,
      required this.package,
      required this.value,
      required this.isActive,
      required this.onChanged,
    });
  
    @override
    Widget build(BuildContext context) {
      return FadeScaleEnterBox(
        child: ActivateBox(
          active: isActive,
          child: ListItem.checkbox(
            leading: SizedBox(
              width: 48,
              height: 48,
              child: FutureBuilder<ImageProvider?>(
                future: app?.getPackageIcon(package.packageName),
                builder: (_, snapshot) {
                  if (!snapshot.hasData && snapshot.data == null) {
                    return Container();
                  } else {
                    return Image(
                      image: snapshot.data!,
                      gaplessPlayback: true,
                      width: 48,
                      height: 48,
                    );
                  }
                },
              ),
            ),
            title: Text(
              package.label,
              style: const TextStyle(
                overflow: TextOverflow.ellipsis,
              ),
              maxLines: 1,
            ),
            subtitle: Text(
              package.packageName,
              style: const TextStyle(
                overflow: TextOverflow.ellipsis,
              ),
              maxLines: 1,
            ),
            delegate: CheckboxDelegate(
              value: value,
              onChanged: onChanged,
            ),
          ),
        ),
      );
    }
  }
  
  class AccessControlSearchDelegate extends SearchDelegate {
    List<String> acceptList = [];
    List<String> rejectList = [];
  
    AccessControlSearchDelegate({
      required this.acceptList,
      required this.rejectList,
    });
  
    @override
    List<Widget>? buildActions(BuildContext context) {
      return [
        IconButton(
          onPressed: () {
            if (query.isEmpty) {
              close(context, null);
              return;
            }
            query = '';
          },
          icon: const Icon(Icons.clear),
        ),
        const SizedBox(
          width: 8,
        )
      ];
    }
  
    @override
    Widget? buildLeading(BuildContext context) {
      return IconButton(
        onPressed: () {
          close(context, null);
        },
        icon: const Icon(Icons.arrow_back),
      );
    }
  
    _handleSelected(
        WidgetRef ref, List<String> valueList, Package package, bool? value) {
      if (value == true) {
        valueList.add(package.packageName);
      } else {
        valueList.remove(package.packageName);
      }
      ref.read(vpnSettingProvider.notifier).updateState((state) {
        return switch (
            state.accessControl.mode == AccessControlMode.acceptSelected) {
          true => state.copyWith.accessControl(
              acceptList: valueList,
            ),
          false => state.copyWith.accessControl(
              rejectList: valueList,
            ),
        };
      });
    }
  
    Widget _packageList() {
      final lowQuery = query.toLowerCase();
      return Consumer(
        builder: (context, ref, __) {
          final vm3 = ref.watch(
            packageListSelectorStateProvider.select(
              (state) => VM3(
                a: state.getSortList(
                  state.accessControl.mode == AccessControlMode.acceptSelected
                      ? acceptList
                      : rejectList,
                ),
                b: state.accessControl.enable,
                c: state.accessControl.currentList,
              ),
            ),
          );
          final packages = vm3.a;
          final queryPackages = packages
              .where(
                (package) =>
                    package.label.toLowerCase().contains(lowQuery) ||
                    package.packageName.contains(lowQuery),
              )
              .toList();
          final isAccessControl = vm3.b;
          final currentList = vm3.c;
          final packageNameList = packages.map((e) => e.packageName).toList();
          final valueList = currentList.intersection(packageNameList);
          return DisabledMask(
            status: !isAccessControl,
            child: ListView.builder(
              itemCount: queryPackages.length,
              itemBuilder: (_, index) {
                final package = queryPackages[index];
                return PackageListItem(
                  key: Key(package.packageName),
                  package: package,
                  value: valueList.contains(package.packageName),
                  isActive: isAccessControl,
                  onChanged: (value) {
                    _handleSelected(
                      ref,
                      valueList,
                      package,
                      value,
                    );
                  },
                );
              },
            ),
          );
        },
      );
    }
  
    @override
    Widget buildResults(BuildContext context) {
      return buildSuggestions(context);
    }
  
    @override
    Widget buildSuggestions(BuildContext context) {
      return _packageList();
    }
  }
  
  class AccessControlPanel extends ConsumerStatefulWidget {
    const AccessControlPanel({
      super.key,
    });
  
    @override
    ConsumerState createState() => _AccessControlPanelState();
  }
  
  class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
    IconData _getIconWithAccessControlMode(AccessControlMode mode) {
      return switch (mode) {
        AccessControlMode.acceptSelected => Icons.adjust_outlined,
        AccessControlMode.rejectSelected => Icons.block_outlined,
      };
    }
  
    String _getTextWithAccessControlMode(AccessControlMode mode) {
      return switch (mode) {
        AccessControlMode.acceptSelected => appLocalizations.whitelistMode,
        AccessControlMode.rejectSelected => appLocalizations.blacklistMode,
      };
    }
  
    String _getTextWithAccessSortType(AccessSortType type) {
      return switch (type) {
        AccessSortType.none => appLocalizations.defaultText,
        AccessSortType.name => appLocalizations.name,
        AccessSortType.time => appLocalizations.time,
      };
    }
  
    IconData _getIconWithProxiesSortType(AccessSortType type) {
      return switch (type) {
        AccessSortType.none => Icons.sort,
        AccessSortType.name => Icons.sort_by_alpha,
        AccessSortType.time => Icons.timeline,
      };
    }
  
    List<Widget> _buildModeSetting() {
      return generateSection(
        title: appLocalizations.mode,
        items: [
          SingleChildScrollView(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            scrollDirection: Axis.horizontal,
            child: Consumer(
              builder: (_, ref, __) {
                final accessControlMode = ref.watch(
                  vpnSettingProvider.select((state) => state.accessControl.mode),
                );
                return Wrap(
                  spacing: 16,
                  children: [
                    for (final item in AccessControlMode.values)
                      SettingInfoCard(
                        Info(
                          label: _getTextWithAccessControlMode(item),
                          iconData: _getIconWithAccessControlMode(item),
                        ),
                        isSelected: accessControlMode == item,
                        onPressed: () {
                          ref.read(vpnSettingProvider.notifier).updateState(
                                (state) => state.copyWith.accessControl(
                                  mode: item,
                                ),
                              );
                        },
                      )
                  ],
                );
              },
            ),
          )
        ],
      );
    }
  
    List<Widget> _buildSortSetting() {
      return generateSection(
        title: appLocalizations.sort,
        items: [
          SingleChildScrollView(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            scrollDirection: Axis.horizontal,
            child: Consumer(
              builder: (_, ref, __) {
                final accessSortType = ref.watch(
                  vpnSettingProvider.select((state) => state.accessControl.sort),
                );
                return Wrap(
                  spacing: 16,
                  children: [
                    for (final item in AccessSortType.values)
                      SettingInfoCard(
                        Info(
                          label: _getTextWithAccessSortType(item),
                          iconData: _getIconWithProxiesSortType(item),
                        ),
                        isSelected: accessSortType == item,
                        onPressed: () {
                          ref.read(vpnSettingProvider.notifier).updateState(
                                (state) => state.copyWith.accessControl(
                                  sort: item,
                                ),
                              );
                        },
                      ),
                  ],
                );
              },
            ),
          ),
        ],
      );
    }
  
    List<Widget> _buildSourceSetting() {
      return generateSection(
        title: appLocalizations.source,
        items: [
          SingleChildScrollView(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            scrollDirection: Axis.horizontal,
            child: Consumer(
              builder: (_, ref, __) {
                final vm2 = ref.watch(
                  vpnSettingProvider.select(
                    (state) => VM2(
                      a: state.accessControl.isFilterSystemApp,
                      b: state.accessControl.isFilterNonInternetApp,
                    ),
                  ),
                );
                return Wrap(
                  spacing: 16,
                  children: [
                    SettingTextCard(
                      appLocalizations.systemApp,
                      isSelected: vm2.a == false,
                      onPressed: () {
                        ref.read(vpnSettingProvider.notifier).updateState(
                              (state) => state.copyWith.accessControl(
                                isFilterSystemApp: !vm2.a,
                              ),
                            );
                      },
                    ),
                    SettingTextCard(
                      appLocalizations.noNetworkApp,
                      isSelected: vm2.b == false,
                      onPressed: () {
                        ref.read(vpnSettingProvider.notifier).updateState(
                              (state) => state.copyWith.accessControl(
                                isFilterNonInternetApp: !vm2.b,
                              ),
                            );
                      },
                    )
                  ],
                );
              },
            ),
          )
        ],
      );
    }
  
    _copyToClipboard() async {
      await globalState.safeRun(() {
        final data = globalState.config.vpnProps.accessControl.toJson();
        Clipboard.setData(
          ClipboardData(
            text: json.encode(data),
          ),
        );
      });
      if (!mounted) return;
      Navigator.of(context).pop();
    }
  
    _pasteToClipboard() async {
      await globalState.safeRun(
        () async {
          final data = await Clipboard.getData('text/plain');
          final text = data?.text;
          if (text == null) return;
          ref.read(vpnSettingProvider.notifier).updateState(
                (state) => state.copyWith(
                  accessControl: AccessControl.fromJson(
                    json.decode(text),
                  ),
                ),
              );
        },
      );
      if (!mounted) return;
      Navigator.of(context).pop();
    }
  
    List<Widget> _buildActionSetting() {
      return generateSection(
        title: appLocalizations.action,
        items: [
          Padding(
            padding: const EdgeInsets.symmetric(
              horizontal: 16,
            ),
            child: Wrap(
              runSpacing: 16,
              spacing: 16,
              children: [
                CommonChip(
                  avatar: const Icon(Icons.auto_awesome),
                  label: appLocalizations.intelligentSelected,
                  onPressed: () {
                    Navigator.of(context).pop(1);
                  },
                ),
                CommonChip(
                  avatar: const Icon(Icons.paste),
                  label: appLocalizations.clipboardImport,
                  onPressed: _pasteToClipboard,
                ),
                CommonChip(
                  avatar: const Icon(Icons.content_copy),
                  label: appLocalizations.clipboardExport,
                  onPressed: _copyToClipboard,
                )
              ],
            ),
          )
        ],
      );
    }
  
    @override
    Widget build(BuildContext context) {
      return SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.only(bottom: 32),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              ..._buildModeSetting(),
              ..._buildSortSetting(),
              ..._buildSourceSetting(),
              ..._buildActionSetting(),
            ],
          ),
        ),
      );
    }
  }
  