import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../services/map/map_provider_interface.dart'; import '../../../services/map/map_providers.dart'; import '../../../services/alerts/road_alert_models.dart'; import '../../../services/alerts/road_alert_providers.dart'; import '../../../services/alerts/road_alert_service.dart'; import '../../../services/osm_oauth_service.dart'; import '../../../services/sensor_fusion_service.dart'; import '../../../services/field_test/field_test_logger.dart'; import '../../../database/database.dart'; import '../../rides/ride_state_machine.dart'; import '../../../core/theme/evolution_theme_config.dart'; import '../../../core/app_info.dart'; class EvolutionSettingsView extends ConsumerStatefulWidget { const EvolutionSettingsView({super.key}); @override ConsumerState createState() => _EvolutionSettingsViewState(); } class _EvolutionSettingsViewState extends ConsumerState { late TextEditingController _tomtomKeyController; late TextEditingController _communityKeyController; late TextEditingController _communityEndpointController; bool _isTestingTomTom = false; bool _isTestingCommunity = false; @override void initState() { super.initState(); _tomtomKeyController = TextEditingController( text: ref.read(tomtomApiKeyProvider), ); _communityKeyController = TextEditingController( text: ref.read(communityApiKeyProvider), ); _communityEndpointController = TextEditingController( text: ref.read(communityEndpointUrlProvider), ); } @override void dispose() { _tomtomKeyController.dispose(); _communityKeyController.dispose(); _communityEndpointController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final mapType = ref.watch(mapProviderTypeNotifierProvider); final themeMode = ref.watch(dashboardThemeModeProvider); final roadAlertsEnabled = ref.watch(roadAlertsEnabledProvider); final audioAlertsEnabled = ref.watch(audioAlertsEnabledProvider); final alertRadiusMeters = ref.watch(alertRadiusMetersProvider); final enabledCategories = ref.watch(enabledCategoriesProvider); final osmAuth = ref.watch(osmOAuthServiceProvider); final radiusMiles = (alertRadiusMeters / 1609.34).toStringAsFixed(1); final isLandscape = MediaQuery.of(context).orientation == Orientation.landscape; return Scaffold( backgroundColor: Colors.black, appBar: AppBar( toolbarHeight: isLandscape ? 36 : 52, backgroundColor: Colors.transparent, elevation: 0, centerTitle: true, title: Text( 'SETTINGS & SYSTEM CONFIG', style: TextStyle( fontFamily: 'Sora', fontSize: isLandscape ? 13 : 16, fontWeight: FontWeight.w900, color: Colors.white, letterSpacing: 0.12, ), ), ), body: LayoutBuilder( builder: (context, constraints) { final isLandscape = constraints.maxWidth > constraints.maxHeight; final themeToggleBar = Container( margin: EdgeInsets.only(bottom: isLandscape ? 6 : 12), padding: EdgeInsets.symmetric(horizontal: 12, vertical: isLandscape ? 6 : 12), decoration: BoxDecoration( color: const Color(0xFF0D0D0D), borderRadius: BorderRadius.circular(14), border: Border.all(color: const Color(0xFF1F1F1F), width: 1.2), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Row( children: [ Icon(Icons.palette_outlined, color: Color(0xFF00E5FF), size: 18), SizedBox(width: 8), Text( 'ACTIVE THEME:', style: TextStyle( fontFamily: 'Inter', fontSize: 11, fontWeight: FontWeight.w800, color: Colors.white70, ), ), ], ), SegmentedButton( style: const ButtonStyle( visualDensity: VisualDensity.compact, ), segments: const [ ButtonSegment( value: DashboardThemeMode.classic, label: Text('Classic', style: TextStyle(fontSize: 11)), ), ButtonSegment( value: DashboardThemeMode.evolution, label: Text('Evolution', style: TextStyle(fontSize: 11)), ), ], selected: {themeMode}, onSelectionChanged: (Set selection) { ref.read(dashboardThemeModeProvider.notifier).setThemeMode(selection.first); }, ), ], ), ); final osmAccountCard = _buildOsmAccountCard(context, ref, osmAuth); final mapEngineCard = _buildMapEngineCard(context, ref, mapType); final alertConfigCard = _buildAlertConfigCard( context, ref, roadAlertsEnabled, audioAlertsEnabled, enabledCategories, alertRadiusMeters, radiusMiles, ); final sensorsCard = _buildSensorsCalibrationCard(context, ref); final tomtomCard = _buildTomTomCard(context, ref); final communityCard = _buildCommunityCard(context, ref); final dataCard = _buildDataManagementCard(context, ref); final engineeringCard = _buildEngineeringDiagnosticsCard(context, ref); final versionFooter = _buildVersionFooter(); if (isLandscape) { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( children: [ themeToggleBar, Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( children: [ sensorsCard, const SizedBox(height: 16), mapEngineCard, const SizedBox(height: 16), osmAccountCard, const SizedBox(height: 16), tomtomCard, ], ), ), const SizedBox(width: 16), Expanded( child: Column( children: [ alertConfigCard, const SizedBox(height: 16), communityCard, const SizedBox(height: 16), engineeringCard, const SizedBox(height: 16), dataCard, ], ), ), ], ), const SizedBox(height: 24), versionFooter, ], ), ); } return SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ themeToggleBar, sensorsCard, const SizedBox(height: 16), engineeringCard, const SizedBox(height: 16), dataCard, const SizedBox(height: 16), osmAccountCard, const SizedBox(height: 16), mapEngineCard, const SizedBox(height: 16), alertConfigCard, const SizedBox(height: 16), communityCard, const SizedBox(height: 16), tomtomCard, const SizedBox(height: 24), versionFooter, const SizedBox(height: 24), ], ), ); }, ), ); } Widget _buildOsmAccountCard(BuildContext context, WidgetRef ref, OsmAuthState osmAuth) { final isAuthed = osmAuth.isAuthenticated; final user = osmAuth.user; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF0D0D0D), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFF1F1F1F), width: 1.2), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Row( children: [ Icon(Icons.public, color: Color(0xFF00E5FF), size: 18), SizedBox(width: 8), Text( 'OPENSTREETMAP ACCOUNT', style: TextStyle( fontFamily: 'Sora', fontSize: 12, fontWeight: FontWeight.w800, color: Color(0xFF00E5FF), letterSpacing: 0.1, ), ), ], ), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration( color: isAuthed ? const Color(0xFF00E5FF).withValues(alpha: 0.15) : Colors.white10, borderRadius: BorderRadius.circular(8), ), child: Text( isAuthed ? 'CONNECTED' : 'DISCONNECTED', style: TextStyle( fontFamily: 'Inter', fontSize: 9, fontWeight: FontWeight.w800, color: isAuthed ? const Color(0xFF00E5FF) : Colors.white38, ), ), ), ], ), const SizedBox(height: 12), if (isAuthed && user != null) ...[ Row( children: [ CircleAvatar( radius: 20, backgroundColor: Colors.white12, backgroundImage: user.avatarUrl != null ? NetworkImage(user.avatarUrl!) : null, child: user.avatarUrl == null ? const Icon(Icons.person, color: Color(0xFF00E5FF), size: 22) : null, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( user.displayName, style: const TextStyle( fontFamily: 'Inter', fontSize: 14, fontWeight: FontWeight.w700, color: Colors.white, ), ), Text( 'Changesets: ${user.changesetCount} • Sync Enabled', style: const TextStyle( fontFamily: 'Inter', fontSize: 11, color: Colors.white54, ), ), ], ), ), ], ), const SizedBox(height: 12), OutlinedButton.icon( style: OutlinedButton.styleFrom( foregroundColor: Colors.redAccent, side: const BorderSide(color: Colors.redAccent, width: 0.8), minimumSize: const Size(double.infinity, 38), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), icon: const Icon(Icons.link_off, size: 16), label: const Text('Disconnect OpenStreetMap', style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), onPressed: () => ref.read(osmOAuthServiceProvider.notifier).logout(), ), ] else ...[ const Text( 'Link your OpenStreetMap account via OAuth 2.0 PKCE to upload GPS traces and sync ride telemetry.', style: TextStyle(fontFamily: 'Inter', fontSize: 11, color: Colors.white60, height: 1.4), ), const SizedBox(height: 12), ElevatedButton.icon( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF00E5FF), foregroundColor: Colors.black, minimumSize: const Size(double.infinity, 40), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), icon: const Icon(Icons.login, size: 18), label: const Text( 'Connect OpenStreetMap Account', style: TextStyle(fontFamily: 'Inter', fontSize: 12, fontWeight: FontWeight.w800), ), onPressed: osmAuth.isLoading ? null : () => ref.read(osmOAuthServiceProvider.notifier).startLoginFlow(), ), ], if (osmAuth.errorMessage != null) ...[ const SizedBox(height: 8), Text( osmAuth.errorMessage!, style: const TextStyle(color: Colors.redAccent, fontSize: 10), ), ], ], ), ); } Widget _buildMapEngineCard(BuildContext context, WidgetRef ref, AppMapType mapType) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF0D0D0D), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFF1F1F1F), width: 1.2), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'MAP ENGINE & RENDERING', style: TextStyle( fontFamily: 'Sora', fontSize: 12, fontWeight: FontWeight.w800, color: Color(0xFF00E5FF), letterSpacing: 0.1, ), ), const SizedBox(height: 12), const Text( 'Active Map Engine', style: TextStyle(fontFamily: 'Inter', fontSize: 11, fontWeight: FontWeight.w700, color: Colors.white70), ), const SizedBox(height: 8), _buildEngineOption( title: 'OpenStreetMap', selected: mapType == AppMapType.openStreetMap, onTap: () => ref.read(mapProviderTypeNotifierProvider.notifier).setMapProviderType(AppMapType.openStreetMap), ), const SizedBox(height: 6), _buildEngineOption( title: 'Google Maps', selected: mapType == AppMapType.googleMaps, onTap: () => ref.read(mapProviderTypeNotifierProvider.notifier).setMapProviderType(AppMapType.googleMaps), ), const SizedBox(height: 16), const Text( 'Rendering Preferences', style: TextStyle(fontFamily: 'Inter', fontSize: 11, fontWeight: FontWeight.w700, color: Colors.white70), ), const SizedBox(height: 8), _buildSwitchRow(Icons.apartment, '3D Buildings', true, (_) {}), _buildSwitchRow(Icons.terrain, 'Terrain Topo Data', true, (_) {}), _buildSwitchRow(Icons.traffic, 'Live Traffic Overlay', true, (_) {}), _buildSwitchRow(Icons.play_circle_outline, 'Replay Preview', false, (_) {}), ], ), ); } Widget _buildAlertConfigCard( BuildContext context, WidgetRef ref, bool roadAlertsEnabled, bool audioAlertsEnabled, Set enabledCategories, double alertRadiusMeters, String radiusMiles, ) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF0D0D0D), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFF1F1F1F), width: 1.2), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'ROAD ALERT CONFIGURATION', style: TextStyle( fontFamily: 'Sora', fontSize: 12, fontWeight: FontWeight.w800, color: Color(0xFF00E5FF), letterSpacing: 0.1, ), ), const SizedBox(height: 12), const Text( 'Alert Categories', style: TextStyle(fontFamily: 'Inter', fontSize: 11, fontWeight: FontWeight.w700, color: Colors.white70), ), const SizedBox(height: 8), _buildSwitchRow( Icons.local_police_outlined, 'Police Presence', enabledCategories.contains(RoadAlertCategory.police), (v) => ref.read(enabledCategoriesProvider.notifier).toggleCategory(RoadAlertCategory.police, v), ), _buildSwitchRow( Icons.camera_alt_outlined, 'Speed Cameras', enabledCategories.contains(RoadAlertCategory.speedCamera), (v) => ref.read(enabledCategoriesProvider.notifier).toggleCategory(RoadAlertCategory.speedCamera, v), ), _buildSwitchRow( Icons.warning_amber_rounded, 'Road Hazards', enabledCategories.contains(RoadAlertCategory.hazard), (v) => ref.read(enabledCategoriesProvider.notifier).toggleCategory(RoadAlertCategory.hazard, v), ), _buildSwitchRow( Icons.car_crash_outlined, 'Traffic Incidents', enabledCategories.contains(RoadAlertCategory.traffic), (v) => ref.read(enabledCategoriesProvider.notifier).toggleCategory(RoadAlertCategory.traffic, v), ), const SizedBox(height: 16), const Text( 'Radius Configuration', style: TextStyle(fontFamily: 'Inter', fontSize: 11, fontWeight: FontWeight.w700, color: Colors.white70), ), const SizedBox(height: 8), Center( child: Text( '$radiusMiles MILE', style: const TextStyle( fontFamily: 'JetBrains Mono', fontSize: 22, fontWeight: FontWeight.w900, color: Colors.white, ), ), ), const SizedBox(height: 8), Row( children: [ IconButton( icon: const Icon(Icons.remove_circle_outline, color: Color(0xFF00E5FF)), onPressed: alertRadiusMeters > 500 ? () => ref.read(alertRadiusMetersProvider.notifier).setRadius(alertRadiusMeters - 500.0) : null, ), Expanded( child: LinearProgressIndicator( value: (alertRadiusMeters / 5000.0).clamp(0.1, 1.0), backgroundColor: const Color(0xFF1F1F1F), valueColor: const AlwaysStoppedAnimation(Color(0xFF00E5FF)), minHeight: 8, borderRadius: BorderRadius.circular(4), ), ), IconButton( icon: const Icon(Icons.add_circle_outline, color: Color(0xFF00E5FF)), onPressed: alertRadiusMeters < 5000 ? () => ref.read(alertRadiusMetersProvider.notifier).setRadius(alertRadiusMeters + 500.0) : null, ), ], ), const Center( child: Text( 'Distance to detect upcoming incidents', style: TextStyle(fontFamily: 'Inter', fontSize: 9, color: Colors.white38), ), ), const SizedBox(height: 12), SizedBox( width: double.infinity, child: OutlinedButton.icon( icon: const Icon(Icons.volume_up, size: 16), label: const Text('TRIGGER TEST ALERT (TTS + HUD)'), style: OutlinedButton.styleFrom( foregroundColor: const Color(0xFF00E5FF), side: const BorderSide(color: Color(0xFF00E5FF), width: 1), padding: const EdgeInsets.symmetric(vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), onPressed: () => ref.read(roadAlertServiceProvider).triggerTestAlert(), ), ), ], ), ); } Widget _buildEngineOption({required String title, required bool selected, required VoidCallback onTap}) { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(8), child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), decoration: BoxDecoration( color: selected ? const Color(0xFF00252A) : const Color(0xFF141414), borderRadius: BorderRadius.circular(8), border: Border.all( color: selected ? const Color(0xFF00E5FF) : const Color(0xFF222222), width: selected ? 1.4 : 1.0, ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( title, style: TextStyle( fontFamily: 'Inter', fontSize: 12, fontWeight: FontWeight.w700, color: selected ? Colors.white : Colors.white60, ), ), if (selected) const Icon(Icons.check, color: Color(0xFF00E5FF), size: 16), ], ), ), ); } Widget _buildSwitchRow(IconData icon, String title, bool value, ValueChanged onChanged) { return Padding( padding: const EdgeInsets.symmetric(vertical: 2), child: Row( children: [ Icon(icon, color: Colors.white60, size: 16), const SizedBox(width: 8), Expanded( child: Text( title, style: const TextStyle(fontFamily: 'Inter', fontSize: 11, fontWeight: FontWeight.w600, color: Colors.white), ), ), Switch( value: value, activeThumbColor: const Color(0xFF00E5FF), onChanged: onChanged, ), ], ), ); } Widget _buildSensorsCalibrationCard(BuildContext context, WidgetRef ref) { final leanAsync = ref.watch(leanAngleStreamProvider); final currentLean = leanAsync.value ?? 0.0; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF0D0D0D), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFF1F1F1F), width: 1.2), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon(Icons.screen_rotation, color: Color(0xFF00E5FF), size: 18), SizedBox(width: 8), Text( 'SENSORS & MOUNT CALIBRATION', style: TextStyle( fontFamily: 'Inter', fontSize: 12, fontWeight: FontWeight.w800, color: Colors.white, letterSpacing: 0.1, ), ), ], ), const SizedBox(height: 8), const Text( 'Place motorcycle upright on level ground. Zeroes the roll and pitch offsets for your phone mount angle.', style: TextStyle(fontFamily: 'Inter', fontSize: 10, color: Colors.white60, height: 1.3), ), const SizedBox(height: 12), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), decoration: BoxDecoration( color: const Color(0xFF141414), borderRadius: BorderRadius.circular(8), border: Border.all(color: const Color(0xFF222222)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'CURRENT LEAN ANGLE:', style: TextStyle(fontFamily: 'Inter', fontSize: 10, fontWeight: FontWeight.bold, color: Colors.white54), ), Text( '${currentLean.toStringAsFixed(1)}°', style: const TextStyle(fontFamily: 'Inter', fontSize: 14, fontWeight: FontWeight.w900, color: Color(0xFF00E5FF)), ), ], ), ), const SizedBox(height: 12), SizedBox( width: double.infinity, child: ElevatedButton.icon( icon: const Icon(Icons.tune, size: 16), label: const Text('CALIBRATE UPRIGHT ZERO'), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF00E5FF), foregroundColor: Colors.black, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), textStyle: const TextStyle(fontFamily: 'Inter', fontSize: 11, fontWeight: FontWeight.w900, letterSpacing: 0.8), ), onPressed: () { final session = ref.read(rideStateMachineProvider); if (session.state == RideState.riding) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Cannot calibrate while a ride is actively recording.')), ); return; } ref.read(sensorFusionServiceProvider).calibrateUpright(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Sensors Calibrated! Zero mount angle saved.')), ); }, ), ), ], ), ); } Widget _buildDataManagementCard(BuildContext context, WidgetRef ref) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF0D0D0D), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFF1F1F1F), width: 1.2), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon(Icons.storage_outlined, color: Color(0xFF00E5FF), size: 18), SizedBox(width: 8), Text( 'DATA & STORAGE', style: TextStyle( fontFamily: 'Inter', fontSize: 12, fontWeight: FontWeight.w800, color: Colors.white, letterSpacing: 0.1, ), ), ], ), const SizedBox(height: 12), InkWell( onTap: () => context.go('/settings/diagnostics'), borderRadius: BorderRadius.circular(8), child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), decoration: BoxDecoration( color: const Color(0xFF141414), borderRadius: BorderRadius.circular(8), border: Border.all(color: const Color(0xFF222222)), ), child: const Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Icon(Icons.monitor_heart, color: Color(0xFF00E5FF), size: 16), SizedBox(width: 8), Text( 'Sensor Diagnostics & Raw Telemetry', style: TextStyle(fontFamily: 'Inter', fontSize: 11, fontWeight: FontWeight.w700, color: Colors.white), ), ], ), Icon(Icons.chevron_right, color: Colors.white54, size: 18), ], ), ), ), const SizedBox(height: 10), SizedBox( width: double.infinity, child: OutlinedButton.icon( icon: const Icon(Icons.delete_forever, color: Colors.redAccent, size: 16), label: const Text('CLEAR ALL RIDE HISTORY'), style: OutlinedButton.styleFrom( foregroundColor: Colors.redAccent, side: const BorderSide(color: Colors.redAccent, width: 1), padding: const EdgeInsets.symmetric(vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), textStyle: const TextStyle(fontFamily: 'Inter', fontSize: 11, fontWeight: FontWeight.w800, letterSpacing: 0.5), ), onPressed: () async { final session = ref.read(rideStateMachineProvider); if (session.state == RideState.riding) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Cannot clear history while a ride is actively recording.')), ); return; } final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( backgroundColor: const Color(0xFF141414), title: const Text('Clear All Ride History?'), content: const Text('This will permanently delete all saved rides, telemetry points, and events. This action cannot be undone.'), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), child: const Text('CANCEL'), ), TextButton( onPressed: () => Navigator.pop(ctx, true), child: const Text('CLEAR ALL', style: TextStyle(color: Colors.redAccent, fontWeight: FontWeight.bold)), ), ], ), ); if (confirmed == true && context.mounted) { final db = ref.read(databaseProvider); await db.transaction(() async { await db.delete(db.telemetrySamples).go(); await db.delete(db.rideEvents).go(); await db.delete(db.performanceRuns).go(); await db.delete(db.rides).go(); }); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('All ride history cleared.')), ); } } }, ), ), ], ), ); } Widget _buildEngineeringDiagnosticsCard(BuildContext context, WidgetRef ref) { final isEnabled = ref.watch(fieldTestModeEnabledProvider); final activeSession = ref.watch(activeFieldTestSessionProvider); return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF0D0D0D), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFF1F1F1F), width: 1.2), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Icon(Icons.biotech, color: isEnabled ? Colors.greenAccent : const Color(0xFF00E5FF), size: 18), const SizedBox(width: 8), const Text( 'ENGINEERING & DIAGNOSTICS', style: TextStyle( fontFamily: 'Inter', fontSize: 12, fontWeight: FontWeight.w800, color: Colors.white, letterSpacing: 0.1, ), ), ], ), Switch( value: isEnabled, activeThumbColor: Colors.greenAccent, onChanged: (val) { ref.read(fieldTestModeEnabledProvider.notifier).setEnabled(val); }, ), ], ), const SizedBox(height: 6), const Text( 'High-resolution black-box recorder for sensor fusion, GPS, and Kalman filter diagnostics.', style: TextStyle(fontFamily: 'Inter', fontSize: 10, color: Colors.white60, height: 1.3), ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('RECORDER STATUS:', style: TextStyle(fontFamily: 'Inter', color: Colors.white54, fontSize: 10, fontWeight: FontWeight.bold)), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: activeSession != null ? Colors.greenAccent.withValues(alpha: 0.15) : (isEnabled ? const Color(0xFF00E5FF).withValues(alpha: 0.15) : Colors.white10), borderRadius: BorderRadius.circular(6), border: Border.all( color: activeSession != null ? Colors.greenAccent : (isEnabled ? const Color(0xFF00E5FF) : Colors.white24), ), ), child: Text( activeSession != null ? 'RECORDING (Ride #$activeSession)' : (isEnabled ? 'ARMED (Auto-starts on Ride)' : 'OFF'), style: TextStyle( fontFamily: 'Inter', fontSize: 10, fontWeight: FontWeight.bold, color: activeSession != null ? Colors.greenAccent : (isEnabled ? const Color(0xFF00E5FF) : Colors.white54), ), ), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: ElevatedButton.icon( style: ElevatedButton.styleFrom( backgroundColor: Colors.greenAccent.withValues(alpha: 0.2), foregroundColor: Colors.greenAccent, side: const BorderSide(color: Colors.greenAccent), padding: const EdgeInsets.symmetric(vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), icon: const Icon(Icons.fact_check, size: 16), label: const Text('PRE-RIDE CHECK', style: TextStyle(fontFamily: 'Inter', fontSize: 9, fontWeight: FontWeight.w800)), onPressed: () => context.go('/settings/field-test-pre-ride-check'), ), ), const SizedBox(width: 6), Expanded( child: ElevatedButton.icon( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF00E5FF).withValues(alpha: 0.15), foregroundColor: const Color(0xFF00E5FF), side: const BorderSide(color: Color(0xFF00E5FF)), padding: const EdgeInsets.symmetric(vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), icon: const Icon(Icons.assessment, size: 16), label: const Text('REPORTS', style: TextStyle(fontFamily: 'Inter', fontSize: 9, fontWeight: FontWeight.w800)), onPressed: () => context.go('/settings/field-test-report'), ), ), const SizedBox(width: 6), Expanded( child: ElevatedButton.icon( style: ElevatedButton.styleFrom( backgroundColor: Colors.white10, foregroundColor: Colors.white, side: const BorderSide(color: Colors.white24), padding: const EdgeInsets.symmetric(vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), icon: const Icon(Icons.stream, size: 16), label: const Text('LIVE LOG', style: TextStyle(fontFamily: 'Inter', fontSize: 9, fontWeight: FontWeight.w800)), onPressed: () => context.go('/settings/field-test-live-log'), ), ), ], ), ], ), ); } Widget _buildTomTomCard(BuildContext context, WidgetRef ref) { final tomtomStatus = ref.watch(tomtomStatusProvider); return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF0D0D0D), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFF1F1F1F), width: 1.2), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Row( children: [ Icon(Icons.traffic, color: Color(0xFF00E5FF), size: 18), SizedBox(width: 8), Text( 'TOMTOM TRAFFIC PROVIDER', style: TextStyle( fontFamily: 'Inter', fontSize: 12, fontWeight: FontWeight.w800, color: Colors.white, letterSpacing: 0.1, ), ), ], ), _buildStatusBadge(tomtomStatus), ], ), const SizedBox(height: 8), const Text( 'Supplies real-time accidents, hazards, road closures, and construction feeds.', style: TextStyle(fontFamily: 'Inter', fontSize: 10, color: Colors.white60, height: 1.3), ), const SizedBox(height: 12), TextField( controller: _tomtomKeyController, decoration: InputDecoration( labelText: 'TomTom API Key', labelStyle: const TextStyle(fontFamily: 'Inter', fontSize: 11, color: Colors.white60), hintText: 'Enter your TomTom API Key', hintStyle: const TextStyle(fontFamily: 'Inter', fontSize: 11, color: Colors.white30), filled: true, fillColor: const Color(0xFF141414), border: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: Color(0xFF222222))), enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: Color(0xFF222222))), focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: Color(0xFF00E5FF))), isDense: true, ), style: const TextStyle(fontFamily: 'Inter', fontSize: 12, color: Colors.white), onChanged: (val) { ref.read(tomtomApiKeyProvider.notifier).setApiKey(val); }, ), const SizedBox(height: 12), Align( alignment: Alignment.centerRight, child: ElevatedButton.icon( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF00E5FF).withValues(alpha: 0.15), foregroundColor: const Color(0xFF00E5FF), side: const BorderSide(color: Color(0xFF00E5FF)), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), onPressed: _isTestingTomTom ? null : () async { setState(() => _isTestingTomTom = true); final messenger = ScaffoldMessenger.of(context); final provider = ref.read(tomtomProviderInstance); final status = await provider.testConnection(_tomtomKeyController.text); ref.read(tomtomStatusProvider.notifier).state = status; setState(() => _isTestingTomTom = false); String msg = status == RoadAlertProviderStatus.connected ? 'TomTom API Connected successfully!' : 'Status: ${status.name}'; messenger.showSnackBar(SnackBar(content: Text(msg))); }, icon: _isTestingTomTom ? const SizedBox(width: 14, height: 14, child: CircularProgressIndicator(strokeWidth: 2, color: Color(0xFF00E5FF))) : const Icon(Icons.sync, size: 16), label: const Text('TEST CONNECTION', style: TextStyle(fontFamily: 'Inter', fontSize: 10, fontWeight: FontWeight.w800)), ), ), ], ), ); } Widget _buildCommunityCard(BuildContext context, WidgetRef ref) { final communityStatus = ref.watch(communityStatusProvider); return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF0D0D0D), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFF1F1F1F), width: 1.2), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Row( children: [ Icon(Icons.local_police, color: Color(0xFF00E5FF), size: 18), SizedBox(width: 8), Text( 'COMMUNITY TRAFFIC PROVIDER', style: TextStyle( fontFamily: 'Inter', fontSize: 12, fontWeight: FontWeight.w800, color: Colors.white, letterSpacing: 0.1, ), ), ], ), _buildStatusBadge(communityStatus), ], ), const SizedBox(height: 8), const Text( 'Live crowdsourced speed traps, police radars, and hazard reports.', style: TextStyle(fontFamily: 'Inter', fontSize: 10, color: Colors.white60, height: 1.3), ), const SizedBox(height: 12), TextField( controller: _communityKeyController, decoration: InputDecoration( labelText: 'Community API Key', labelStyle: const TextStyle(fontFamily: 'Inter', fontSize: 11, color: Colors.white60), hintText: 'Enter API Key', hintStyle: const TextStyle(fontFamily: 'Inter', fontSize: 11, color: Colors.white30), filled: true, fillColor: const Color(0xFF141414), border: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: Color(0xFF222222))), enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: Color(0xFF222222))), focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: Color(0xFF00E5FF))), isDense: true, ), style: const TextStyle(fontFamily: 'Inter', fontSize: 12, color: Colors.white), onChanged: (val) { ref.read(communityApiKeyProvider.notifier).setApiKey(val); }, ), const SizedBox(height: 10), TextField( controller: _communityEndpointController, decoration: InputDecoration( labelText: 'Custom Endpoint URL (Optional)', labelStyle: const TextStyle(fontFamily: 'Inter', fontSize: 11, color: Colors.white60), hintText: 'e.g. https://google-map-places.p.rapidapi.com', hintStyle: const TextStyle(fontFamily: 'Inter', fontSize: 11, color: Colors.white30), filled: true, fillColor: const Color(0xFF141414), border: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: Color(0xFF222222))), enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: Color(0xFF222222))), focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: Color(0xFF00E5FF))), isDense: true, ), style: const TextStyle(fontFamily: 'Inter', fontSize: 12, color: Colors.white), onChanged: (val) { ref.read(communityEndpointUrlProvider.notifier).setEndpoint(val); }, ), const SizedBox(height: 12), Align( alignment: Alignment.centerRight, child: ElevatedButton.icon( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF00E5FF).withValues(alpha: 0.15), foregroundColor: const Color(0xFF00E5FF), side: const BorderSide(color: Color(0xFF00E5FF)), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), onPressed: _isTestingCommunity ? null : () async { setState(() => _isTestingCommunity = true); final messenger = ScaffoldMessenger.of(context); final provider = ref.read(communityProviderInstance); final status = await provider.testConnection(_communityKeyController.text); ref.read(communityStatusProvider.notifier).state = status; setState(() => _isTestingCommunity = false); String msg = status == RoadAlertProviderStatus.connected ? 'Community API Connected successfully!' : 'Status: ${status.name}'; messenger.showSnackBar(SnackBar(content: Text(msg))); }, icon: _isTestingCommunity ? const SizedBox(width: 14, height: 14, child: CircularProgressIndicator(strokeWidth: 2, color: Color(0xFF00E5FF))) : const Icon(Icons.sync, size: 16), label: const Text('TEST CONNECTION', style: TextStyle(fontFamily: 'Inter', fontSize: 10, fontWeight: FontWeight.w800)), ), ), ], ), ); } Widget _buildStatusBadge(RoadAlertProviderStatus status) { Color color = Colors.white30; String text = 'OFF'; if (status == RoadAlertProviderStatus.connected) { color = const Color(0xFF69F0AE); text = 'CONNECTED'; } else if (status == RoadAlertProviderStatus.authError) { color = const Color(0xFFFF5252); text = 'AUTH ERROR'; } else if (status == RoadAlertProviderStatus.rateLimited) { color = const Color(0xFFFFD740); text = 'RATE LIMITED'; } else if (status == RoadAlertProviderStatus.networkError) { color = const Color(0xFFFF5252); text = 'NETWORK ERROR'; } return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: color.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(6), border: Border.all(color: color, width: 0.8), ), child: Text( text, style: TextStyle( fontFamily: 'Inter', fontSize: 9, fontWeight: FontWeight.w800, color: color, ), ), ); } Widget _buildVersionFooter() { return FutureBuilder( future: AppInfo.versionString(), builder: (context, snapshot) { final ver = snapshot.data ?? AppInfo.fallbackVersion; return Center( child: Column( children: [ Text( 'MotorSignal v$ver', style: const TextStyle( fontFamily: 'Inter', fontSize: 12, fontWeight: FontWeight.w700, color: Colors.white54, letterSpacing: 1.0, ), ), const SizedBox(height: 2), const Text( 'High-Performance Motorcycle Telemetry Engine', style: TextStyle( fontFamily: 'Inter', fontSize: 9, color: Colors.white30, ), ), ], ), ); }, ); } }