import 'dart:math';

import 'package:collection/collection.dart';
import 'package:fl_clash/clash/core.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:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class ProxiesFragment extends StatefulWidget {
  const ProxiesFragment({super.key});

  @override
  State<ProxiesFragment> createState() => _ProxiesFragmentState();
}

class _ProxiesFragmentState extends State<ProxiesFragment> {
  _initActions() {
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      final commonScaffoldState =
          context.findAncestorStateOfType<CommonScaffoldState>();
      final items = [
        CommonPopupMenuItem(
          action: ProxiesSortType.none,
          label: appLocalizations.defaultSort,
          iconData: Icons.reorder,
        ),
        CommonPopupMenuItem(
          action: ProxiesSortType.delay,
          label: appLocalizations.delaySort,
          iconData: Icons.network_ping,
        ),
        CommonPopupMenuItem(
          action: ProxiesSortType.name,
          label: appLocalizations.nameSort,
          iconData: Icons.sort_by_alpha,
        ),
      ];
      commonScaffoldState?.actions = [
        Selector<Config, ProxiesType>(
          selector: (_, config) => config.proxiesType,
          builder: (_, proxiesType, __) {
            return IconButton(
              icon: Icon(
                switch (proxiesType) {
                  ProxiesType.tab => Icons.view_list,
                  ProxiesType.expansion => Icons.view_carousel,
                },
              ),
              onPressed: () {
                final config = globalState.appController.config;
                config.proxiesType = config.proxiesType == ProxiesType.tab
                    ? ProxiesType.expansion
                    : ProxiesType.tab;
              },
            );
          },
        ),
        IconButton(
          icon: const Icon(
            Icons.view_column,
          ),
          onPressed: () {
            globalState.appController.changeColumns();
          },
        ),
        IconButton(
          icon: const Icon(Icons.transform_sharp),
          onPressed: () {
            final config = globalState.appController.config;
            config.proxyCardType = config.proxyCardType == ProxyCardType.expand
                ? ProxyCardType.shrink
                : ProxyCardType.expand;
          },
        ),
        Selector<Config, ProxiesSortType>(
          selector: (_, config) => config.proxiesSortType,
          builder: (_, proxiesSortType, __) {
            return CommonPopupMenu<ProxiesSortType>.radio(
              items: items,
              icon: const Icon(Icons.sort_sharp),
              onSelected: (value) {
                final config = context.read<Config>();
                config.proxiesSortType = value;
              },
              selectedValue: proxiesSortType,
            );
          },
        ),
        const SizedBox(
          width: 8,
        )
      ];
    });
  }

  @override
  Widget build(BuildContext context) {
    return Selector<AppState, bool>(
      selector: (_, appState) => appState.currentLabel == 'proxies',
      builder: (_, isCurrent, child) {
        if (isCurrent) {
          _initActions();
        }
        return child!;
      },
      child: Selector<Config, ProxiesType>(
        selector: (_, config) => config.proxiesType,
        builder: (_, proxiesType, __) {
          return switch (proxiesType) {
            ProxiesType.tab => const ProxiesTabFragment(),
            ProxiesType.expansion => const ProxiesExpansionPanelFragment(),
          };
        },
      ),
    );
  }
}

class ProxiesTabFragment extends StatefulWidget {
  const ProxiesTabFragment({super.key});

  @override
  State<ProxiesTabFragment> createState() => _ProxiesTabFragmentState();
}

class _ProxiesTabFragmentState extends State<ProxiesTabFragment>
    with TickerProviderStateMixin {
  TabController? _tabController;

  _handleTabControllerChange() {
    final indexIsChanging = _tabController?.indexIsChanging ?? false;
    if (indexIsChanging) return;
    final index = _tabController?.index;
    if (index == null) return;
    final appController = globalState.appController;
    final currentGroups = appController.appState.currentGroups;
    if (currentGroups.length > index) {
      appController.config.updateCurrentGroupName(currentGroups[index].name);
    }
  }

  @override
  void dispose() {
    super.dispose();
    _tabController?.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Selector2<AppState, Config, ProxiesSelectorState>(
      selector: (_, appState, config) {
        final currentGroups = appState.currentGroups;
        final groupNames = currentGroups.map((e) => e.name).toList();
        return ProxiesSelectorState(
          groupNames: groupNames,
          currentGroupName: config.currentGroupName,
        );
      },
      shouldRebuild: (prev, next) {
        if (!const ListEquality<String>()
            .equals(prev.groupNames, next.groupNames)) {
          _tabController?.removeListener(_handleTabControllerChange);
          _tabController?.dispose();
          _tabController = null;
          return true;
        }
        return false;
      },
      builder: (_, state, __) {
        final index = state.groupNames.indexWhere(
          (item) => item == state.currentGroupName,
        );
        _tabController ??= TabController(
          length: state.groupNames.length,
          initialIndex: index == -1 ? 0 : index,
          vsync: this,
        )..addListener(_handleTabControllerChange);
        return Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            TabBar(
              controller: _tabController,
              padding: const EdgeInsets.symmetric(horizontal: 16),
              dividerColor: Colors.transparent,
              isScrollable: true,
              tabAlignment: TabAlignment.start,
              overlayColor: const WidgetStatePropertyAll(Colors.transparent),
              tabs: [
                for (final groupName in state.groupNames)
                  Tab(
                    text: groupName,
                  ),
              ],
            ),
            Expanded(
              child: TabBarView(
                controller: _tabController,
                children: [
                  for (final groupName in state.groupNames)
                    KeepContainer(
                      key: ObjectKey(groupName),
                      child: ProxyGroupView(
                        groupName: groupName,
                        type: ProxiesType.tab,
                      ),
                    ),
                ],
              ),
            )
          ],
        );
      },
    );
  }
}

class ProxiesExpansionPanelFragment extends StatefulWidget {
  const ProxiesExpansionPanelFragment({super.key});

  @override
  State<ProxiesExpansionPanelFragment> createState() =>
      _ProxiesExpansionPanelFragmentState();
}

class _ProxiesExpansionPanelFragmentState
    extends State<ProxiesExpansionPanelFragment> {
  @override
  Widget build(BuildContext context) {
    return Selector2<AppState, Config, ProxiesSelectorState>(
      selector: (_, appState, config) {
        final currentGroups = appState.currentGroups;
        final groupNames = currentGroups.map((e) => e.name).toList();
        return ProxiesSelectorState(
          groupNames: groupNames,
          currentGroupName: config.currentGroupName,
        );
      },
      builder: (_, state, __) {
        return ListView.separated(
          padding: const EdgeInsets.all(16),
          itemCount: state.groupNames.length,
          itemBuilder: (_, index) {
            final groupName = state.groupNames[index];
            return ProxyGroupView(
              key: PageStorageKey(groupName),
              groupName: groupName,
              type: ProxiesType.expansion,
            );
          },
          separatorBuilder: (BuildContext context, int index) {
            return const SizedBox(
              height: 16,
            );
          },
        );
      },
    );
  }
}

class ProxyGroupView extends StatefulWidget {
  final String groupName;
  final ProxiesType type;

  const ProxyGroupView({
    super.key,
    required this.groupName,
    required this.type,
  });

  @override
  State<ProxyGroupView> createState() => _ProxyGroupViewState();
}

class _ProxyGroupViewState extends State<ProxyGroupView> {
  var isLock = false;
  final scrollController = ScrollController();
  var isEnd = false;

  String get groupName => widget.groupName;

  ProxiesType get type => widget.type;

  double _getItemHeight(ProxyCardType proxyCardType) {
    final isExpand = proxyCardType == ProxyCardType.expand;
    final measure = globalState.appController.measure;
    final baseHeight =
        12 * 2 + measure.bodyMediumHeight * 2 + measure.bodySmallHeight + 8;
    return isExpand ? baseHeight + measure.labelSmallHeight + 8 : baseHeight;
  }

  _delayTest(List<Proxy> proxies) async {
    if (isLock) return;
    isLock = true;
    final appController = globalState.appController;
    for (final proxy in proxies) {
      final proxyName =
          appController.appState.getRealProxyName(proxy.name) ?? proxy.name;
      globalState.appController.setDelay(
        Delay(
          name: proxyName,
          value: 0,
        ),
      );
      clashCore.getDelay(proxyName).then((delay) {
        globalState.appController.setDelay(delay);
      });
    }
    await Future.delayed(httpTimeoutDuration + moreDuration);
    appController.appState.sortNum++;
    isLock = false;
  }

  Widget _currentProxyNameBuilder({
    required Widget Function(String) builder,
  }) {
    return Selector2<AppState, Config, String>(
      selector: (_, appState, config) {
        final group = appState.getGroupWithName(groupName)!;
        return config.currentSelectedMap[groupName] ?? group.now ?? '';
      },
      builder: (_, value, ___) {
        return builder(value);
      },
    );
  }

  Widget _buildTabGroupView({
    required List<Proxy> proxies,
    required int columns,
    required ProxyCardType proxyCardType,
  }) {
    final sortedProxies = globalState.appController.getSortProxies(
      proxies,
    );
    return DelayTestButtonContainer(
      onClick: () async {
        await _delayTest(
          proxies,
        );
      },
      child: Align(
        alignment: Alignment.topCenter,
        child: GridView.builder(
          padding: const EdgeInsets.all(16),
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: columns,
            mainAxisSpacing: 8,
            crossAxisSpacing: 8,
            mainAxisExtent: _getItemHeight(proxyCardType),
          ),
          itemCount: sortedProxies.length,
          itemBuilder: (_, index) {
            final proxy = sortedProxies[index];
            return _currentProxyNameBuilder(builder: (value) {
              return ProxyCard(
                type: proxyCardType,
                key: ValueKey('$groupName.${proxy.name}'),
                isSelected: value == proxy.name,
                proxy: proxy,
                groupName: groupName,
              );
            });
          },
        ),
      ),
    );
  }

  Widget _buildExpansionGroupView({
    required List<Proxy> proxies,
    required int columns,
    required ProxyCardType proxyCardType,
  }) {
    final sortedProxies = globalState.appController.getSortProxies(
      proxies,
    );
    final group =
        globalState.appController.appState.getGroupWithName(groupName)!;
    final itemHeight = _getItemHeight(proxyCardType);
    final innerHeight = context.appSize.height - 200;
    final lines = (sortedProxies.length / columns).ceil();
    final minLines =
        innerHeight >= 200 ? (innerHeight / itemHeight).floor() : 3;
    final height = (itemHeight + 8) * min(lines, minLines) - 8;
    return Selector<Config, Set<String>>(
      selector: (_, config) => config.currentUnfoldSet,
      builder: (_, currentUnfoldSet, __) {
        return CommonCard(
          child: ExpansionTile(
            childrenPadding: const EdgeInsets.all(8),
            initiallyExpanded: currentUnfoldSet.contains(groupName),
            iconColor: context.colorScheme.onSurfaceVariant,
            onExpansionChanged: (value) {
              final tempUnfoldSet = Set<String>.from(currentUnfoldSet);
              if (value) {
                tempUnfoldSet.add(groupName);
              } else {
                tempUnfoldSet.remove(groupName);
              }
              globalState.appController.config.updateCurrentUnfoldSet(
                tempUnfoldSet,
              );
            },
            controlAffinity: ListTileControlAffinity.trailing,
            title: Row(
              mainAxisSize: MainAxisSize.min,
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Flexible(
                  flex: 1,
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(groupName),
                      const SizedBox(
                        height: 4,
                      ),
                      Flexible(
                        flex: 1,
                        child: Row(
                          mainAxisSize: MainAxisSize.min,
                          mainAxisAlignment: MainAxisAlignment.start,
                          crossAxisAlignment: CrossAxisAlignment.center,
                          children: [
                            Text(
                              group.type.name,
                              style: context.textTheme.labelMedium?.toLight,
                            ),
                            Flexible(
                              flex: 1,
                              child: _currentProxyNameBuilder(
                                builder: (value) {
                                  return Row(
                                    mainAxisSize: MainAxisSize.min,
                                    mainAxisAlignment: MainAxisAlignment.start,
                                    crossAxisAlignment:
                                        CrossAxisAlignment.center,
                                    children: [
                                      if (value.isNotEmpty) ...[
                                        Icon(
                                          Icons.arrow_right,
                                          color: context
                                              .colorScheme.onSurfaceVariant,
                                        ),
                                        Flexible(
                                          flex: 1,
                                          child: Text(
                                            overflow: TextOverflow.ellipsis,
                                            value,
                                            style: context
                                                .textTheme.labelMedium?.toLight,
                                          ),
                                        ),
                                      ]
                                    ],
                                  );
                                },
                              ),
                            ),
                          ],
                        ),
                      ),
                      const SizedBox(
                        height: 4,
                      ),
                    ],
                  ),
                ),
                IconButton(
                  icon: Icon(
                    Icons.network_ping,
                    size: 20,
                    color: context.colorScheme.onSurfaceVariant,
                  ),
                  onPressed: () {
                    _delayTest(sortedProxies);
                  },
                ),
              ],
            ),
            shape: const RoundedRectangleBorder(
              side: BorderSide.none,
            ),
            collapsedShape: const RoundedRectangleBorder(
              side: BorderSide.none,
            ),
            children: [
              SizedBox(
                height: height,
                child: GridView.builder(
                  key: widget.key,
                  controller: scrollController,
                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: columns,
                    mainAxisSpacing: 8,
                    crossAxisSpacing: 8,
                    mainAxisExtent: _getItemHeight(proxyCardType),
                  ),
                  itemCount: sortedProxies.length,
                  itemBuilder: (_, index) {
                    final proxy = sortedProxies[index];
                    return _currentProxyNameBuilder(
                      builder: (value) {
                        return ProxyCard(
                          style: CommonCardType.filled,
                          type: proxyCardType,
                          isSelected: value == proxy.name,
                          key: ValueKey('$groupName.${proxy.name}'),
                          proxy: proxy,
                          groupName: groupName,
                        );
                      },
                    );
                  },
                ),
              ),
            ],
          ),
        );
      },
    );
  }

  @override
  void dispose() {
    super.dispose();
    scrollController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Selector2<AppState, Config, ProxyGroupSelectorState>(
      selector: (_, appState, config) {
        final group = appState.getGroupWithName(groupName)!;
        return ProxyGroupSelectorState(
          proxyCardType: config.proxyCardType,
          proxiesSortType: config.proxiesSortType,
          columns: globalState.appController.columns,
          sortNum: appState.sortNum,
          proxies: group.all,
        );
      },
      builder: (_, state, __) {
        final proxies = state.proxies;
        final columns = state.columns;
        final proxyCardType = state.proxyCardType;
        return switch (type) {
          ProxiesType.tab => _buildTabGroupView(
              proxies: proxies,
              columns: columns,
              proxyCardType: proxyCardType,
            ),
          ProxiesType.expansion => _buildExpansionGroupView(
              proxies: proxies,
              columns: columns,
              proxyCardType: proxyCardType,
            ),
        };
      },
    );
  }
}

class DelayTestButtonContainer extends StatefulWidget {
  final Widget child;
  final Future Function() onClick;

  const DelayTestButtonContainer({
    super.key,
    required this.child,
    required this.onClick,
  });

  @override
  State<DelayTestButtonContainer> createState() =>
      _DelayTestButtonContainerState();
}

class _DelayTestButtonContainerState extends State<DelayTestButtonContainer>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _scale;

  _healthcheck() async {
    _controller.forward();
    await widget.onClick();
    _controller.reverse();
  }

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(
        milliseconds: 200,
      ),
    );
    _scale = Tween<double>(
      begin: 1.0,
      end: 0.0,
    ).animate(
      CurvedAnimation(
        parent: _controller,
        curve: const Interval(
          0,
          1,
        ),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    _controller.reverse();
    return FloatLayout(
      floatingWidget: FloatWrapper(
        child: AnimatedBuilder(
          animation: _controller.view,
          builder: (_, child) {
            return SizedBox(
              width: 56,
              height: 56,
              child: Transform.scale(
                scale: _scale.value,
                child: child,
              ),
            );
          },
          child: FloatingActionButton(
            heroTag: null,
            onPressed: _healthcheck,
            child: const Icon(Icons.network_ping),
          ),
        ),
      ),
      child: widget.child,
    );
  }
}

class ProxyCard extends StatelessWidget {
  final String groupName;
  final Proxy proxy;
  final bool isSelected;
  final CommonCardType style;
  final ProxyCardType type;

  const ProxyCard({
    super.key,
    required this.groupName,
    required this.proxy,
    required this.isSelected,
    this.style = CommonCardType.plain,
    required this.type,
  });

  Measure get measure => globalState.appController.measure;

  Widget _buildDelayText() {
    return SizedBox(
      height: measure.labelSmallHeight,
      child: Selector<AppState, int?>(
        selector: (context, appState) => appState.getDelay(
          proxy.name,
        ),
        builder: (context, delay, __) {
          return FadeBox(
            child: Builder(
              builder: (_) {
                if (delay == null) {
                  return Container();
                }
                if (delay == 0) {
                  return SizedBox(
                    height: measure.labelSmallHeight,
                    width: measure.labelSmallHeight,
                    child: const CircularProgressIndicator(
                      strokeWidth: 2,
                    ),
                  );
                }
                return Text(
                  delay > 0 ? '$delay ms' : "Timeout",
                  style: context.textTheme.labelSmall?.copyWith(
                    overflow: TextOverflow.ellipsis,
                    color: other.getDelayColor(
                      delay,
                    ),
                  ),
                );
              },
            ),
          );
        },
      ),
    );
  }

  Widget _buildProxyNameText(BuildContext context) {
    return SizedBox(
      height: measure.bodyMediumHeight * 2,
      child: Text(
        proxy.name,
        maxLines: 2,
        style: context.textTheme.bodyMedium?.copyWith(
          overflow: TextOverflow.ellipsis,
        ),
      ),
    );
  }

  _changeProxy(BuildContext context) {
    final appController = globalState.appController;
    final group = appController.appState.getGroupWithName(groupName)!;
    if (group.type != GroupType.Selector) {
      globalState.showSnackBar(
        context,
        message: appLocalizations.notSelectedTip,
      );
      return;
    }
    globalState.appController.config.updateCurrentSelectedMap(
      groupName,
      proxy.name,
    );
    clashCore.changeProxy(
      ChangeProxyParams(
        groupName: groupName,
        proxyName: proxy.name,
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final measure = globalState.appController.measure;
    final delayText = _buildDelayText();
    final proxyNameText = _buildProxyNameText(context);
    return CommonCard(
      type: style,
      key: key,
      onPressed: () {
        _changeProxy(context);
      },
      isSelected: isSelected,
      child: Container(
        padding: const EdgeInsets.all(12),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            proxyNameText,
            const SizedBox(
              height: 8,
            ),
            if (type == ProxyCardType.expand) ...[
              SizedBox(
                height: measure.bodySmallHeight,
                child: Selector<AppState, String>(
                  selector: (context, appState) => appState.getDesc(
                    proxy.type,
                    proxy.name,
                  ),
                  builder: (_, desc, __) {
                    return TooltipText(
                      text: Text(
                        desc,
                        style: context.textTheme.bodySmall?.copyWith(
                          overflow: TextOverflow.ellipsis,
                          color: context.textTheme.bodySmall?.color?.toLight(),
                        ),
                      ),
                    );
                  },
                ),
              ),
              const SizedBox(
                height: 8,
              ),
              delayText,
            ] else
              SizedBox(
                height: measure.bodySmallHeight,
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Flexible(
                      flex: 1,
                      child: TooltipText(
                        text: Text(
                          proxy.type,
                          style: context.textTheme.bodySmall?.copyWith(
                            overflow: TextOverflow.ellipsis,
                            color:
                                context.textTheme.bodySmall?.color?.toLight(),
                          ),
                        ),
                      ),
                    ),
                    delayText,
                  ],
                ),
              )
          ],
        ),
      ),
    );
  }
}
