import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../services/sensor_fusion_service.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 '../../database/database.dart'; class SettingsScreen extends ConsumerStatefulWidget { const SettingsScreen({super.key}); @override ConsumerState createState() => _SettingsScreenState(); } class _SettingsScreenState extends ConsumerState { late TextEditingController _tomtomKeyController; late TextEditingController _communityKeyController; bool _isTestingTomTom = false; bool _isTestingCommunity = false; @override void initState() { super.initState(); _tomtomKeyController = TextEditingController(text: ref.read(tomtomApiKeyProvider)); _communityKeyController = TextEditingController(text: ref.read(communityApiKeyProvider)); } @override void dispose() { _tomtomKeyController.dispose(); _communityKeyController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final mapType = ref.watch(mapProviderTypeNotifierProvider); final roadAlertsEnabled = ref.watch(roadAlertsEnabledProvider); final audioAlertsEnabled = ref.watch(audioAlertsEnabledProvider); final alertRadiusMeters = ref.watch(alertRadiusMetersProvider); final enabledCategories = ref.watch(enabledCategoriesProvider); final tomtomStatus = ref.watch(tomtomStatusProvider); final communityStatus = ref.watch(communityStatusProvider); return Scaffold( appBar: AppBar(title: const Text('Settings')), body: ListView( padding: const EdgeInsets.all(16.0), children: [ const Text( 'Maps & Navigation Engine', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.cyanAccent), ), const SizedBox(height: 16), Card( clipBehavior: Clip.antiAlias, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon(Icons.map, color: Colors.cyanAccent), SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Active Map Engine', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), SizedBox(height: 4), Text('Select map rendering backend for live map, route preview, and replay.', style: TextStyle(fontSize: 13, color: Colors.white70)), ], ), ), ], ), const SizedBox(height: 16), SegmentedButton( segments: const [ ButtonSegment( value: AppMapType.openStreetMap, label: Text('OpenStreetMap (Primary)'), icon: Icon(Icons.public), ), ButtonSegment( value: AppMapType.googleMaps, label: Text('Google Maps'), icon: Icon(Icons.map_outlined), ), ], selected: {mapType}, onSelectionChanged: (Set selection) { ref .read(mapProviderTypeNotifierProvider.notifier) .setMapProviderType(selection.first); }, ), ], ), ), ), const SizedBox(height: 32), const Text( 'Road Alerts & Incidents', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.cyanAccent), ), const SizedBox(height: 16), Card( clipBehavior: Clip.antiAlias, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SwitchListTile( activeThumbColor: Colors.cyanAccent, title: const Text('Road Alerts', style: TextStyle(fontWeight: FontWeight.bold)), subtitle: const Text('Display real-time police, hazards, and traffic incidents on map.'), value: roadAlertsEnabled, onChanged: (val) { ref.read(roadAlertsEnabledProvider.notifier).setEnabled(val); }, ), const Divider(), SwitchListTile( activeThumbColor: Colors.cyanAccent, title: const Text('Audio Warnings (TTS)', style: TextStyle(fontWeight: FontWeight.bold)), subtitle: const Text('Announce approaching hazards and enforcement over helmet headset.'), value: audioAlertsEnabled, onChanged: roadAlertsEnabled ? (val) { ref.read(audioAlertsEnabledProvider.notifier).setEnabled(val); } : null, ), const Divider(), Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Alert Radius', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), SizedBox(height: 4), Text('Distance to detect upcoming incidents', style: TextStyle(fontSize: 12, color: Colors.white70)), ], ), DropdownButton( value: alertRadiusMeters, items: const [ DropdownMenuItem(value: 152.4, child: Text('500 ft')), DropdownMenuItem(value: 304.8, child: Text('1,000 ft')), DropdownMenuItem(value: 402.3, child: Text('1/4 Mile')), DropdownMenuItem(value: 804.7, child: Text('1/2 Mile')), DropdownMenuItem(value: 1609.3, child: Text('1 Mile')), ], onChanged: roadAlertsEnabled ? (val) { if (val != null) { ref.read(alertRadiusMetersProvider.notifier).setRadius(val); } } : null, ), ], ), ), const Divider(), const Padding( padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: Text('Alert Categories', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), ), ...RoadAlertCategory.values.map((cat) { final isChecked = enabledCategories.contains(cat); return CheckboxListTile( activeColor: Colors.cyanAccent, checkColor: Colors.black, title: Text(cat.displayName), secondary: Icon(cat.icon, color: cat.color), value: isChecked, onChanged: roadAlertsEnabled ? (val) { ref .read(enabledCategoriesProvider.notifier) .toggleCategory(cat, val ?? true); } : null, ); }), ], ), ), ), const SizedBox(height: 16), // Community Traffic Provider Card Card( clipBehavior: Clip.antiAlias, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.local_police, color: Colors.blueAccent), const SizedBox(width: 12), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Community Traffic Provider', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), SizedBox(height: 4), Text('Supplies Police, Speed Cameras, and Crowd Hazards.', style: TextStyle(fontSize: 12, color: Colors.white70)), ], ), ), _buildStatusBadge(communityStatus), ], ), const SizedBox(height: 16), TextField( controller: _communityKeyController, decoration: const InputDecoration( labelText: 'Community Provider API Key', hintText: 'Enter API Key for Community Traffic Feed', border: OutlineInputBorder(), ), onChanged: (val) { ref.read(communityApiKeyProvider.notifier).setApiKey(val); }, ), const SizedBox(height: 12), Align( alignment: Alignment.centerRight, child: ElevatedButton.icon( 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); if (mounted) { String msg = 'Community API Connected successfully!'; if (status == RoadAlertProviderStatus.authError) msg = 'Authentication Error: Invalid Key.'; if (status == RoadAlertProviderStatus.notConfigured) msg = 'Key is empty.'; messenger.showSnackBar(SnackBar(content: Text(msg))); } }, icon: _isTestingCommunity ? const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2)) : const Icon(Icons.sync), label: const Text('Test Connection'), ), ), ], ), ), ), const SizedBox(height: 16), // TomTom Provider API Key Card Card( clipBehavior: Clip.antiAlias, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.security, color: Colors.cyanAccent), const SizedBox(width: 12), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('TomTom Traffic Provider', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), SizedBox(height: 4), Text('Supplies Accidents, Hazards, Closures & Construction data.', style: TextStyle(fontSize: 12, color: Colors.white70)), ], ), ), _buildStatusBadge(tomtomStatus), ], ), const SizedBox(height: 16), TextField( controller: _tomtomKeyController, decoration: const InputDecoration( labelText: 'TomTom API Key', hintText: 'Enter your free TomTom API Key', border: OutlineInputBorder(), ), onChanged: (val) { ref.read(tomtomApiKeyProvider.notifier).setApiKey(val); }, ), const SizedBox(height: 12), Align( alignment: Alignment.centerRight, child: ElevatedButton.icon( 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); if (mounted) { String msg = 'TomTom API Connected successfully!'; if (status == RoadAlertProviderStatus.authError) msg = 'Authentication Error: Invalid Key.'; if (status == RoadAlertProviderStatus.notConfigured) msg = 'Key is empty.'; messenger.showSnackBar(SnackBar(content: Text(msg))); } }, icon: _isTestingTomTom ? const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2)) : const Icon(Icons.sync), label: const Text('Test Connection'), ), ), ], ), ), ), // Developer Test Mode Section (Debug Mode Only) if (kDebugMode) ...[ const SizedBox(height: 32), const Text( 'Developer Options', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.orangeAccent), ), const SizedBox(height: 16), Card( color: Colors.orange.withValues(alpha: 0.1), shape: RoundedRectangleBorder( side: const BorderSide(color: Colors.orangeAccent), borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon(Icons.bug_report, color: Colors.orangeAccent), SizedBox(width: 12), Text('Road Alert Test Mode', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.orangeAccent)), ], ), const SizedBox(height: 8), const Text('Generate simulated test alerts for real-device testing (Debug Build Only).', style: TextStyle(fontSize: 12, color: Colors.white70)), const SizedBox(height: 16), Wrap( spacing: 8, runSpacing: 8, children: [ ElevatedButton.icon( icon: const Icon(Icons.local_police, color: Colors.blueAccent, size: 18), label: const Text('Test Police (100m)'), onPressed: () { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Simulated Police Alert 100m ahead triggered.'))); }, ), ElevatedButton.icon( icon: const Icon(Icons.warning, color: Colors.orangeAccent, size: 18), label: const Text('Test Hazard (250m)'), onPressed: () { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Simulated Hazard Alert 250m ahead triggered.'))); }, ), ], ), ], ), ), ), ], const SizedBox(height: 32), const Text( 'Sensors', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.cyanAccent), ), const SizedBox(height: 16), Card( clipBehavior: Clip.antiAlias, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Icon(Icons.screen_rotation, color: Colors.cyanAccent), const SizedBox(width: 12), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Calibrate Mount Angle', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), SizedBox(height: 4), Text( 'Place the motorcycle perfectly upright and tap this to zero the lean angle sensor.', style: TextStyle(fontSize: 13, color: Colors.white70, height: 1.3), ), ], ), ), ], ), const SizedBox(height: 16), Align( alignment: Alignment.centerRight, child: ElevatedButton( onPressed: () async { ref.read(sensorFusionServiceProvider).calibrateUpright(); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Sensors Calibrated!')), ); } }, child: const Text('Calibrate'), ), ), ], ), ), ), const SizedBox(height: 32), const Text( 'Data', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.cyanAccent), ), const SizedBox(height: 16), Card( clipBehavior: Clip.antiAlias, child: InkWell( onTap: () => context.go('/settings/diagnostics'), child: const Padding( padding: EdgeInsets.all(16.0), child: Row( children: [ Icon(Icons.monitor_heart, color: Colors.cyanAccent), SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Sensor Diagnostics', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), SizedBox(height: 4), Text('View raw IMU and GPS values.', style: TextStyle(fontSize: 13, color: Colors.white70)), ], ), ), Icon(Icons.chevron_right, color: Colors.white54), ], ), ), ), ), const SizedBox(height: 16), Card( clipBehavior: Clip.antiAlias, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Icon(Icons.delete_forever, color: Colors.redAccent), const SizedBox(width: 12), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Clear Ride History', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), SizedBox(height: 4), Text('Permanently delete all recorded rides and performance runs.', style: TextStyle(fontSize: 13, color: Colors.white70, height: 1.3)), ], ), ), ], ), const SizedBox(height: 16), Align( alignment: Alignment.centerRight, child: ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: Colors.red.withValues(alpha: 0.2)), onPressed: () async { final db = ref.read(databaseProvider); await db.delete(db.rides).go(); await db.delete(db.performanceRuns).go(); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('All data cleared.')), ); } }, child: const Text('Clear', style: TextStyle(color: Colors.redAccent)), ), ), ], ), ), ), ], ), ); } Widget _buildStatusBadge(RoadAlertProviderStatus status) { String label; Color color; switch (status) { case RoadAlertProviderStatus.connected: label = 'Connected'; color = Colors.greenAccent; break; case RoadAlertProviderStatus.configured: label = 'Configured'; color = Colors.cyanAccent; break; case RoadAlertProviderStatus.authError: label = 'Auth Error'; color = Colors.redAccent; break; case RoadAlertProviderStatus.rateLimited: label = 'Rate Limited'; color = Colors.orangeAccent; break; case RoadAlertProviderStatus.networkError: label = 'Network Error'; color = Colors.amberAccent; break; case RoadAlertProviderStatus.notConfigured: label = 'Not Set'; color = Colors.white38; break; } return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: color.withValues(alpha: 0.15), border: Border.all(color: color), borderRadius: BorderRadius.circular(8), ), child: Text(label, style: TextStyle(color: color, fontSize: 11, fontWeight: FontWeight.bold)), ); } }