import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; // Importa il tuo SessionCubit e lo State import 'package:flux/core/blocs/session/session_cubit.dart'; import 'package:flux/core/data/core_repository.dart'; import 'package:flux/core/widgets/set_password_screen.dart'; import 'package:flux/features/customers/ui/customer_mobile_upload_screen.dart'; import 'package:flux/features/auth/ui/auth_screen.dart'; import 'package:flux/features/customers/blocs/customer_files_bloc.dart'; import 'package:flux/features/customers/models/customer_model.dart'; import 'package:flux/features/customers/ui/customer_detail_screen.dart'; import 'package:flux/features/home/ui/home_screen.dart'; import 'package:flux/features/master_data/products/ui/products_screen.dart'; import 'package:flux/features/onboarding/blocs/onboarding_cubit.dart'; import 'package:flux/features/onboarding/ui/onboarding_screen.dart'; import 'package:flux/features/services/blocs/service_files_bloc.dart'; import 'package:flux/features/services/models/service_model.dart'; import 'package:flux/features/services/ui/service_form_screen/service_form_screen.dart'; import 'package:flux/features/services/ui/service_form_screen/service_mobile_upload_screen.dart'; import 'package:get_it/get_it.dart'; import 'package:go_router/go_router.dart'; class AppRouter { static GoRouter createRouter(SessionCubit sessionCubit) { return GoRouter( initialLocation: '/', // MAGIA 1: Il router "ascolta" ogni singolo respiro del SessionCubit refreshListenable: GoRouterRefreshStream(sessionCubit.stream), // MAGIA 2: Il Buttafuori Supremo redirect: (context, state) { final sessionState = sessionCubit.state; final isGoingToLogin = state.matchedLocation == '/login'; final isGoingToOnboarding = state.matchedLocation == '/onboarding'; final isGoingToSetPassword = state.matchedLocation == '/set-password'; // Caso 1: L'app si sta ancora avviando. // Restituiamo null per farlo rimanere sulla SplashScreen del main.dart if (sessionState.status == SessionStatus.initial) { return null; } // Caso 2: Utente NON loggato. if (sessionState.status == SessionStatus.unauthenticated) { // Se sta già andando al login, lascialo andare. Altrimenti, forzalo al login. if (isGoingToLogin || isGoingToSetPassword) return null; return '/login'; } // Caso 3: Utente loggato MA manca un pezzo dell'azienda (Flusso Canalizzatore) if (sessionState.status == SessionStatus.onboardingRequired) { // Se sta già andando all'onboarding, ok. Altrimenti forzalo lì. // Non può "scappare" digitando l'URL della dashboard! return isGoingToOnboarding ? null : '/onboarding'; } // Caso 4: Utente loggato e configurato (Tutto OK!) if (sessionState.status == SessionStatus.authenticated) { // Attenzione: un utente appena invitato viene considerato "loggato" // da Supabase appena clicca il link. Quindi se sta andando su /set-password, // dobbiamo permetterglielo e non rimbalzarlo! if (isGoingToLogin || isGoingToOnboarding) { return '/'; } return null; // Lascia passare per /, /customer, e anche /set-password } return null; }, routes: [ GoRoute( path: '/login', //builder: (context, state) => const LoginScreen(), builder: (context, state) => const AuthScreen(), ), GoRoute( path: '/set-password', builder: (context, state) => const SetPasswordScreen(), ), GoRoute( path: '/onboarding', builder: (context, state) => BlocProvider( create: (context) => OnboardingCubit( GetIt.I.get(), GetIt.I.get(), ), child: const OnboardingScreen(), ), // Nota: All'interno di questa schermata useremo il PageView pilotato // dall'OnboardingStep. Al router non interessa quale step è attivo, // gli basta sapere che deve stare rinchiuso qui dentro! ), GoRoute( path: '/', builder: (context, state) => const HomeScreen(), // La tua home ), GoRoute( path: '/customer/:id', builder: (context, state) { // Recuperiamo l'oggetto customer passato tramite extra final customer = state.extra as CustomerModel; return BlocProvider( create: (context) => CustomerFilesBloc(customer.id!), child: CustomerDetailScreen(customer: customer), ); }, ), GoRoute( path: '/customer/:id/upload', builder: (context, state) { final customerId = state.pathParameters['id']!; // Recuperiamo il nome dalle query se vogliamo mostrarlo nel titolo, // oppure lo caricherà il bloc. final customerName = state.uri.queryParameters['name'] ?? 'Cliente'; return BlocProvider( create: (context) => CustomerFilesBloc(customerId), child: CustomerMobileUploadScreen( customerId: customerId, customerName: customerName, ), ); }, ), GoRoute( path: '/products', name: 'products', builder: (context, state) => const ProductsScreen(), ), GoRoute( path: '/service-form', name: 'service-form', builder: (context, state) { // Recuperiamo l'oggetto se passato tramite 'extra' final existingService = state.extra as ServiceModel?; // Recuperiamo l'ID se presente nell'URL final serviceId = state.uri.queryParameters['serviceId']; return BlocProvider( create: (context) => ServiceFilesBloc(serviceId: serviceId ?? existingService?.id), child: ServiceFormScreen( serviceId: serviceId ?? existingService?.id, existingService: existingService, ), ); }, ), GoRoute( path: '/service/:id/upload', builder: (context, state) { final serviceId = state.pathParameters['id']!; final serviceName = state.uri.queryParameters['name'] ?? 'Pratica'; return BlocProvider( // Inizializziamo il bloc col serviceId corretto! create: (context) => ServiceFilesBloc(serviceId: serviceId), child: ServiceMobileUploadScreen( serviceId: serviceId, serviceName: serviceName, ), ); }, ), ], ); } } /// Utility fondamentale per GoRouter: trasforma lo Stream del Cubit /// in un Listenable che GoRouter può ascoltare per forzare i redirect. class GoRouterRefreshStream extends ChangeNotifier { GoRouterRefreshStream(Stream stream) { notifyListeners(); _subscription = stream.asBroadcastStream().listen( (dynamic _) => notifyListeners(), ); } late final StreamSubscription _subscription; @override void dispose() { _subscription.cancel(); super.dispose(); } }