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

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hub/features/backend/index.dart';
import 'package:hub/features/history/index.dart';
import 'package:hub/features/home/index.dart';
import 'package:hub/features/local/index.dart';
import 'package:hub/features/menu/index.dart';
import 'package:hub/features/property/index.dart';
import 'package:hub/features/storage/index.dart';
import 'package:hub/flutter_flow/flutter_flow_util.dart';
import 'package:hub/flutter_flow/nav/nav.dart';
import 'package:hub/pages/delivery_schedule_page/delivery_schedule_widget.dart';
import 'package:hub/pages/fast_pass_page/fast_pass_page_widget.dart';
import 'package:hub/pages/forgot_password_page/forgot_password_screen.dart';
import 'package:hub/pages/liberation_history/liberation_history_widget.dart';
import 'package:hub/pages/message_history_page/message_history_page_widget.dart';
import 'package:hub/pages/package_order_page/package_order_page.dart';
import 'package:hub/pages/people_on_the_property_page/people_on_the_property_page_widget.dart';
import 'package:hub/pages/pets_on_the_property_page/pets_history_screen.dart';
import 'package:hub/pages/pets_page/pets_page_widget.dart';
import 'package:hub/pages/preferences_settings_page/preferences_settings_widget.dart';
import 'package:hub/pages/provisional_schedule_page/provisional_schedule_widget.dart';
import 'package:hub/pages/qr_code_page/qr_code_page_widget.dart';
import 'package:hub/pages/reception_page/reception_page_widget.dart';
import 'package:hub/pages/register_visitor_page/register_visitor_page_widget.dart';
import 'package:hub/pages/reservation_page/reservation_page_widget.dart';
import 'package:hub/pages/residents_on_the_property/residents_on_the_property_screen.dart';
import 'package:hub/pages/schedule_complete_visit_page/schedule_complete_visit_page_widget.dart';
import 'package:hub/pages/sign_in_page/sign_in_page_widget.dart';
import 'package:hub/pages/sign_up_page/sign_up_page_widget.dart';
import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart';
import 'package:hub/pages/visits_on_the_property/visits_on_the_property_screen.dart';
import 'package:hub/pages/welcome_page/welcome_page_widget.dart';
import 'package:hub/shared/utils/dialog_util.dart';
import 'package:provider/provider.dart';
export 'package:go_router/go_router.dart';

export 'serialization_util.dart';

const kTransitionInfoKey = '__transition_info__';

final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

class AppStateNotifier extends ChangeNotifier {
  AppStateNotifier._();

  static AppStateNotifier? _instance;
  static AppStateNotifier get instance => _instance ??= AppStateNotifier._();

  bool showSplashImage = true;

  void stopShowingSplashImage() {
    showSplashImage = false;
    notifyListeners();
  }
}

GoRouter createRouter(AppStateNotifier appStateNotifier) {
  return GoRouter(
    navigatorKey: navigatorKey,
    initialLocation: '/',
    debugLogDiagnostics: true,
    redirect: (context, state) {
      if (Platform.isIOS) {
        if (state.uri.toString().contains("freaccess://changepass/")) {
          throw Exception('Redirecting to forgotPassword');
        }
      }
      return null;
    },
    refreshListenable: appStateNotifier,
    // errorBuilder: (context, state) => appStateNotifier.showSplashImage
    //     ? Builder( builder: (context) => Container(color: FlutterFlowTheme.of(context).primary, child: Image.asset( 'assets/images/logo.svg', fit: BoxFit.cover)))
    //     : const PeopleOnThePropertyPageWidget(),
    routes: [
      // FFRoute(name: '_initialize', path: '/',builder: (context, _) => appStateNotifier.showSplashImage
      //       ? Builder(builder: (context) => Container(color: FlutterFlowTheme.of(context).primary, child: Image.asset('assets/images/favicon.png', fit: BoxFit.cover)))
      //       : const OnBoardingPageWidget(),
      // ),
      FFRoute(
        name: '_initialize',
        path: '/',
        builder: (context, _) {
          return FutureBuilder<Widget>(
            future: () async {
              final bool isLogged =
                  await StorageHelper().get(SecureStorageKey.isLogged.value) ==
                      'true';
              final bool haveLocal =
                  await StorageHelper().get(SecureStorageKey.haveLocal.value) ==
                      'true';
              final bool haveUserUUID =
                  (await StorageHelper().get(ProfileStorageKey.userUUID.key))
                          ?.isNotEmpty ??
                      false;
              final bool haveDevUUID =
                  (await StorageHelper().get(ProfileStorageKey.devUUID.key))
                          ?.isNotEmpty ??
                      false;

              if (isLogged && haveDevUUID && haveUserUUID) {
                return haveLocal
                    ? MultiBlocProvider(
                        providers: [
                          BlocProvider(
                            create: (context) => MenuBloc(
                              style: MenuView.list_grid,
                              item: EnumMenuItem.button,
                              entries: MenuEntry.getEntriesByType(
                                  MenuEntryType.Home),
                            )..add(MenuEvent()),
                          ),
                          BlocProvider<HomeBloc>(
                            create: (context) => HomeBloc()..add(HomeEvent()),
                          ),
                          BlocProvider(
                            create: (context) =>
                                LocalProfileBloc()..add(LocalProfileEvent()),
                          ),
                        ],
                        child: HomePageWidget(
                            key: UniqueKey(), LocalsRepositoryImpl().update),
                      )
                    : const ReceptionPageWidget();
              } else {
                return const WelcomePage();
              }
            }(),
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return const Center(child: CircularProgressIndicator());
              } else if (snapshot.hasError) {
                DialogUtil.error(context, snapshot.error.toString());
                return const WelcomePage();
              } else {
                return snapshot.data!;
              }
            },
          );
        },
      ),
      FFRoute(
          name: 'forgotPassword',
          path: '/forgotPassword',
          builder: (context, params) {
            late final String email =
                params.getParam('email', ParamType.String);
            late final String token =
                params.getParam('token', ParamType.String);

            return ForgotPasswordScreen(
              key: ValueKey('ForgotPasswordScreen'),
              email: email,
              token: token,
            );
          }),
      FFRoute(
        name: 'aboutSystemPage',
        path: '/aboutSystemPage',
        builder: (context, params) => AboutSystemPage(),
      ),
      FFRoute(
          name: 'homePage',
          path: '/homePage',
          builder: (context, params) {
            final Future<bool> Function(BuildContext context)? update =
                params.getParam('update', ParamType.Function);
            return MultiBlocProvider(
              providers: [
                BlocProvider(
                  create: (context) => MenuBloc(
                    style: MenuView.list_grid,
                    item: EnumMenuItem.button,
                    entries: MenuEntry.getEntriesByType(MenuEntryType.Home),
                  )..add(MenuEvent()),
                ),
                BlocProvider<HomeBloc>(
                  create: (context) => HomeBloc()..add(HomeEvent()),
                ),
                BlocProvider(
                  create: (context) =>
                      LocalProfileBloc()..add(LocalProfileEvent()),
                ),
              ],
              child: HomePageWidget(key: UniqueKey(), update),
            );
          }),
      FFRoute(
          name: 'petsOnThePropertyPage',
          path: '/petsOnThePropertyPage',
          builder: (context, params) =>
              Scaffold(body: const PetsHistoryScreen(isApp: true))),
      FFRoute(
          name: 'vehiclesOnThePropertyPage',
          path: '/vehiclesOnThePropertyPage',
          builder: (context, params) => const VehicleOnTheProperty()),
      FFRoute(
          name: 'receptionPage',
          path: '/receptionPage',
          builder: (context, params) => const ReceptionPageWidget()),
      FFRoute(
          name: 'messageHistoryPage',
          path: '/messageHistoryPage',
          builder: (context, params) => const MessageHistoryPageWidget()),
      FFRoute(
          name: 'registerVisitorPage',
          path: '/registerVisitorPage',
          builder: (context, params) => const RegisterVisitorPageWidget()),
      FFRoute(
          name: 'scheduleCompleteVisitPage',
          path: '/scheduleCompleteVisitPage',
          builder: (context, params) =>
              const ScheduleCompleteVisitPageWidget()),
      FFRoute(
          name: 'deliverySchedule',
          path: '/deliverySchedule',
          builder: (context, params) => const DeliverySchedule()),
      FFRoute(
          name: 'provisionalSchedule',
          path: '/provisionalSchedule',
          builder: (context, params) => const ProvisionalSchedule()),
      FFRoute(
          name: 'fastPassPage',
          path: '/fastPassPage',
          builder: (context, params) => FastPassPageWidget()),
      FFRoute(
          name: 'preferencesSettings',
          path: '/preferencesSettings',
          builder: (context, params) => PreferencesPageWidget()),
      FFRoute(
          name: 'aboutProperty',
          path: '/aboutProperty',
          builder: (context, params) => AboutPropertyPage()),
      FFRoute(
          name: 'residentsOnThePropertyPage',
          path: '/residentsOnThePropertyPage',
          builder: (context, params) => ResidentsOnTheProperty()),
      FFRoute(
          name: 'visitsOnThePropertyPage',
          path: '/visitsOnThePropertyPage',
          builder: (context, params) => VisitsOnTheProperty()),
      FFRoute(
          name: 'peopleOnThePropertyPage',
          path: '/peopleOnThePropertyPage',
          builder: (context, params) => PeopleOnThePropertyPage()),
      FFRoute(
          name: 'acessHistoryPage',
          path: '/acessHistoryPage',
          builder: (context, params) => AccessHistoryScreen(opt: const {
                'personType': '.*',
                'accessType': '.*',
                'search': '.*'
              })),
      FFRoute(
          name: 'provisionalHistoryPage',
          path: '/provisionalHistoryPage',
          builder: (context, params) => ProvisionalHistoryPage()),
      FFRoute(
          name: 'liberationHistory',
          path: '/liberationHistory',
          builder: (context, params) => const LiberationHistoryWidget()),
      FFRoute(
          name: 'signInPage',
          path: '/signInPage',
          builder: (context, params) => const SignInPageWidget()),
      FFRoute(
          name: 'signUpPage',
          path: '/signUpPage',
          builder: (context, params) => const SignUpPageWidget()),
      FFRoute(
          name: 'welcomePage',
          path: '/welcomePage',
          builder: (context, params) => const WelcomePage()),
      FFRoute(
          name: 'qrCodePage',
          path: '/qrCodePage',
          builder: (context, params) => const QrCodePageWidget()),
      FFRoute(
          name: 'preferencesPage',
          path: '/preferencesPage',
          builder: (context, params) => PreferencesPageWidget()),
      FFRoute(
          name: 'packageOrder',
          path: '/packageOrder',
          builder: (context, params) => const PackageOrderPage()),
      FFRoute(
          name: 'reservation',
          path: '/reservation',
          builder: (context, params) => ReservationPageWidget()),
      FFRoute(
        name: 'petsPage',
        path: '/petsPage',
        builder: (context, params) {
          final pet = params.getParam('pet', ParamType.JSON);
          return PetsPageWidget(pet: pet);
        },
      ),
      // FFRoute(name: 'settingsPage', path: '/settingsPage', builder: (context, params) => params.isEmpty ? const NavBarPage(initialPage: 'settingsPage') : const SettingsPageWidget())
    ].map((r) => r.toRoute(appStateNotifier)).toList(),
  );
}

extension NavParamExtensions on Map<String, String?> {
  Map<String, String> get withoutNulls => Map.fromEntries(
        entries
            .where((e) => e.value != null)
            .map((e) => MapEntry(e.key, e.value!)),
      );
}

extension NavigationExtensions on BuildContext {
  void safePop() {
    if (canPop())
      pop();
    else
      go('/');
  }
}

extension _GoRouterStateExtensions on GoRouterState {
  Map<String, dynamic> get extraMap =>
      extra != null ? extra as Map<String, dynamic> : {};
  Map<String, dynamic> get allParams => <String, dynamic>{}
    ..addAll(pathParameters)
    ..addAll(uri.queryParameters)
    ..addAll(extraMap);
  TransitionInfo get transitionInfo => extraMap.containsKey(kTransitionInfoKey)
      ? extraMap[kTransitionInfoKey] as TransitionInfo
      : TransitionInfo.appDefault();
}

extension GoRouterLocationExtension on GoRouter {
  String getCurrentLocation() {
    final RouteMatch lastMatch = routerDelegate.currentConfiguration.last;
    final RouteMatchList matchList = lastMatch is ImperativeRouteMatch
        ? lastMatch.matches
        : routerDelegate.currentConfiguration;
    return matchList.uri.toString();
  }
}

class FFParameters {
  FFParameters(this.state, [this.asyncParams = const {}]);
  final GoRouterState state;

  final Map<String, Future<dynamic> Function(String)> asyncParams;
  Map<String, dynamic> futureParamValues = {};

  bool get isEmpty =>
      state.allParams.isEmpty ||
      (state.allParams.length == 1 &&
          state.extraMap.containsKey(kTransitionInfoKey));
  bool isAsyncParam(MapEntry<String, dynamic> param) =>
      asyncParams.containsKey(param.key) && param.value is String;
  bool get hasFutures => state.allParams.entries.any(isAsyncParam);
  Future<bool> completeFutures() => Future.wait(
        state.allParams.entries.where(isAsyncParam).map(
          (param) async {
            final doc = await asyncParams[param.key]!(param.value)
                .onError((_, __) => null);
            if (doc != null) {
              futureParamValues[param.key] = doc;
              return true;
            } else
              return false;
          },
        ),
      ).onError((_, __) => [false]).then((v) => v.every((e) => e));

  dynamic getParam<T>(String paramName, ParamType type,
      {bool isList = false, StructBuilder<T>? structBuilder}) {
    if (futureParamValues.containsKey(paramName))
      return futureParamValues[paramName];
    if (!state.allParams.containsKey(paramName)) return null;
    final param = state.allParams[paramName];
    if (param is! String) return param;
    return deserializeParam<T>(param, type, isList,
        structBuilder: structBuilder);
  }
}

class FFRoute {
  const FFRoute({
    required this.name,
    required this.path,
    required this.builder,
    this.requireAuth = false,
    this.asyncParams = const {},
    this.routes = const [],
  });

  final String name;
  final String path;
  final bool requireAuth;
  final Map<String, Future<dynamic> Function(String)> asyncParams;
  final Widget Function(BuildContext, FFParameters) builder;
  final List<GoRoute> routes;

  GoRoute toRoute(AppStateNotifier appStateNotifier) => GoRoute(
        name: name,
        path: path,
        pageBuilder: (context, state) {
          fixStatusBarOniOS16AndBelow(context);
          final ffParams = FFParameters(state, asyncParams);
          final page = ffParams.hasFutures
              ? FutureBuilder(
                  future: ffParams.completeFutures(),
                  builder: (context, _) => builder(context, ffParams),
                )
              : builder(context, ffParams);
          final child = page;
          final transitionInfo = state.transitionInfo;
          return transitionInfo.hasTransition
              ? CustomTransitionPage(
                  key: state.pageKey,
                  child: child,
                  transitionDuration: transitionInfo.duration,
                  transitionsBuilder:
                      (context, animation, secondaryAnimation, child) =>
                          PageTransition(
                    type: transitionInfo.transitionType,
                    duration: transitionInfo.duration,
                    reverseDuration: transitionInfo.duration,
                    alignment: transitionInfo.alignment,
                    child: child,
                  ).buildTransitions(
                              context, animation, secondaryAnimation, child),
                )
              : MaterialPage(key: state.pageKey, child: child);
        },
        routes: routes,
      );
}

class TransitionInfo {
  const TransitionInfo({
    required this.hasTransition,
    this.transitionType = PageTransitionType.fade,
    this.duration = const Duration(milliseconds: 300),
    this.alignment,
  });

  final bool hasTransition;
  final PageTransitionType transitionType;
  final Duration duration;
  final Alignment? alignment;

  static TransitionInfo appDefault() =>
      const TransitionInfo(hasTransition: false);
}

class RootPageContext {
  const RootPageContext(this.isRootPage, [this.errorRoute]);
  final bool isRootPage;
  final String? errorRoute;

  static bool isInactiveRootPage(BuildContext context) {
    final rootPageContext = context.read<RootPageContext?>();
    final isRootPage = rootPageContext?.isRootPage ?? false;
    final location = GoRouterState.of(context).uri.toString();
    return isRootPage &&
        location != '/' &&
        location != rootPageContext?.errorRoute;
  }

  static Widget wrap(Widget child, {String? errorRoute}) =>
      Provider.value(value: RootPageContext(true, errorRoute), child: child);
}
