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

import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flux_localization/flux_localization.dart';
import 'package:flux_ui/flux_ui.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';

import '../../data/boxes.dart';
import '../../menu/maintab_delegate.dart';
import '../../models/app_model.dart';
import '../../models/entities/product_category_menu_style.dart';
import '../../models/index.dart'
    show BackDropArguments, CartModel, Product, RecentModel, UserModel, User;
import '../../modules/analytics/analytics.dart';
import '../../modules/dynamic_layout/helper/helper.dart';
import '../../modules/dynamic_layout/index.dart';
import '../../routes/flux_navigate.dart';
import '../../screens/blog/views/blog_list_category.dart';
import '../../screens/index.dart';
import '../../services/index.dart';
import '../../widgets/common/webview.dart';
import '../config.dart';
import '../constants.dart';
import '../events.dart';
import '../tools.dart';
import 'flash.dart';

class NavigateTools {
  static final Map<String, dynamic> _pendingAction = {};

  static Future onTapNavigateOptions({
    required BuildContext context,
    required Map config,
    List<Product>? products,
  }) async {
    /// support to show the product detail
    if (config.hasProduct) {
      unawaited(
        FlashHelper.message(
          context,
          message: S.of(context).loading,
          duration: const Duration(seconds: 1),
        ),
      );

      /// Prevent users from tapping multiple times.
      if (_pendingAction[config.product.toString()] == true) {
        return;
      }

      _pendingAction[config.product.toString()] = true;

      /// for pre-load the product detail
      final service = Services();
      var product = await service.api.getProduct(config.product.toString());

      _pendingAction.remove(config.product.toString());

      return navigateToProductDetail(
        context,
        product: product,
        config: ProductConfig.fromJson(config),
      );
    }
    if (config.hasTab) {
      return MainTabControlDelegate.getInstance().changeTab(config.tab);
    }
    if (config.hasTabNumber) {
      final index = (Helper.formatInt(config.tabNumber, 1) ?? 1) - 1;
      var appModel = Provider.of<AppModel>(context, listen: false);
      final userModel = Provider.of<UserModel>(context, listen: false);
      var tabData = appModel.appConfig?.tabBar[index];

      if (tabData != null) {
        var routeData;
        if ([
          'chat-gpt',
          'image-generate',
          'text-generate',
        ].contains(tabData.layout)) {
          routeData = {
            'identifier': userModel.user?.email,
            'loginCallback': () async {
              await FluxNavigate.pushNamed(
                RouteList.login,
                forceRootNavigator: true,
                context: context,
              );
              final userModel = Provider.of<UserModel>(context, listen: false);
              return userModel.user?.email;
            },
          };
        }
        if (!tabData.visible) {
          return FluxNavigate.pushNamed(
            RouteList.pageTab,
            arguments: routeData ?? tabData,
            context: context,
          );
        }
        if (tabData.isFullscreen) {
          return FluxNavigate.pushNamed(
            tabData.layout.toString(),
            arguments: routeData ?? tabData,
            forceRootNavigator: true,
            context: context,
          );
        }
      }
      return MainTabControlDelegate.getInstance().tabAnimateTo(index);
    }
    if (config.hasScreen) {
      var tabData = TabBarMenuConfig(jsonData: {});
      var screen = config.screen.toString();
      var routeData;
      var appModel = Provider.of<AppModel>(context, listen: false);
      tabData =
          appModel.appConfig?.tabBar.firstWhereOrNull(
            (element) => element.layout == screen,
          ) ??
          tabData;
      final userModel = Provider.of<UserModel>(context, listen: false);

      if ([
        'chat-gpt',
        'image-generate',
        'text-generate',
      ].contains(tabData.layout)) {
        routeData = {
          'identifier': userModel.user?.email,
          'loginCallback': () async {
            await FluxNavigate.pushNamed(
              RouteList.login,
              forceRootNavigator: true,
              context: context,
            );
            final userModel = Provider.of<UserModel>(context, listen: false);
            return userModel.user?.email;
          },
        };
      } else if (screen == 'orders') {
        if (!userModel.loggedIn) {
          await navigateToLogin(context);
        }

        final user = context.read<UserModel>().user;

        if (user == null) {
          return;
        }

        routeData = user;
      }

      if (config.isFullscreen) {
        return FluxNavigate.pushNamed(
          screen,
          arguments: routeData ?? tabData,
          context: context,
        );
      }

      return Navigator.of(
        context,
      ).pushNamed(screen, arguments: routeData ?? tabData);
    }

    /// Launch the URL from external
    if (config.hasUrlLaunch) {
      await Tools.launchURL(
        config.urlLaunch,
        mode: LaunchMode.externalApplication,
      );
      return;
    }

    /// support to show blog detail
    if (config.hasBlog) {
      final id = config.blog.toString();
      Analytics.triggerTapBlog(Blog(id: id));
      return FluxNavigate.pushNamed(
        RouteList.detailBlog,
        arguments: BlogDetailArguments(id: id),
        context: context,
      );
    }

    /// support to show blog category
    if (config.hasBlogCategory) {
      return Navigator.push(
        context,
        MaterialPageRoute<void>(
          builder: (BuildContext context) =>
              BlogListCategory(id: config.blogCategory.toString()),
          fullscreenDialog: true,
        ),
      );
    }

    if (config.hasCoupon) {
      return Navigator.of(context).push(
        MaterialPageRoute(
          fullscreenDialog: true,
          builder: (context) => CouponList(
            couponCode: config.coupon.toString(),
            onSelect: (String couponCode) {
              UserBox().savedCoupon = couponCode;
              Provider.of<CartModel>(context, listen: false).loadSavedCoupon();

              Tools.showSnackBar(
                ScaffoldMessenger.of(context),
                S.of(context).couponHasBeenSavedSuccessfully,
              );
            },
          ),
        ),
      );
    }

    /// Navigate to vendor store on Banner Image
    if (config.hasVendor) {
      await Navigator.of(context).push(
        MaterialPageRoute(
          builder: (context) =>
              Services().widget.renderVendorScreen(config.vendor),
        ),
      );
      return;
    }

    /// support to show the post detail
    if (config.hasUrl) {
      return FluxNavigate.push(
        MaterialPageRoute(
          builder: (context) => WebView(
            config.url,
            enableBackward: config.enableBackward,
            enableForward: config.enableForward,
            enableClose: config.enableClose,
            title: config.title,
            script: config.script ?? '',
            requiredLogin: config.requiredLogin,
          ),
        ),
        context: context,
      );
    } else {
      /// For static image
      if ((config.hasCategory == false ||
              config.category == kEmptyCategoryID) &&
          config.hasTag == false &&
          (products?.isEmpty ?? true) &&
          config.hasLocation == false &&
          config.hasAdvancedParams == false) {
        return;
      }

      final category = config.category;
      final showSubcategory = config.isShowSubcategory;
      final categoryMenuStyle = ProductCategoryMenuStyle.fromString(
        '${config['productCategoryMenuStyle']}',
      );
      final categoryMenuShowDepth =
          bool.tryParse('${config['categoryMenuShowDepth']}') ??
          bool.tryParse('${config['categoryShowDepth']}') ??
          false;
      final allowFilterMultipleCategory = bool.tryParse(
        '${config['allowFilterMultipleCategory']}',
      );

      if (category != null && showSubcategory) {
        unawaited(
          FluxNavigate.pushNamed(
            RouteList.subCategories,
            arguments: SubcategoryArguments(
              parentId: category.toString(),
              categoryMenuStyle: categoryMenuStyle,
              categoryMenuShowDepth: categoryMenuShowDepth,
              allowFilterMultipleCategory: allowFilterMultipleCategory,
            ),
            context: context,
          ),
        );
        return;
      }

      /// Default navigate to show the list products
      await FluxNavigate.pushNamed(
        RouteList.backdrop,
        arguments: BackDropArguments(
          config: config,
          data: products,
          categoryMenuStyle: categoryMenuStyle,
          categoryMenuShowDepth: categoryMenuShowDepth,
          allowFilterMultipleCategory: allowFilterMultipleCategory,
        ),
        context: context,
      );
    }
  }

  static void onTapOpenDrawerMenu(BuildContext context) {
    final isLayoutDesktop = Layout.isDisplayDesktop(context);

    if (isLayoutDesktop) {
      eventBus.fire(const EventSwitchStateCustomDrawer());
    } else {
      eventBus.fire(const EventDrawerSettings());
      eventBus.fire(const EventOpenNativeDrawer());
    }
  }

  static void navigateHome(BuildContext context) {
    navigateToRootTab(context, RouteList.home);
  }

  static void navigateToDefaultTab(BuildContext context) {
    Navigator.popUntil(context, (route) => route.isFirst);
    MainTabControlDelegate.getInstance().changeToDefaultTab();
  }

  static void navigateToRootTab(BuildContext context, String name) {
    Navigator.popUntil(context, (route) => route.isFirst);
    MainTabControlDelegate.getInstance().changeTab(name);
  }

  static Future<void> navigateToLogin(
    BuildContext context, {
    bool replacement = false,
  }) async {
    if (ServerConfig().isSupportAuthWebView) {
      final value = await _getFluxNavigate(
        routeName: RouteList.loginWithWebview,
        replacement: replacement,
        context: context,
      );

      return NavigateTools.redirect(context, value);
    }

    if (kLoginSetting.smsLoginAsDefault) {
      await navigateToLoginSms(context, replacement: replacement);
      return;
    }
    await _getFluxNavigate(
      routeName: RouteList.login,
      replacement: replacement,
      context: context,
    );
  }

  static Future<void> navigateToLoginSms(
    BuildContext context, {
    bool replacement = false,
  }) async {
    if (kAdvanceConfig.enableDigitsMobileLogin) {
      await _getFluxNavigate(
        routeName: RouteList.digitsMobileLogin,
        replacement: replacement,
        context: context,
      );
      return;
    }
    await _getFluxNavigate(
      routeName: RouteList.loginSMS,
      replacement: replacement,
      context: context,
    );
  }

  static Future<Object?> _getFluxNavigate({
    required BuildContext context,
    required String routeName,
    required bool replacement,
  }) {
    if (replacement) {
      return FluxNavigate.pushReplacementNamed(
        routeName,
        forceRootNavigator: true,
        context: context,
      );
    }
    return FluxNavigate.pushNamed(
      routeName,
      forceRootNavigator: true,
      context: context,
    );
  }

  static dynamic _pushRouteName(
    BuildContext context,
    String routeName, {
    bool replacement = false,
    Object? arguments,
  }) {
    if (replacement) {
      return Navigator.of(
        context,
      ).pushReplacementNamed(routeName, arguments: arguments);
    }

    return Navigator.of(context).pushNamed(routeName, arguments: arguments);
  }

  static void navigateAfterLogin(User user, BuildContext context) {
    eventBus.fire(const EventLoggedIn());
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('${S.of(context).welcome} ${user.name}!')),
    );

    if (kLoginSetting.isRequiredLogin) {
      Navigator.of(context).pushReplacementNamed(RouteList.dashboard);
      return;
    }

    var routeFound = false;
    var routeNames = [RouteList.dashboard, RouteList.productDetail];
    Navigator.popUntil(context, (route) {
      if (routeNames.any(
        (element) => route.settings.name?.contains(element) ?? false,
      )) {
        routeFound = true;
      }
      return routeFound || route.isFirst;
    });

    if (!routeFound) {
      Navigator.of(context).pushReplacementNamed(RouteList.dashboard);
    }
  }

  static void navigateRegister(context, {bool replacement = false}) {
    if (ServerConfig().isSupportAuthWebView) {
      _getFluxNavigate(
        routeName: RouteList.signUpWithWebview,
        replacement: replacement,
        context: context,
      ).then((value) => NavigateTools.redirect(context, value));
    } else if (kAdvanceConfig.enableMembershipUltimate) {
      _pushRouteName(
        context,
        RouteList.memberShipUltimatePlans,
        replacement: replacement,
      );
    } else if (kAdvanceConfig.enableWooCommerceWholesalePrices &&
        ServerConfig().isWooPluginSupported) {
      _pushRouteName(
        context,
        RouteList.wholesaleSignUp,
        replacement: replacement,
      );
    } else if (kAdvanceConfig.b2bKingConfig.enabled &&
        ServerConfig().isWooPluginSupported) {
      _pushRouteName(
        context,
        RouteList.b2bkingSignUp,
        replacement: replacement,
      );
    } else if (kAdvanceConfig.enablePaidMembershipPro) {
      _pushRouteName(
        context,
        RouteList.paidMemberShipProPlans,
        replacement: replacement,
      );
    } else if (kAdvanceConfig.enableDigitsMobileLogin &&
        ServerConfig().isWooPluginSupported) {
      _pushRouteName(
        context,
        RouteList.digitsMobileLoginSignUp,
        replacement: replacement,
      );
    } else {
      if (kLoginSetting.smsLoginAsDefault) {
        navigateToLoginSms(context, replacement: replacement);
        return;
      }

      _pushRouteName(context, RouteList.register, replacement: replacement);
    }
  }

  static void goBackLogin(BuildContext context) {
    var routeFound = false;
    var routeNames = [RouteList.login];

    Navigator.popUntil(context, (route) {
      if (routeNames.any(
        (element) => route.settings.name?.contains(element) ?? false,
      )) {
        routeFound = true;
      }
      return routeFound || route.isFirst;
    });

    if (!routeFound) {
      Navigator.of(context).pushReplacementNamed(RouteList.login);
    }
  }

  static Future<void> redirect(
    BuildContext context,
    dynamic arguments, {
    bool isReplacement = false,
  }) async {
    if (arguments == null) {
      return;
    }
    if (arguments is String) {
      final screenName = arguments.toString();

      if (screenName.contains('mailto:')) {
        // handle send email
        return;
      }

      switch (screenName) {
        case 'home':
          navigateHome(context);
          break;
        case 'login':
          await navigateToLogin(context, replacement: isReplacement);
          break;
        case 'register':
          navigateRegister(context, replacement: isReplacement);
          break;
      }
    }
  }

  static void navigateToProductDetail(
    BuildContext context, {
    bool isFromSearchScreen = false,
    Product? product,
    ProductConfig? config,
  }) async {
    if (product == null || product.isEmptyProduct()) return;

    Analytics.triggerTapProduct(
      product,
      config?.category?.join(',') ??
          config?.tag?.join(',') ??
          product.categoryId,
      config?.name,
    );

    try {
      // Fix case open product from sidemenu, but the sidemenu is closed soon so
      // the context is not available anymore
      Provider.of<RecentModel>(
        context,
        listen: false,
      ).addRecentProduct(product);
    } catch (e, stackTrace) {
      printError(e, stackTrace);
    }

    if (kProductCard.typeShouldOpenInWebview.contains(product.type)) {
      var productLink = product.permalink ?? '';
      if (productLink.isNotEmpty) {
        if (ServerConfig().isWooType || ServerConfig().isWordPress) {
          final cookie = context.read<UserModel>().user?.cookie;
          productLink = productLink.addWooCookieToUrl(cookie);
        }

        final uri = Uri.tryParse(productLink);

        if (uri != null) {
          try {
            final canLaunch = await canLaunchUrl(uri);
            if (canLaunch) {
              if (kIsWeb || Tools.needToOpenExternalApp(productLink)) {
                unawaited(Tools.launchURL(productLink));
                return;
              }
              unawaited(
                FluxNavigate.push(
                  MaterialPageRoute(
                    builder: (context) =>
                        WebView(productLink, title: product.name),
                  ),
                  context: context,
                  forceRootNavigator: true,
                ),
              );
              return;
            }
          } catch (err, trace) {
            printError(err, trace);
          }
        }
      }
    }

    if (isFromSearchScreen) {
      unawaited(
        Navigator.of(
          context,
        ).pushNamed(RouteList.productDetail, arguments: product),
      );
      return;
    }

    unawaited(
      FluxNavigate.pushNamed(
        RouteList.productDetail,
        arguments: product,
        context: context,
      ),
    );
  }

  /// Because Flutter does not exit app correctly when using
  /// `await SystemNavigator.pop();`.
  /// Ref: https://github.com/flutter/flutter/issues/98133
  ///
  /// Just make the app goes to background with the animation, not exit
  /// immediately. Then exit the app on background
  static Future<bool> forceExitApp() {
    Future.delayed(const Duration(milliseconds: 250), () {
      exit(0);
    });

    return Future.delayed(const Duration(milliseconds: 100), () {
      return true;
    });
  }

  /// Put the app to background
  /// Note: This method only works on Android, not iOS
  static Future<void> exitApp() async {
    await SystemNavigator.pop();
  }
}

extension _ExtensionNavigatorToolConfig on Map {
  ///
  dynamic get category => this['category'];
  dynamic get url => this['url'];
  dynamic get vendor => this['vendor'];
  dynamic get coupon => this['coupon'];
  dynamic get blogCategory => this['blog_category'];
  dynamic get blog => this['blog'];
  dynamic get urlLaunch => this['url_launch'];
  dynamic get screen => this['screen'];
  dynamic get tabNumber => this['tab_number'];
  dynamic get tab => this['tab'];
  dynamic get product => this['product'];
  dynamic get tag => this['tag'];
  dynamic get location => this['location'];
  dynamic get advancedParams => this['advancedParams'];

  ///
  bool get hasLocation => location != null;
  bool get hasTag => tag != null;
  bool get hasUrl => url != null;
  bool get hasVendor => vendor != null;
  bool get hasCoupon => coupon != null;
  bool get hasBlogCategory => blogCategory != null;
  bool get hasBlog => blog != null;
  bool get hasUrlLaunch => urlLaunch != null;
  bool get hasScreen => screen != null;
  bool get hasTabNumber => tabNumber != null;
  bool get hasTab => tab != null;
  bool get hasProduct => product != null;
  bool get hasCategory => category != null;
  bool get hasAdvancedParams =>
      advancedParams != null &&
      advancedParams is Map &&
      advancedParams.isNotEmpty;

  ///
  bool get isFullscreen => bool.tryParse('${this['fullscreen']}') ?? false;
  bool get isShowSubcategory =>
      bool.tryParse('${this['showSubcategory']}') ?? false;
  bool get enableBackward =>
      bool.tryParse('${this['enableBackward']}') ?? false;
  bool get enableForward => bool.tryParse('${this['enableForward']}') ?? false;
  bool get enableClose => bool.tryParse('${this['enableClose']}') ?? true;
  bool get requiredLogin => bool.tryParse('${this['requiredLogin']}') ?? false;
  String? get title => this['title']?.toString();
  String? get script => this['script']?.toString();
}
