import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:moneymgr_mobile/providers/auth_state.dart';
import 'package:moneymgr_mobile/routes/login/login_screen.dart';
import 'package:moneymgr_mobile/routes/login/manual_auth_screen.dart';
import 'package:moneymgr_mobile/routes/login/qr_auth_screen.dart';
import 'package:moneymgr_mobile/routes/profile/profile_screen.dart';
import 'package:moneymgr_mobile/routes/scan/scan_screen.dart';
import 'package:moneymgr_mobile/routes/scan_details/scan_details.dart';
import 'package:moneymgr_mobile/routes/scans_list/scans_list_screen.dart';
import 'package:moneymgr_mobile/routes/settings/settings_screen.dart';
import 'package:moneymgr_mobile/services/router/routes_list.dart';
import 'package:moneymgr_mobile/services/storage/prefs.dart';
import 'package:moneymgr_mobile/widgets/load_startup_data.dart';
import 'package:moneymgr_mobile/widgets/scaffold_with_navigation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'router.g.dart';

/// The router config for the app.
@riverpod
GoRouter router(Ref ref) {
  // Local notifier for the current auth state. The purpose of this notifier is
  // to provide a [Listenable] to the [GoRouter] exposed by this provider.
  final authStateNotifier = ValueNotifier(AuthState.unknown);
  ref
    ..onDispose(authStateNotifier.dispose)
    ..listen(currentAuthStateProvider, (_, value) {
      authStateNotifier.value = value;
    });

  final prefs = ref.read(prefsProvider).requireValue;

  // This is the only place you need to define your navigation items. The items
  // will be propagated automatically to the router and the navigation bar/rail
  // of the scaffold.
  //
  // To configure the authentication state needed to access a particular item,
  // see [AuthState] enum.
  final navigationItems = [
    NavigationItem(
      path: capturePage,
      body: (_) => ScanScreen(),
      icon: Icons.camera_alt_outlined,
      selectedIcon: Icons.camera_alt,
      label: "Scan",
    ),
    NavigationItem(
      path: scansPage,
      body: (_) => ScansListScreen(),
      icon: Icons.list,
      selectedIcon: Icons.list_alt,
      label: "List",
      routes: [
        GoRoute(
          path: ":id",
          builder: (_, state) {
            final id = int.parse(state.pathParameters["id"]!);
            return ScanDetailScreen(id: id);
          },
        ),
      ],
    ),
    NavigationItem(
      path: profilePage,
      body: (_) => ProfileScreen(),
      icon: Icons.person_outline,
      selectedIcon: Icons.person,
      label: 'Profile',
    ),
  ];

  final router = GoRouter(
    debugLogDiagnostics: true,
    initialLocation: prefs.startOnScansListScreen() ? scansPage : capturePage,
    routes: [
      GoRoute(path: homePage, builder: (_, _) => const Scaffold()),
      GoRoute(path: authPage, builder: (_, _) => const LoginScreen()),
      GoRoute(path: qrAuthPath, builder: (_, _) => const QrAuthScreen()),
      GoRoute(
        path: manualAuthPage,
        builder: (_, _) => const ManualAuthScreen(),
      ),
      GoRoute(path: settingsPage, builder: (_, _) => const SettingsScreen()),

      // Configuration for the bottom navigation bar routes. The routes themselves
      // should be defined in [navigationItems]. Modification to this [ShellRoute]
      // config is rarely needed.
      ShellRoute(
        builder: (_, _, child) => child,
        routes: [
          for (final (index, item) in navigationItems.indexed)
            GoRoute(
              path: item.path,
              pageBuilder: (context, _) => NoTransitionPage(
                child: LoadStartupData(
                  child: ScaffoldWithNavigation(
                    selectedIndex: index,
                    navigationItems: navigationItems,
                    child: item.body(context),
                  ),
                ),
              ),
              routes: item.routes,
            ),
        ],
      ),
    ],
    refreshListenable: authStateNotifier,
    redirect: (_, state) {
      // Get the current auth state.
      final authState = ref.read(currentAuthStateProvider);

      // Check if the current path is allowed for the current auth state. If not,
      // redirect to the redirect target of the current auth state.
      if (authState.allowedPaths?.contains(state.fullPath) == false ||
          authState.forbiddenPaths?.contains(state.fullPath) == true) {
        return authState.redirectPath;
      }

      // If the current path is allowed for the current auth state, don't redirect.
      return null;
    },
  );
  ref.onDispose(router.dispose);

  return router;
}
