import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../database/database.dart'; import '../../models/motorcycle_models.dart'; import '../../core/responsive/responsive_layout.dart'; import '../../core/theme/theme_extensions.dart'; import 'garage_providers.dart'; import 'widgets/motorcycle_photo.dart'; import '../../services/entitlements/entitlement_providers.dart'; import '../../services/entitlements/feature_access_service.dart'; import '../../services/sync/sync_providers.dart'; import '../../services/auth/auth_providers.dart'; import '../entitlements/widgets/pro_lock_badge.dart'; class GarageScreen extends ConsumerWidget { const GarageScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final bikesAsync = ref.watch(garageMotorcyclesStreamProvider); final colors = context.motorColors; final isSyncing = ref.watch(isSyncProcessingProvider); final pendingCount = ref.watch(pendingSyncCountProvider); return Scaffold( appBar: AppBar( title: const Text( 'My Garage', style: TextStyle(fontWeight: FontWeight.bold), ), actions: [ if (isSyncing) const Padding( padding: EdgeInsets.symmetric(horizontal: 14), child: SizedBox( width: 20, height: 20, child: CircularProgressIndicator( key: ValueKey('garage_sync_spinner'), strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Color(0xFF00E5FF)), ), ), ) else IconButton( key: const ValueKey('garage_sync_button'), tooltip: pendingCount > 0 ? 'Sync Garage ($pendingCount pending)' : 'Sync Garage with Cloud', icon: Badge( isLabelVisible: pendingCount > 0, label: Text('$pendingCount'), child: const Icon(Icons.cloud_sync_outlined), ), onPressed: () async { final auth = ref.read(authNotifierProvider); if (!auth.isAuthenticated) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( key: ValueKey('garage_sync_auth_required_snackbar'), content: Text('Sign in to sync your garage with MotorCloud.'), ), ); return; } ScaffoldMessenger.of(context).showSnackBar( const SnackBar( key: ValueKey('garage_sync_started_snackbar'), content: Text('Syncing garage and telemetry with cloud...'), ), ); try { await ref.read(firebaseAccountSyncServiceProvider).syncAccount(); await ref.read(syncManagerProvider).processQueue(); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( key: ValueKey('garage_sync_success_snackbar'), content: Text('Garage and rides synced successfully.'), backgroundColor: Color(0xFF10B981), ), ); } } catch (e) { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( key: const ValueKey('garage_sync_error_snackbar'), content: Text('Sync failed: $e. Changes are queued offline.'), backgroundColor: const Color(0xFFEF4444), ), ); } } }, ), ], ), body: bikesAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (err, stack) => Center(child: Text('Error loading garage: $err')), data: (bikes) { if (bikes.isEmpty) return _buildEmptyState(context, colors, ref); return ResponsiveLayout( builder: (context, responsive) { final isMultiColumn = responsive.isExpanded || (responsive.isMedium && responsive.isLandscape); if (isMultiColumn) { return GridView.builder( padding: const EdgeInsets.all(16.0), gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 550, mainAxisExtent: 420, crossAxisSpacing: 16, mainAxisSpacing: 16, ), itemCount: bikes.length, itemBuilder: (context, index) { return _buildMotorcycleCard(context, ref, bikes[index], colors, isGrid: true); }, ); } return ListView.builder( padding: const EdgeInsets.all(16.0), itemCount: bikes.length, itemBuilder: (context, index) { return _buildMotorcycleCard(context, ref, bikes[index], colors, isGrid: false); }, ); }, ); }, ), floatingActionButton: bikesAsync.value != null && bikesAsync.value!.isNotEmpty ? FloatingActionButton.extended( key: const ValueKey('garage_add_motorcycle_fab'), backgroundColor: colors.cyberCyan, foregroundColor: Colors.black, icon: const Icon(Icons.two_wheeler), label: const Text( 'Add Motorcycle', style: TextStyle(fontWeight: FontWeight.w600, letterSpacing: 0.2), ), onPressed: () => _onAddMotorcycle(context, ref, bikesAsync.value), ) : null, ); } void _onAddMotorcycle( BuildContext context, WidgetRef ref, List? bikes, ) { if (bikes != null && bikes.isNotEmpty) { final canAddMore = ref .read(featureAccessProvider) .canAccess(MotorSignalFeature.useMultipleMotorcycles); if (!canAddMore) { showProUpgradeSheet( context, ref, feature: MotorSignalFeature.useMultipleMotorcycles, ); return; } } context.push('/garage/add'); } Widget _buildEmptyState( BuildContext context, MotorSignalThemeColors colors, WidgetRef ref, ) { final theme = Theme.of(context); return Center( child: Padding( padding: const EdgeInsets.all(32.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.two_wheeler, size: 80, color: theme.colorScheme.onSurface.withValues(alpha: 0.25), ), const SizedBox(height: 24), Text( 'Your Garage is Empty', style: TextStyle( fontFamily: 'Sora', fontSize: 18, fontWeight: FontWeight.w700, letterSpacing: 0.2, color: theme.colorScheme.onSurface, ), ), const SizedBox(height: 12), Text( 'Add your motorcycle to personalize your ride history and performance data.', textAlign: TextAlign.center, style: TextStyle( color: theme.colorScheme.onSurface.withValues(alpha: 0.7), height: 1.4, ), ), const SizedBox(height: 32), ElevatedButton.icon( key: const ValueKey('garage_add_motorcycle_empty_button'), style: ElevatedButton.styleFrom( backgroundColor: colors.cyberCyan, foregroundColor: Colors.black, padding: const EdgeInsets.symmetric( horizontal: 24, vertical: 14, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), icon: const Icon(Icons.add), label: const Text( 'Add Motorcycle', style: TextStyle( fontWeight: FontWeight.w600, letterSpacing: 0.3, ), ), onPressed: () => context.push('/garage/add'), ), const SizedBox(height: 12), OutlinedButton.icon( key: const ValueKey('garage_sync_cloud_button'), style: OutlinedButton.styleFrom( foregroundColor: colors.cyberCyan, side: BorderSide(color: colors.cyberCyan.withValues(alpha: 0.6)), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), icon: const Icon(Icons.cloud_sync_outlined, size: 18), label: const Text('Restore from Cloud / Sync'), onPressed: () async { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Checking cloud for motorcycles...')), ); await ref.read(firebaseAccountSyncServiceProvider).syncAccount(); }, ), ], ), ), ); } Widget _buildMotorcycleCard( BuildContext context, WidgetRef ref, GarageMotorcycle bike, MotorSignalThemeColors colors, { bool isGrid = false, }) { MotorcycleSpecification? spec; if (bike.specificationJson != null) { try { spec = MotorcycleSpecification.fromJson( json.decode(bike.specificationJson!), ); } catch (_) {} } final displayName = bike.nickname != null && bike.nickname!.isNotEmpty ? '${bike.nickname} (${bike.year} ${bike.makeName} ${bike.modelName})' : '${bike.year} ${bike.makeName} ${bike.modelName}'; final theme = Theme.of(context); return Container( margin: EdgeInsets.only(bottom: isGrid ? 0 : 16), decoration: BoxDecoration( color: colors.surfaceContainer, borderRadius: BorderRadius.circular(14), border: Border.all( color: bike.isDefault ? colors.cyberCyan.withValues(alpha: 0.6) : colors.outlineVariant, width: bike.isDefault ? 1.0 : 0.8, ), ), child: Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(14), onTap: () => context.push('/garage/${bike.id}'), child: Padding( padding: const EdgeInsets.all(12.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MotorcyclePhoto( year: bike.year, make: bike.makeName, model: bike.modelName, height: 150, borderRadius: BorderRadius.circular(10), ), const SizedBox(height: 12), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( displayName, style: TextStyle( fontFamily: 'Sora', fontSize: 16, fontWeight: FontWeight.w700, color: theme.colorScheme.onSurface, ), ), ), if (bike.isDefault) Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 3, ), decoration: BoxDecoration( color: colors.cyberCyan.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(6), border: Border.all( color: colors.cyberCyan.withValues(alpha: 0.4), width: 0.8, ), ), child: Text( 'Current', style: TextStyle( fontFamily: 'Inter', fontSize: 10, fontWeight: FontWeight.w600, color: colors.cyberCyan, letterSpacing: 0.3, ), ), ), ], ), const SizedBox(height: 4), Text( spec != null ? '${spec.displacementCc?.toStringAsFixed(0) ?? "—"} cc • ${spec.horsepower?.toStringAsFixed(0) ?? "—"} hp' : '${bike.year} • ${bike.makeName}', style: TextStyle( fontFamily: 'JetBrains Mono', color: colors.cyberCyan, fontSize: 12.5, fontWeight: FontWeight.w600, ), ), ], ), ), ], ), const SizedBox(height: 12), Wrap( alignment: WrapAlignment.spaceBetween, crossAxisAlignment: WrapCrossAlignment.center, spacing: 8, runSpacing: 4, children: [ TextButton.icon( icon: Icon(Icons.directions_bike, size: 18, color: colors.cyberCyan), label: Text( 'View Specs & Stats', style: TextStyle(color: colors.cyberCyan, fontWeight: FontWeight.bold), ), onPressed: () => context.push('/garage/${bike.id}'), ), if (!bike.isDefault) OutlinedButton( style: OutlinedButton.styleFrom( foregroundColor: theme.colorScheme.onSurface, side: BorderSide(color: colors.outlineVariant), ), onPressed: () { ref .read(garageControllerProvider) .setDefaultMotorcycle(bike.id); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Set ${bike.makeName} ${bike.modelName} as current motorcycle.', ), ), ); }, child: const Text('Set as Current'), ), ], ), ], ), ), ), ), ); } }