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 '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)); 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}'), ), actions: [ IconButton( icon: const Icon(Icons.edit), tooltip: 'Edit Nickname', onPressed: () => _showEditNicknameDialog(context, ref, bike), ), IconButton( icon: const Icon(Icons.delete_outline, color: Colors.redAccent), tooltip: 'Delete Motorcycle', onPressed: () => _confirmDelete(context, ref, bike), ), ], ), body: SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildHeaderCard(context, ref, bike, spec), const SizedBox(height: 24), _buildMyPerformanceSection(context, statsAsync, spec), const SizedBox(height: 24), _buildSpecificationSheet(context, spec), ], ), ), ); }, ); } Widget _buildHeaderCard(BuildContext context, WidgetRef ref, GarageMotorcycle bike, MotorcycleSpecification? spec) { return Card( color: const Color(0xFF161616), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: BorderSide(color: bike.isDefault ? Colors.cyanAccent : Colors.white12, width: 2), ), child: Padding( padding: const EdgeInsets.all(20.0), child: Column( children: [ Row( children: [ const Icon(Icons.two_wheeler, size: 54, color: Colors.cyanAccent), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '${bike.year}', style: const TextStyle(fontSize: 16, color: Colors.cyanAccent, fontWeight: FontWeight.bold), ), Text( '${bike.makeName} ${bike.modelName}'.toUpperCase(), style: const TextStyle(fontSize: 22, fontWeight: FontWeight.w900, letterSpacing: 0.5), ), if (bike.nickname != null && bike.nickname!.isNotEmpty) Padding( padding: const EdgeInsets.only(top: 4.0), child: Text( '"${bike.nickname}"', style: const TextStyle(fontSize: 16, fontStyle: FontStyle.italic, color: Colors.amberAccent), ), ), ], ), ), ], ), const SizedBox(height: 16), const Divider(color: Colors.white10), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildHeaderStat('DISPLACEMENT', spec?.displacementCc != null ? '${spec!.displacementCc!.toStringAsFixed(0)} cc' : '—'), _buildHeaderStat('POWER', spec?.horsepower != null ? '${spec!.horsepower!.toStringAsFixed(0)} HP' : '—'), _buildHeaderStat('TORQUE', spec?.torqueLbFt != null ? '${spec!.torqueLbFt!.toStringAsFixed(1)} lb-ft' : '—'), ], ), const SizedBox(height: 16), SizedBox( width: double.infinity, child: bike.isDefault ? Container( padding: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: Colors.cyanAccent.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.cyanAccent), ), child: const Center( child: Text( 'CURRENT MOTORCYCLE', style: TextStyle(fontWeight: FontWeight.bold, letterSpacing: 1.5, color: Colors.cyanAccent), ), ), ) : OutlinedButton( style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 12), side: const BorderSide(color: Colors.cyanAccent), foregroundColor: Colors.cyanAccent, ), 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(fontWeight: FontWeight.bold, letterSpacing: 1.2)), ), ), ], ), ), ); } Widget _buildHeaderStat(String label, String value) { return Column( children: [ Text(label, style: const TextStyle(fontSize: 10, color: Colors.white54, letterSpacing: 1.2)), const SizedBox(height: 4), Text(value, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white)), ], ); } Widget _buildMyPerformanceSection(BuildContext context, AsyncValue statsAsync, MotorcycleSpecification? spec) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'MY PERFORMANCE', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.cyanAccent, letterSpacing: 1.2), ), 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}')), const SizedBox(width: 12), Expanded(child: _buildPerfTile('TOTAL DISTANCE', '${stats.totalDistanceMiles.toStringAsFixed(1)} MI')), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: _buildPerfTile( 'BEST TOP SPEED', '${stats.topSpeedMph.toStringAsFixed(1)} MPH', 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' : '—', 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)}°')), const SizedBox(width: 12), Expanded(child: _buildPerfTile('MAX ACCEL G', '${stats.maxAccelG.toStringAsFixed(2)} G')), ], ), ], ); }, ), ], ); } Widget _buildPerfTile(String label, String value, {String? published}) { return Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.05), borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.white10), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: const TextStyle(fontSize: 10, color: Colors.white54, letterSpacing: 1.0)), const SizedBox(height: 4), Text(value, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white)), if (published != null) ...[ const SizedBox(height: 4), Text('PUBLISHED: $published', style: const TextStyle(fontSize: 10, color: Colors.cyanAccent)), ], ], ), ); } Widget _buildSpecificationSheet(BuildContext context, MotorcycleSpecification? spec) { if (spec == null) { return const Card( child: 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: [ const Text( 'SPECIFICATIONS', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.cyanAccent, letterSpacing: 1.2), ), 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), ]), _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), ]), _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), ]), _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), ]), _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), ]), _buildSpecGroup('TRANSMISSION', [ _SpecRow('Transmission Type', spec.transmissionType), _SpecRow('Gears', spec.gearCount?.toString()), _SpecRow('Final Drive', spec.finalDrive), ]), if (spec.source != null) ...[ const SizedBox(height: 12), Text( 'Specifications Source: ${spec.source}', style: const TextStyle(fontSize: 12, color: Colors.white38), ), ], ], ); } Widget _buildSpecGroup(String title, List<_SpecRow> rows) { final validRows = rows.where((r) => r.value != null && r.value!.isNotEmpty).toList(); if (validRows.isEmpty) return const SizedBox(); return Card( margin: const EdgeInsets.only(bottom: 12), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.cyanAccent, fontSize: 14, letterSpacing: 1.0)), const SizedBox(height: 8), ...validRows.map( (r) => Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(r.label, style: const TextStyle(color: Colors.white70)), Flexible( child: Text( r.value!, textAlign: TextAlign.right, style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.white), ), ), ], ), ), ), ], ), ), ); } 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) { 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.redAccent), 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); }