import 'dart:async'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import '../models/route_model.dart'; import './transportation_service.dart'; /// Global app state service to manage selected route across the entire app class AppStateService extends ChangeNotifier { static final AppStateService _instance = AppStateService._internal(); factory AppStateService() => _instance; AppStateService._internal(); final TransportationService _transportationService = TransportationService(); // Global state String? _selectedRouteId; String? _selectedRouteName; List _allRoutes = []; bool _isLoadingRoutes = false; String? _error; // Getters String? get selectedRouteId => _selectedRouteId; String? get selectedRouteName => _selectedRouteName; List get allRoutes => List.unmodifiable(_allRoutes); bool get isLoadingRoutes => _isLoadingRoutes; String? get error => _error; bool get hasSelectedRoute => _selectedRouteId != null && _selectedRouteName != null; /// Initialize app state - call this on app start Future initialize() async { await loadRoutes(); } /// Load all routes from Supabase Future loadRoutes() async { if (_isLoadingRoutes) return; _isLoadingRoutes = true; notifyListeners(); try { final result = await _transportationService.getRoutesWithCount(); final routes = result['data'] as List; final count = result['count'] as int; final error = result['error'] as String?; if (error != null) { print('Error loading routes: $error'); // Handle error but continue with empty list _allRoutes = []; } else { _allRoutes = routes; // Auto-select first route if none selected and routes available if (routes.isNotEmpty && _selectedRouteId == null) { await selectRoute(routes.first.id); } } _isLoadingRoutes = false; notifyListeners(); // Show toast if no routes found as specified in requirements if (_allRoutes.isEmpty) { _showNoRoutesToast(); } } catch (e) { print('Exception loading routes: $e'); _allRoutes = []; _isLoadingRoutes = false; notifyListeners(); // Show error toast for exceptions Fluttertoast.showToast( msg: "Error loading routes: ${e.toString()}", toastLength: Toast.LENGTH_LONG, gravity: ToastGravity.CENTER, backgroundColor: Colors.red, textColor: Colors.white, fontSize: 16.0, ); } } void _showNoRoutesToast() { Fluttertoast.showToast( msg: "No routes found", toastLength: Toast.LENGTH_LONG, gravity: ToastGravity.CENTER, backgroundColor: Colors.orange, textColor: Colors.white, fontSize: 16.0, ); } /// Select a route and update global state Future selectRoute(String routeId) async { if (routeId == _selectedRouteId) return; try { // Find the route in our cached routes final route = _allRoutes.firstWhere( (r) => r.id == routeId, orElse: () => throw Exception('Route not found: $routeId'), ); _selectedRouteId = routeId; _selectedRouteName = route.displayName; _error = null; debugPrint('✅ Route selected: $routeId - $_selectedRouteName'); notifyListeners(); } catch (e) { _error = 'Error selecting route: $e'; debugPrint('❌ Error selecting route: $e'); notifyListeners(); } } /// Clear selected route void clearSelectedRoute() { _selectedRouteId = null; _selectedRouteName = null; notifyListeners(); } /// Get selected route model RouteModel? getSelectedRoute() { if (_selectedRouteId == null) return null; try { return _allRoutes.firstWhere((r) => r.id == _selectedRouteId!); } catch (e) { return null; } } /// Private method to select first available route with stops Future _selectFirstAvailableRoute() async { for (final route in _allRoutes) { try { final stops = await _transportationService.getRouteStopsOrderedBySeq(route.id); if (stops.isNotEmpty) { _selectedRouteId = route.id; _selectedRouteName = route.displayName; debugPrint( '✅ Auto-selected route: ${route.id} - ${route.displayName}'); return; } } catch (e) { debugPrint('⚠️ Error checking stops for route ${route.id}: $e'); } } // Fallback: select first route even if no stops if (_allRoutes.isNotEmpty) { final firstRoute = _allRoutes.first; _selectedRouteId = firstRoute.id; _selectedRouteName = firstRoute.displayName; debugPrint( '✅ Fallback: selected first route: ${firstRoute.id} - ${firstRoute.displayName}'); } } /// Refresh routes and maintain selection if possible Future refreshRoutes() async { final previousSelectedId = _selectedRouteId; await loadRoutes(); // Try to restore previous selection if (previousSelectedId != null && _allRoutes.any((r) => r.id == previousSelectedId)) { await selectRoute(previousSelectedId); } } /// Get route by ID RouteModel? getRouteById(String routeId) { try { return _allRoutes.firstWhere((r) => r.id == routeId); } catch (e) { return null; } } /// Check if route exists bool hasRoute(String routeId) { return _allRoutes.any((r) => r.id == routeId); } /// Show toast message for no routes found void showNoRoutesToast(BuildContext context) { if (_allRoutes.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('No routes found'), backgroundColor: Colors.orange, duration: Duration(seconds: 3), ), ); } } }