import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../core/theme/evolution_theme_config.dart'; import '../../services/auth/auth_providers.dart'; import '../../services/social/handle_validator.dart'; import '../../services/social/rider_profile_models.dart'; import '../../services/social/social_providers.dart'; import 'widgets/avatar_badge.dart'; import 'widgets/avatar_selector.dart'; /// Screen allowing a rider to view and manage their public profile and privacy. class RiderProfileScreen extends ConsumerStatefulWidget { const RiderProfileScreen({super.key}); @override ConsumerState createState() => _RiderProfileScreenState(); } class _RiderProfileScreenState extends ConsumerState { final _formKey = GlobalKey(); late TextEditingController _handleController; late TextEditingController _displayNameController; late TextEditingController _bioController; late TextEditingController _regionController; String? _selectedAvatarPreset; bool _isSearchableByHandle = true; bool _isProfilePublic = true; bool _allowFriendRequests = true; bool _isSaving = false; String? _errorMessage; String? _successMessage; bool _initialized = false; @override void initState() { super.initState(); _handleController = TextEditingController(); _displayNameController = TextEditingController(); _bioController = TextEditingController(); _regionController = TextEditingController(); } @override void dispose() { _handleController.dispose(); _displayNameController.dispose(); _bioController.dispose(); _regionController.dispose(); super.dispose(); } void _populateFromProfile(RiderProfile profile) { if (_initialized) return; _handleController.text = profile.handle; _displayNameController.text = profile.displayName; _bioController.text = profile.bio; _regionController.text = profile.region; _selectedAvatarPreset = profile.avatarPreset; _isSearchableByHandle = profile.privacySettings.isSearchableByHandle; _isProfilePublic = profile.privacySettings.isProfilePublic; _allowFriendRequests = profile.privacySettings.allowFriendRequests; _initialized = true; } void _populateDefaults() { if (_initialized) return; final user = ref.read(currentUserProvider); if (user != null && !user.isAnonymous) { if (user.displayName != null && user.displayName!.isNotEmpty) { _displayNameController.text = user.displayName!; final candidate = user.displayName! .toLowerCase() .replaceAll(RegExp(r'[^a-z0-9_]'), '_'); if (candidate.length >= 3) { _handleController.text = candidate.substring(0, candidate.length.clamp(0, 18)); } } else if (user.email != null) { final candidate = user.email!.split('@').first.replaceAll(RegExp(r'[^a-z0-9_]'), '_'); _handleController.text = candidate.substring(0, candidate.length.clamp(0, 18)); } } _selectedAvatarPreset = PresetAvatar.availablePresets.first.id; _initialized = true; } Future _handleSave() async { final user = ref.read(currentUserProvider); if (user == null || user.isAnonymous) return; final handleValidation = HandleValidator.validate(_handleController.text); if (!handleValidation.isValid) { setState(() { _errorMessage = handleValidation.error; }); return; } setState(() { _isSaving = true; _errorMessage = null; _successMessage = null; }); try { final profileService = ref.read(riderProfileServiceProvider); final privacy = ProfilePrivacySettings( isSearchableByHandle: _isSearchableByHandle, isProfilePublic: _isProfilePublic, allowFriendRequests: _allowFriendRequests, ); await profileService.saveProfile( uid: user.uid, handle: _handleController.text, displayName: _displayNameController.text.trim().isEmpty ? _handleController.text : _displayNameController.text.trim(), bio: _bioController.text.trim(), region: _regionController.text.trim(), avatarPreset: _selectedAvatarPreset, privacySettings: privacy, ); if (mounted) { setState(() { _isSaving = false; _successMessage = 'Profile updated successfully!'; }); } } catch (e) { if (mounted) { setState(() { _isSaving = false; _errorMessage = e.toString().replaceFirst('Exception: ', ''); }); } } } @override Widget build(BuildContext context) { final authState = ref.watch(authNotifierProvider); final isAuthed = authState.isAuthenticated; final themeMode = ref.watch(dashboardThemeModeProvider); final isEvolution = themeMode == DashboardThemeMode.evolution; final bgColor = isEvolution ? Colors.black : Theme.of(context).scaffoldBackgroundColor; final titleFont = isEvolution ? 'Sora' : null; if (!isAuthed) { return Scaffold( backgroundColor: bgColor, appBar: AppBar( title: Text( 'Rider Profile', style: TextStyle(fontFamily: titleFont, fontWeight: FontWeight.w800), ), ), body: Center( child: Padding( padding: const EdgeInsets.all(24.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.person_pin_circle_outlined, size: 64, color: Color(0xFF00E5FF), ), const SizedBox(height: 16), Text( 'MOTORCLOUD ACCOUNT REQUIRED', style: TextStyle( fontFamily: titleFont, fontWeight: FontWeight.w800, fontSize: 16, color: Colors.white, letterSpacing: 0.8, ), ), const SizedBox(height: 8), const Text( 'To create a public rider handle, connect with friends, and join future group rides, please sign in or create an account.', textAlign: TextAlign.center, style: TextStyle(fontSize: 13, color: Colors.white70), ), const SizedBox(height: 24), ElevatedButton.icon( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF00E5FF), foregroundColor: Colors.black, ), icon: const Icon(Icons.login), label: const Text('SIGN IN OR REGISTER'), onPressed: () => context.push('/settings/account'), ), ], ), ), ), ); } final profileAsync = ref.watch(myRiderProfileProvider); return Scaffold( backgroundColor: bgColor, appBar: AppBar( title: Text( 'Rider Profile', style: TextStyle(fontFamily: titleFont, fontWeight: FontWeight.w800), ), actions: [ IconButton( icon: const Icon(Icons.people_outline), tooltip: 'Friends & Requests', onPressed: () => context.push('/settings/friends'), ), ], ), body: profileAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (err, _) => Center(child: Text('Error loading profile: $err')), data: (profile) { if (profile != null) { _populateFromProfile(profile); } else { _populateDefaults(); } return SingleChildScrollView( padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 100.0), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Header avatar preview Center( child: Column( children: [ AvatarBadge( avatarPreset: _selectedAvatarPreset, displayName: _displayNameController.text.isEmpty ? 'Rider' : _displayNameController.text, radius: 40, ), const SizedBox(height: 8), Text( _handleController.text.isNotEmpty ? '@${_handleController.text}' : '@handle', style: const TextStyle( color: Color(0xFF00E5FF), fontWeight: FontWeight.w800, fontSize: 14, fontFamily: 'Inter', ), ), ], ), ), const SizedBox(height: 20), // Messages if (_errorMessage != null) ...[ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.redAccent.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.redAccent), ), child: Text( _errorMessage!, style: const TextStyle(color: Colors.redAccent, fontSize: 12), ), ), const SizedBox(height: 16), ], if (_successMessage != null) ...[ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.greenAccent.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.greenAccent), ), child: Text( _successMessage!, style: const TextStyle(color: Colors.greenAccent, fontSize: 12), ), ), const SizedBox(height: 16), ], // Avatar preset selector AvatarSelector( selectedPresetId: _selectedAvatarPreset, onSelected: (presetId) { setState(() { _selectedAvatarPreset = presetId; }); }, ), const SizedBox(height: 24), // Handle input TextFormField( controller: _handleController, decoration: const InputDecoration( labelText: 'Rider Handle', hintText: 'e.g. canyon_runner', prefixText: '@', helperText: 'Unique, 3-20 characters, lowercase letters, numbers, and underscores', border: OutlineInputBorder(), ), onChanged: (_) => setState(() {}), ), const SizedBox(height: 16), // Display Name input TextFormField( controller: _displayNameController, decoration: const InputDecoration( labelText: 'Display Name', hintText: 'e.g. Alex Rivera', border: OutlineInputBorder(), ), onChanged: (_) => setState(() {}), ), const SizedBox(height: 16), // Region input TextFormField( controller: _regionController, decoration: const InputDecoration( labelText: 'Riding Region (Optional)', hintText: 'e.g. Blue Ridge Mountains, NC', border: OutlineInputBorder(), ), ), const SizedBox(height: 16), // Bio input TextFormField( controller: _bioController, maxLines: 3, decoration: const InputDecoration( labelText: 'Rider Bio (Optional)', hintText: 'Tell fellow riders about what you ride and your riding style...', border: OutlineInputBorder(), ), ), const SizedBox(height: 24), // Privacy Settings Card Card( color: Colors.white.withValues(alpha: 0.05), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon(Icons.shield_outlined, color: Color(0xFF00E5FF), size: 18), SizedBox(width: 8), Text( 'PRIVACY CONTROLS', style: TextStyle( color: Color(0xFF00E5FF), fontWeight: FontWeight.w800, fontSize: 12, letterSpacing: 0.8, ), ), ], ), const SizedBox(height: 12), SwitchListTile( contentPadding: EdgeInsets.zero, title: const Text('Discoverable by Handle', style: TextStyle(fontSize: 14)), subtitle: const Text( 'Allow other riders to find your profile by searching your @handle.', style: TextStyle(fontSize: 11, color: Colors.white60), ), value: _isSearchableByHandle, onChanged: (val) => setState(() => _isSearchableByHandle = val), ), const Divider(color: Colors.white12), SwitchListTile( contentPadding: EdgeInsets.zero, title: const Text('Public Profile', style: TextStyle(fontSize: 14)), subtitle: const Text( 'When off, non-friends only see your handle and avatar, hiding bio and region.', style: TextStyle(fontSize: 11, color: Colors.white60), ), value: _isProfilePublic, onChanged: (val) => setState(() => _isProfilePublic = val), ), const Divider(color: Colors.white12), SwitchListTile( contentPadding: EdgeInsets.zero, title: const Text('Allow Friend Requests', style: TextStyle(fontSize: 14)), subtitle: const Text( 'Allow other riders to send you friend requests.', style: TextStyle(fontSize: 11, color: Colors.white60), ), value: _allowFriendRequests, onChanged: (val) => setState(() => _allowFriendRequests = val), ), ], ), ), ), const SizedBox(height: 24), // Save Button SizedBox( height: 48, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF00E5FF), foregroundColor: Colors.black, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), onPressed: _isSaving ? null : _handleSave, child: _isSaving ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.black, ), ) : const Text( 'SAVE PROFILE', style: TextStyle(fontWeight: FontWeight.w900, letterSpacing: 0.8), ), ), ), ], ), ), ); }, ), ); } }