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 'widgets/motorcycle_photo.dart'; import 'garage_providers.dart'; class MotorcycleDetailScreen extends ConsumerWidget { final int motorcycleId; const MotorcycleDetailScreen({super.key, required this.motorcycleId}); @override Widget build(BuildContext context, WidgetRef ref) { final bikesAsync = ref.watch(garageMotorcyclesStreamProvider); final statsAsync = ref.watch(motorcyclePerformanceStatsProvider(motorcycleId)); final colors = context.motorColors; final theme = Theme.of(context); return bikesAsync.when( loading: () => const Scaffold(body: Center(child: CircularProgressIndicator())), error: (err, _) => Scaffold(body: Center(child: Text('Error: $err'))), data: (bikes) { final bike = bikes.firstWhere( (b) => b.id == motorcycleId, orElse: () => GarageMotorcycle( id: 0, year: 2024, makeId: '', makeName: 'Unknown', modelId: '', modelName: 'Unknown', isDefault: false, dateAdded: DateTime.now(), lastUpdated: DateTime.now(), ), ); if (bike.id == 0) { return Scaffold( appBar: AppBar(), body: const Center(child: Text('Motorcycle not found')), ); } MotorcycleSpecification? spec; if (bike.specificationJson != null) { try { spec = MotorcycleSpecification.fromJson(json.decode(bike.specificationJson!)); } catch (_) {} } return Scaffold( appBar: AppBar( title: FittedBox( fit: BoxFit.scaleDown, alignment: Alignment.centerLeft, child: Text( '${bike.year} ${bike.makeName} ${bike.modelName}', style: const TextStyle(fontWeight: FontWeight.bold), ), ), actions: [ IconButton( icon: const Icon(Icons.edit), tooltip: 'Edit Nickname', onPressed: () => _showEditNicknameDialog(context, ref, bike), ), IconButton( icon: Icon(Icons.delete_outline, color: colors.raceRed), tooltip: 'Delete Motorcycle', onPressed: () => _confirmDelete(context, ref, bike, colors), ), ], ), body: ResponsiveLayout( builder: (context, responsive) { final isWide = responsive.isExpanded || (responsive.isMedium && responsive.isLandscape); if (isWide) { return SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 4, child: _buildHeaderCard(context, ref, bike, spec, colors, theme), ), const SizedBox(width: 16), Expanded( flex: 6, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildMyPerformanceSection(context, statsAsync, spec, colors, theme), const SizedBox(height: 20), _buildSpecificationSheet(context, spec, colors, theme), ], ), ), ], ), ); } return SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildHeaderCard(context, ref, bike, spec, colors, theme), const SizedBox(height: 20), _buildMyPerformanceSection(context, statsAsync, spec, colors, theme), const SizedBox(height: 20), _buildSpecificationSheet(context, spec, colors, theme), const SizedBox(height: 24), ], ), ); }, ), ); }, ); } Widget _buildHeaderCard( BuildContext context, WidgetRef ref, GarageMotorcycle bike, MotorcycleSpecification? spec, MotorSignalThemeColors colors, ThemeData theme, ) { return Card( elevation: 2, color: colors.surfaceContainer, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: BorderSide( color: bike.isDefault ? colors.cyberCyan : colors.outlineVariant, width: bike.isDefault ? 1.8 : 1.0, ), ), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MotorcyclePhoto( year: bike.year, make: bike.makeName, model: bike.modelName, height: 190, borderRadius: BorderRadius.circular(12), showAttribution: true, fit: BoxFit.cover, ), const SizedBox(height: 16), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '${bike.year}', style: TextStyle( fontSize: 14, color: colors.cyberCyan, fontWeight: FontWeight.bold, fontFamily: 'JetBrains Mono', ), ), Text( '${bike.makeName} ${bike.modelName}'.toUpperCase(), style: TextStyle( fontSize: 20, fontWeight: FontWeight.w900, fontFamily: 'Sora', color: theme.colorScheme.onSurface, ), ), if (bike.nickname != null && bike.nickname!.isNotEmpty) Padding( padding: const EdgeInsets.only(top: 4.0), child: Text( '"${bike.nickname}"', style: TextStyle( fontSize: 15, fontStyle: FontStyle.italic, color: colors.warningAmber, fontWeight: FontWeight.w600, ), ), ), ], ), const SizedBox(height: 16), Divider(color: colors.outlineVariant, height: 1), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildHeaderStat( 'DISPLACEMENT', spec?.displacementCc != null ? '${spec!.displacementCc!.toStringAsFixed(0)} cc' : '—', theme, ), _buildHeaderStat( 'POWER', spec?.horsepower != null ? '${spec!.horsepower!.toStringAsFixed(0)} HP' : '—', theme, ), _buildHeaderStat( 'TORQUE', spec?.torqueLbFt != null ? '${spec!.torqueLbFt!.toStringAsFixed(1)} lb-ft' : '—', theme, ), ], ), const SizedBox(height: 16), SizedBox( width: double.infinity, child: bike.isDefault ? Container( padding: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: colors.cyberCyan.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(8), border: Border.all(color: colors.cyberCyan), ), child: Center( child: Text( 'CURRENT MOTORCYCLE', style: TextStyle( fontFamily: 'Sora', fontWeight: FontWeight.bold, letterSpacing: 1.2, color: colors.cyberCyan, ), ), ), ) : OutlinedButton( style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 12), side: BorderSide(color: colors.cyberCyan), foregroundColor: colors.cyberCyan, ), 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 MOTORCYCLE', style: TextStyle( fontFamily: 'Sora', fontWeight: FontWeight.bold, letterSpacing: 1.2, ), ), ), ), ], ), ), ); } Widget _buildHeaderStat(String label, String value, ThemeData theme) { return Expanded( child: Column( children: [ FittedBox( fit: BoxFit.scaleDown, child: Text( label, style: TextStyle( fontSize: 9.5, color: theme.colorScheme.onSurface.withValues(alpha: 0.5), letterSpacing: 1.0, fontWeight: FontWeight.w700, ), ), ), const SizedBox(height: 4), FittedBox( fit: BoxFit.scaleDown, child: Text( value, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, fontFamily: 'JetBrains Mono', color: theme.colorScheme.onSurface, ), ), ), ], ), ); } Widget _buildMyPerformanceSection( BuildContext context, AsyncValue statsAsync, MotorcycleSpecification? spec, MotorSignalThemeColors colors, ThemeData theme, ) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'MY PERFORMANCE', style: TextStyle( fontFamily: 'Sora', fontSize: 16, fontWeight: FontWeight.bold, color: colors.cyberCyan, letterSpacing: 0.8, ), ), const SizedBox(height: 12), statsAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (err, _) => Text('Error loading stats: $err'), data: (stats) { return Column( children: [ Row( children: [ Expanded(child: _buildPerfTile('TOTAL RIDES', '${stats.rideCount}', colors, theme)), const SizedBox(width: 12), Expanded( child: _buildPerfTile( 'TOTAL DISTANCE', '${stats.totalDistanceMiles.toStringAsFixed(1)} MI', colors, theme, ), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: _buildPerfTile( 'BEST TOP SPEED', '${stats.topSpeedMph.toStringAsFixed(1)} MPH', colors, theme, published: spec?.claimedTopSpeedMph != null ? '~${spec!.claimedTopSpeedMph!.toStringAsFixed(0)} MPH' : null, ), ), const SizedBox(width: 12), Expanded( child: _buildPerfTile( 'BEST 0–60', stats.best0to60Ms != null ? '${(stats.best0to60Ms! / 1000.0).toStringAsFixed(2)} s' : '—', colors, theme, published: spec?.claimedZeroToSixtySec != null ? '~${spec!.claimedZeroToSixtySec!.toStringAsFixed(1)} s' : null, ), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: _buildPerfTile( 'MAX LEAN', '${stats.maxLean.toStringAsFixed(1)}°', colors, theme, ), ), const SizedBox(width: 12), Expanded( child: _buildPerfTile( 'MAX ACCEL G', '${stats.maxAccelG.toStringAsFixed(2)} G', colors, theme, ), ), ], ), ], ); }, ), ], ); } Widget _buildPerfTile( String label, String value, MotorSignalThemeColors colors, ThemeData theme, { String? published, }) { return Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: colors.surfaceContainer, borderRadius: BorderRadius.circular(12), border: Border.all(color: colors.outlineVariant, width: 1.2), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: TextStyle( fontSize: 10, color: theme.colorScheme.onSurface.withValues(alpha: 0.5), fontWeight: FontWeight.w700, letterSpacing: 0.8, ), ), const SizedBox(height: 4), FittedBox( fit: BoxFit.scaleDown, child: Text( value, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, fontFamily: 'JetBrains Mono', color: theme.colorScheme.onSurface, ), ), ), if (published != null) ...[ const SizedBox(height: 4), Text( 'PUBLISHED: $published', style: TextStyle( fontSize: 9.5, color: colors.cyberCyan, fontFamily: 'JetBrains Mono', ), ), ], ], ), ); } Widget _buildSpecificationSheet( BuildContext context, MotorcycleSpecification? spec, MotorSignalThemeColors colors, ThemeData theme, ) { if (spec == null) { return Card( color: colors.surfaceContainer, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide(color: colors.outlineVariant), ), child: const Padding( padding: EdgeInsets.all(16.0), child: Text( 'Detailed specifications unavailable for this motorcycle.', style: TextStyle(color: Colors.white54), ), ), ); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'SPECIFICATIONS', style: TextStyle( fontFamily: 'Sora', fontSize: 16, fontWeight: FontWeight.bold, color: colors.cyberCyan, letterSpacing: 0.8, ), ), const SizedBox(height: 12), _buildSpecGroup('ENGINE', [ _SpecRow('Displacement', spec.displacementCc != null ? '${spec.displacementCc!.toStringAsFixed(0)} cc' : null), _SpecRow('Configuration', spec.engineConfiguration), _SpecRow('Cylinders', spec.cylinderCount?.toString()), _SpecRow('Horsepower', spec.horsepower != null ? '${spec.horsepower!.toStringAsFixed(0)} HP @ ${spec.horsepowerRpm ?? "—"} RPM' : null), _SpecRow('Torque', spec.torqueLbFt != null ? '${spec.torqueLbFt!.toStringAsFixed(1)} lb-ft @ ${spec.torqueRpm ?? "—"} RPM' : null), _SpecRow('Redline', spec.redlineRpm != null ? '${spec.redlineRpm} RPM' : null), _SpecRow('Compression Ratio', spec.compressionRatio), _SpecRow('Cooling System', spec.coolingSystem), _SpecRow('Fuel System', spec.fuelSystem), ], colors, theme), _buildSpecGroup('PERFORMANCE (PUBLISHED)', [ _SpecRow('Claimed Top Speed', spec.claimedTopSpeedMph != null ? '~${spec.claimedTopSpeedMph!.toStringAsFixed(0)} MPH' : null), _SpecRow('Claimed 0–60 MPH', spec.claimedZeroToSixtySec != null ? '~${spec.claimedZeroToSixtySec!.toStringAsFixed(1)} sec' : null), _SpecRow('Claimed 1/4 Mile', spec.claimedQuarterMileSec != null ? '~${spec.claimedQuarterMileSec!.toStringAsFixed(1)} sec' : null), ], colors, theme), _buildSpecGroup('WEIGHT & DIMENSIONS', [ _SpecRow('Wet Weight', spec.wetWeightLbs != null ? '${spec.wetWeightLbs!.toStringAsFixed(0)} lbs' : null), _SpecRow('Dry Weight', spec.dryWeightLbs != null ? '${spec.dryWeightLbs!.toStringAsFixed(0)} lbs' : null), _SpecRow('Seat Height', spec.seatHeightInches != null ? '${spec.seatHeightInches!.toStringAsFixed(1)} in' : null), _SpecRow('Wheelbase', spec.wheelbaseInches != null ? '${spec.wheelbaseInches!.toStringAsFixed(1)} in' : null), ], colors, theme), _buildSpecGroup('FUEL', [ _SpecRow('Tank Capacity', spec.fuelCapacityGallons != null ? '${spec.fuelCapacityGallons!.toStringAsFixed(1)} gal' : null), _SpecRow('Fuel Economy', spec.fuelEconomyMpg != null ? '${spec.fuelEconomyMpg!.toStringAsFixed(0)} MPG' : null), _SpecRow('Estimated Range', spec.estimatedRangeMiles != null ? '~${spec.estimatedRangeMiles!.toStringAsFixed(0)} miles' : null), ], colors, theme), _buildSpecGroup('CHASSIS & BRAKES', [ _SpecRow('Frame Type', spec.frameType), _SpecRow('Front Suspension', spec.frontSuspension), _SpecRow('Rear Suspension', spec.rearSuspension), _SpecRow('Front Brake', spec.frontBrake), _SpecRow('Rear Brake', spec.rearBrake), _SpecRow('ABS System', spec.absType), _SpecRow('Front Tire', spec.frontTireSize), _SpecRow('Rear Tire', spec.rearTireSize), ], colors, theme), _buildSpecGroup('TRANSMISSION', [ _SpecRow('Transmission Type', spec.transmissionType), _SpecRow('Gears', spec.gearCount?.toString()), _SpecRow('Final Drive', spec.finalDrive), ], colors, theme), if (spec.source != null) ...[ const SizedBox(height: 12), Text( 'Specifications Source: ${spec.source}', style: TextStyle( fontSize: 11, color: theme.colorScheme.onSurface.withValues(alpha: 0.4), fontFamily: 'Inter', ), ), ], ], ); } Widget _buildSpecGroup( String title, List<_SpecRow> rows, MotorSignalThemeColors colors, ThemeData theme, ) { final validRows = rows.where((r) => r.value != null && r.value!.isNotEmpty).toList(); if (validRows.isEmpty) return const SizedBox.shrink(); return Card( elevation: 1, color: colors.surfaceContainer, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide(color: colors.outlineVariant), ), margin: const EdgeInsets.only(bottom: 12), child: Padding( padding: const EdgeInsets.all(14.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle( fontFamily: 'Sora', fontWeight: FontWeight.bold, color: colors.cyberCyan, fontSize: 12, letterSpacing: 0.8, ), ), const SizedBox(height: 8), ...validRows.map( (r) => Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( r.label, style: TextStyle( fontSize: 12, color: theme.colorScheme.onSurface.withValues(alpha: 0.6), ), ), const SizedBox(width: 8), Expanded( child: Text( r.value!, textAlign: TextAlign.right, style: TextStyle( fontWeight: FontWeight.bold, fontFamily: 'JetBrains Mono', fontSize: 12, color: theme.colorScheme.onSurface, ), ), ), ], ), ), ), ], ), ), ); } void _showEditNicknameDialog(BuildContext context, WidgetRef ref, GarageMotorcycle bike) { final controller = TextEditingController(text: bike.nickname ?? ''); showDialog( context: context, builder: (context) { return AlertDialog( title: const Text('Edit Nickname'), content: TextField( controller: controller, decoration: const InputDecoration(hintText: 'Enter nickname'), ), actions: [ TextButton(onPressed: () => Navigator.pop(context), child: const Text('Cancel')), ElevatedButton( onPressed: () { ref.read(garageControllerProvider).updateNickname( bike.id, controller.text.trim().isNotEmpty ? controller.text.trim() : null, ); Navigator.pop(context); }, child: const Text('Save'), ), ], ); }, ); } void _confirmDelete( BuildContext context, WidgetRef ref, GarageMotorcycle bike, MotorSignalThemeColors colors, ) { showDialog( context: context, builder: (context) { return AlertDialog( title: const Text('Delete Motorcycle?'), content: Text( 'Are you sure you want to remove ${bike.makeName} ${bike.modelName} from your Garage? Historical rides associated with this motorcycle will be preserved.', ), actions: [ TextButton(onPressed: () => Navigator.pop(context), child: const Text('Cancel')), ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: colors.raceRed), onPressed: () { ref.read(garageControllerProvider).deleteMotorcycle(bike.id); Navigator.pop(context); context.pop(); }, child: const Text('Delete', style: TextStyle(color: Colors.white)), ), ], ); }, ); } } class _SpecRow { final String label; final String? value; _SpecRow(this.label, this.value); }