// ignore_for_file: use_build_context_synchronously

import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';

import 'package:youtipie/class/channels/channel_info.dart';
import 'package:youtipie/core/enum.dart';
import 'package:youtipie/youtipie.dart';

import 'package:namida/controller/navigator_controller.dart';
import 'package:namida/core/constants.dart';
import 'package:namida/core/extensions.dart';
import 'package:namida/core/functions.dart';
import 'package:namida/core/icon_fonts/broken_icons.dart';
import 'package:namida/core/namida_converter_ext.dart';
import 'package:namida/core/translations/language.dart';
import 'package:namida/core/utils.dart';
import 'package:namida/ui/widgets/custom_widgets.dart';
import 'package:namida/youtube/controller/youtube_subscriptions_controller.dart';

class _YTSubscribeButtonManager {
  const _YTSubscribeButtonManager();

  static final _activeChannelInfos = <String, List<RxBaseCore<YoutiPieChannelInfo?>>>{};
  static final _activeModifications = <String, bool>{}.obs;

  static void add(String? channelId, RxBaseCore<YoutiPieChannelInfo?> channelPageRx) {
    if (channelId == null) return;
    _activeChannelInfos.addForce(channelId, channelPageRx);
  }

  static void remove(String? channelId, RxBaseCore<YoutiPieChannelInfo?> channelPageRx) {
    if (channelId == null) return;
    _activeChannelInfos[channelId]?.remove(channelPageRx);
    if (_activeChannelInfos[channelId]?.isEmpty == true) _activeChannelInfos.remove(channelId);
  }

  static void onModifyStart(String? channelId) {
    if (channelId == null) return;
    _activeModifications[channelId] = true;
  }

  static void onModifyDone(String? channelId) {
    if (channelId == null) return;
    _activeModifications[channelId] = false;
  }

  static void afterModifySuccess(String? channelId, RxBaseCore<YoutiPieChannelInfo?> channelPageRx) {
    if (channelId == null) return;
    final activeChannels = _activeChannelInfos[channelId];
    if (activeChannels != null) {
      for (var item in activeChannels) {
        item.value?.rebuild(
          newSubscribed: channelPageRx.value?.subscribed,
          newNotifications: channelPageRx.value?.notifications,
          newNotificationParameters: channelPageRx.value?.notificationParameters,
        );
        item.refresh();
      }
    }
  }
}

class YTSubscribeButton extends StatefulWidget {
  final String? channelID;
  final RxBaseCore<YoutiPieChannelInfo?> mainChannelInfo;
  final SubscribeTextDisplayPolicy subscribeTextDisplayPolicy;
  final bool subscribeTextOnLeft;

  const YTSubscribeButton({
    super.key,
    required this.channelID,
    required this.mainChannelInfo,
    this.subscribeTextDisplayPolicy = SubscribeTextDisplayPolicy.always,
    this.subscribeTextOnLeft = false,
  });

  @override
  State<YTSubscribeButton> createState() => _YTSubscribeButtonState();
}

class _YTSubscribeButtonState extends State<YTSubscribeButton> {
  late bool? _currentSubscribed;
  late ChannelNotifications? _currentNotificationsStatus;

  void _onPageChanged() {
    if (mounted) {
      final channelInfo = widget.mainChannelInfo.value;
      setState(() {
        _currentSubscribed = channelInfo?.subscribed;
        _currentNotificationsStatus = channelInfo?.notifications;
      });
    }
  }

  Future<bool> _onChangeSubscribeStatus(bool isSubscribed, void Function() onStart, void Function() onEnd) async {
    final channelInfo = widget.mainChannelInfo.value;
    if (channelInfo == null) return isSubscribed;

    onStart();
    _YTSubscribeButtonManager.onModifyStart(widget.channelID);
    final res = await YoutiPie.channelAction.changeSubscribeStatus(
      mainChannelInfo: widget.mainChannelInfo.value,
      channelEngagement: channelInfo.channelEngagement,
      subscribe: !isSubscribed,
    );
    _YTSubscribeButtonManager.onModifyDone(widget.channelID);
    onEnd();
    if (res != null) {
      _YTSubscribeButtonManager.afterModifySuccess(widget.channelID, widget.mainChannelInfo);
      refreshState(() {
        _currentSubscribed = res.isNowSubbed;
        _currentNotificationsStatus = res.newNotificationStatus;
      });
      return !isSubscribed;
    }

    return isSubscribed;
  }

  void _onNotificationsTap() async {
    final tileActiveNoti = _currentNotificationsStatus.obs;
    final isSaving = false.obs;

    await NamidaNavigator.inst.navigateDialog(
      onDisposing: () {
        tileActiveNoti.close();
        isSaving.close();
      },
      dialog: CustomBlurryDialog(
        title: lang.configure,
        normalTitleStyle: true,
        actions: [
          CancelButtonDisabled(
            disabledRx: isSaving,
          ),
          ObxO(
            rx: tileActiveNoti,
            builder: (context, activeNoti) => ObxO(
              rx: isSaving,
              builder: (context, saving) => NamidaButton(
                enabled: activeNoti != _currentNotificationsStatus && !saving,
                text: lang.save.toUpperCase(),
                isLoading: saving,
                onTap: () async {
                  final notiToActivate = tileActiveNoti.value;
                  if (notiToActivate == null) return;

                  isSaving.value = true;
                  _YTSubscribeButtonManager.onModifyStart(widget.channelID);
                  final res = await YoutiPie.channelAction.changeChannelNotificationStatus(
                    mainChannelInfo: widget.mainChannelInfo.value,
                    notifications: notiToActivate,
                  );
                  _YTSubscribeButtonManager.onModifyDone(widget.channelID);
                  isSaving.value = false;
                  if (res == true) {
                    _YTSubscribeButtonManager.afterModifySuccess(widget.channelID, widget.mainChannelInfo);
                    refreshState(() => _currentNotificationsStatus = notiToActivate);
                    NamidaNavigator.inst.closeDialog();
                  }
                },
              ),
            ),
          ),
        ],
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: ObxO(
            rx: tileActiveNoti,
            builder: (context, activeNoti) => Column(
              children: [
                ...ChannelNotifications.values.map(
                  (e) {
                    return Padding(
                      padding: const EdgeInsets.all(3.0),
                      child: ListTileWithCheckMark(
                        leading: _notificationsToIcon(e, 24.0),
                        title: e.toText(),
                        active: e == activeNoti,
                        onTap: () => tileActiveNoti.value = e,
                      ),
                    );
                  },
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  @override
  void initState() {
    _YTSubscribeButtonManager.add(widget.channelID, widget.mainChannelInfo);
    _onPageChanged(); // fill initial values
    widget.mainChannelInfo.addListener(_onPageChanged);
    super.initState();
  }

  @override
  void dispose() {
    _YTSubscribeButtonManager.remove(widget.channelID, widget.mainChannelInfo);
    widget.mainChannelInfo.removeListener(_onPageChanged);
    super.dispose();
  }

  Future<bool> _confirmUnsubscribe() async {
    bool confirmed = false;
    await NamidaNavigator.inst.navigateDialog(
      dialog: CustomBlurryDialog(
        isWarning: true,
        normalTitleStyle: true,
        bodyText: "${lang.confirm}: ${lang.unsubscribe}?",
        actions: [
          const CancelButton(),
          NamidaButton(
            colorScheme: Colors.red,
            text: lang.unsubscribe.toUpperCase(),
            onTap: () async {
              confirmed = true;
              NamidaNavigator.inst.closeDialog();
            },
          ),
        ],
      ),
    );
    return confirmed;
  }

  Widget? _notificationsToIcon(ChannelNotifications? noti, double iconSize) {
    final colorScheme = context.theme.colorScheme.secondary;
    final foregroundColor = context.defaultIconColor(colorScheme).withOpacityExt(0.75);
    return switch (noti) {
      ChannelNotifications.all => Icon(
        Broken.notification_bing,
        size: iconSize,
        color: foregroundColor,
      ),
      ChannelNotifications.personalized => Icon(
        Broken.notification_1,
        size: iconSize,
        color: foregroundColor,
      ),
      ChannelNotifications.none => StackedIcon(
        baseIcon: Broken.notification_1,
        secondaryIcon: Broken.slash,
        iconSize: iconSize,
        secondaryIconSize: 11.0,
        baseIconColor: foregroundColor,
        secondaryIconColor: foregroundColor,
      ),
      null => null,
    };
  }

  Future<void> _onAddGroupTap({required bool Function(String text) doesNameExist, required void Function(String text) onAdd}) async {
    final text = await showNamidaBottomSheetWithTextField(
      title: '',
      textfieldConfig: BottomSheetTextFieldConfig(
        initalControllerText: '',
        hintText: '',
        labelText: lang.group,
        validator: (value) {
          if (value == null || value.isEmpty) return lang.emptyValue;
          if (doesNameExist(value)) return lang.pleaseEnterADifferentName;
          return null;
        },
      ),
      buttonText: lang.add,
      onButtonTap: (text) => true,
    );
    if (text != null) onAdd(text);
  }

  void _showLocalFavouriteChannelsSheet(String channelId) async {
    final addedAtFirst = <String, bool>{};
    final allChannelGroups = <String>[];
    final currentGroups = (YoutubeSubscriptionsController.inst.getGroupsForChannel(channelId)).obs;
    for (var item in currentGroups.value) {
      addedAtFirst[item] = true;
      allChannelGroups.add(item);
    }
    final allGroupsFile = File(AppPaths.YT_SUBSCRIPTIONS_GROUPS_ALL);
    final list = await allGroupsFile.readAsJson() as List?;
    if (list != null) {
      for (var item in list) {
        item as String;
        if (addedAtFirst[item] != true) {
          allChannelGroups.add(item);
        }
      }
    }

    addedAtFirst.clear();

    bool didChangeCurrentGroups = false;

    await NamidaNavigator.inst.showSheet(
      builder: (context, bottomPadding, maxWidth, maxHeight) {
        final textTheme = context.textTheme;
        return PopScope(
          onPopInvokedWithResult: (didPop, _) {
            if (!didPop) return;
            if (didChangeCurrentGroups) YoutubeSubscriptionsController.inst.saveFile();
          },
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 12.0).add(const EdgeInsets.only(bottom: 8.0)),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                const SizedBox(height: 32.0),
                Align(
                  alignment: Alignment.centerLeft,
                  child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 24.0),
                    child: Text(
                      '${lang.configure}: ${lang.channel}',
                      style: textTheme.displayLarge,
                    ),
                  ),
                ),
                const SizedBox(height: 32.0),
                ObxO(
                  rx: YoutubeSubscriptionsController.inst.availableChannels,
                  builder: (context, availableChannels) {
                    final favouriteChannel = availableChannels[channelId]?.subscribed;
                    return TextButton(
                      onPressed: () {
                        YoutubeSubscriptionsController.inst.toggleChannelSubscription(channelId);
                      },
                      child: Row(
                        mainAxisSize: MainAxisSize.min,
                        children: [
                          Icon(
                            favouriteChannel == true ? Broken.tick_square : Broken.video,
                            size: 28.0,
                          ),
                          const SizedBox(width: 8.0),
                          NamidaButtonText(
                            favouriteChannel == true ? lang.removeFromFavourites : lang.addToFavourites,
                            style: const TextStyle(
                              fontSize: 18.0,
                            ),
                          ),
                        ],
                      ),
                    );
                  },
                ),
                const SizedBox(height: 12.0),
                const NamidaContainerDivider(),
                const SizedBox(height: 12.0),
                Align(
                  alignment: Alignment.centerLeft,
                  child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 24.0),
                    child: Row(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Expanded(
                          child: Text(
                            lang.group,
                            style: textTheme.displayLarge,
                          ),
                        ),
                        NamidaInkWellButton(
                          sizeMultiplier: 1.1,
                          text: lang.add,
                          icon: Broken.add_circle,
                          onTap: () => _onAddGroupTap(
                            doesNameExist: allChannelGroups.contains,
                            onAdd: (text) {
                              allChannelGroups.add(text);
                              allGroupsFile.writeAsJson(allChannelGroups);
                            },
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
                const SizedBox(height: 12.0),
                Flexible(
                  child: SmoothSingleChildScrollView(
                    // scrollDirection: Axis.horizontal,
                    child: ObxO(
                      rx: currentGroups,
                      builder: (context, activeG) => Wrap(
                        crossAxisAlignment: WrapCrossAlignment.start,
                        // direction: Axis.vertical,
                        children: allChannelGroups
                            .map(
                              (e) => Padding(
                                padding: const EdgeInsets.all(3.0),
                                child: ListTileWithCheckMark(
                                  icon: Broken.smileys,
                                  title: e,
                                  active: activeG.contains(e),
                                  onTap: () {
                                    didChangeCurrentGroups = true; // we could save here but would be inefficient. we instead save on pop
                                    currentGroups.addOrRemove(e);
                                  },
                                  expanded: false,
                                ),
                              ),
                            )
                            .toFixedList(),
                      ),
                    ),
                  ),
                ),
                const SizedBox(height: 12.0),
                Align(
                  alignment: Alignment.centerRight,
                  child: NamidaButton(
                    text: lang.done,
                    onTap: Navigator.of(context).pop,
                  ),
                ),
              ],
            ),
          ),
        );
      },
    );
    currentGroups.close.executeAfterDelay();
  }

  @override
  Widget build(BuildContext context) {
    final subscribed = _currentSubscribed == true;
    const iconSize = 20.0;
    final notificationIcon = _notificationsToIcon(_currentNotificationsStatus, iconSize);

    final canDisplaySubscribeTextWidget = switch (widget.subscribeTextDisplayPolicy) {
      SubscribeTextDisplayPolicy.never => false,
      SubscribeTextDisplayPolicy.always => true,
      SubscribeTextDisplayPolicy.onlyIfNotSubscribed => !subscribed,
    };

    final subscribeTextWidget = canDisplaySubscribeTextWidget
        ? NamidaLoadingSwitcher(
            size: 24.0,
            builder: (loadingController) => NamidaButton(
              dense: true,
              minHeight: NamidaButton.kDefaultMinHeight * 0.9,
              colors: subscribed ? .dimmed : .mid,
              text: subscribed ? lang.subscribed : lang.subscribe,
              onTap: () async {
                final info = widget.mainChannelInfo.value;
                if (info == null) return;
                if (subscribed) {
                  final confirmed = await _confirmUnsubscribe();
                  if (!confirmed) return;
                }
                _onChangeSubscribeStatus(
                  subscribed,
                  loadingController.startLoading,
                  loadingController.stopLoading,
                );
              },
            ),
          )
        : const SizedBox();

    return LongPressDetector(
      enableSecondaryTap: true,
      onLongPress: widget.channelID == null ? null : () => _showLocalFavouriteChannelsSheet(widget.channelID!),
      child: ObxO(
        rx: _YTSubscribeButtonManager._activeModifications,
        builder: (context, activeModifications) => AnimatedEnabled(
          enabled: activeModifications[widget.channelID] != true && _currentSubscribed != null,
          durationMS: 300,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              if (widget.subscribeTextOnLeft) subscribeTextWidget,
              Opacity(
                opacity: subscribed && notificationIcon != null ? 1.0 : 0.0, // keep visible to maintain stable space for text
                child: NamidaIconButton(
                  horizontalPadding: 8.0,
                  onPressed: () {
                    final info = widget.mainChannelInfo.value;
                    if (info == null) return;
                    _onNotificationsTap();
                  },
                  icon: null,
                  child: notificationIcon,
                ),
              ),
              if (!widget.subscribeTextOnLeft) subscribeTextWidget,
            ],
          ),
        ),
      ),
    );
  }
}

enum SubscribeTextDisplayPolicy {
  never,
  always,
  onlyIfNotSubscribed,
}
