import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../profile/domain/user_profile.dart'; import '../../profile/infrastructure/profile_repository.dart'; import '../application/auth_notifier.dart'; import 'widgets/winded_brand_header.dart'; class RegisterScreen extends ConsumerStatefulWidget { const RegisterScreen({super.key}); @override ConsumerState createState() => _RegisterScreenState(); } class _RegisterScreenState extends ConsumerState { static const _purple = Color(0xFF8B30C8); static const _purpleLight = Color(0xFFBF77F6); final _formKey = GlobalKey(); final _nameCtrl = TextEditingController(); final _emailCtrl = TextEditingController(); final _passwordCtrl = TextEditingController(); final _confirmCtrl = TextEditingController(); bool _obscurePassword = true; bool _obscureConfirm = true; UserRole _selectedRole = UserRole.player; @override void dispose() { _nameCtrl.dispose(); _emailCtrl.dispose(); _passwordCtrl.dispose(); _confirmCtrl.dispose(); super.dispose(); } Future _submit() async { final form = _formKey.currentState; if (form == null || !form.validate()) return; FocusScope.of(context).unfocus(); final displayName = _nameCtrl.text.trim(); final email = _emailCtrl.text.trim(); await ref.read(authNotifierProvider.notifier).register( email: email, password: _passwordCtrl.text, displayName: displayName, ); if (!mounted) return; final state = ref.read(authNotifierProvider); if (state.hasError) { final message = authErrorMessage(state.error!); ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar(SnackBar(content: Text(message))); return; } // Auth succeeded — write the matching Firestore profile so role-based // gates work immediately. Failures here are surfaced but don't block the // sign-in flow because the user is already authenticated. final user = state.valueOrNull; if (user != null) { try { await ref.read(profileRepositoryProvider).createProfile( UserProfile( uid: user.uid, email: user.email, displayName: displayName.isEmpty ? (user.displayName ?? '') : displayName, role: _selectedRole, createdAt: DateTime.now(), ), ); } catch (e) { if (mounted) { ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( SnackBar(content: Text('Could not create profile: $e')), ); } } } // On success, Firebase auth stream emits the new user, AuthNotifier // updates, and the router redirect sends us to /events. } @override Widget build(BuildContext context) { final theme = Theme.of(context); final colors = theme.colorScheme; final authState = ref.watch(authNotifierProvider); final isLoading = authState.isLoading; return Scaffold( backgroundColor: colors.surface, body: SafeArea( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 440), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const WindedBrandHeader(), const SizedBox(height: 8), Text( 'JOIN THE LEAGUE', textAlign: TextAlign.center, style: theme.textTheme.labelMedium?.copyWith( color: colors.onSurfaceVariant, letterSpacing: 2.0, fontWeight: FontWeight.w700, ), ), const SizedBox(height: 32), Container( decoration: BoxDecoration( color: const Color(0xFF1A1A1A), borderRadius: BorderRadius.circular(4), border: Border.all(color: const Color(0xFF3A3A3A)), ), child: Column( children: [ Container( height: 3, decoration: const BoxDecoration( color: _purple, borderRadius: BorderRadius.vertical(top: Radius.circular(4)), ), ), Padding( padding: const EdgeInsets.all(20), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ TextFormField( controller: _nameCtrl, enabled: !isLoading, textCapitalization: TextCapitalization.words, autofillHints: const [AutofillHints.name], textInputAction: TextInputAction.next, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w600, letterSpacing: 0.5, ), decoration: const InputDecoration( labelText: 'DISPLAY NAME', prefixIcon: Icon(Icons.person_outline), ), validator: (v) { final trimmed = v?.trim() ?? ''; if (trimmed.isEmpty) { return 'Enter your name'; } if (trimmed.length < 2) { return 'Name must be at least 2 characters'; } return null; }, ), const SizedBox(height: 16), TextFormField( controller: _emailCtrl, enabled: !isLoading, keyboardType: TextInputType.emailAddress, autofillHints: const [AutofillHints.email], textInputAction: TextInputAction.next, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w600, letterSpacing: 0.5, ), decoration: const InputDecoration( labelText: 'EMAIL', prefixIcon: Icon(Icons.email_outlined), ), validator: (v) { final trimmed = v?.trim() ?? ''; if (trimmed.isEmpty) { return 'Enter your email'; } final emailRegex = RegExp( r'^[^\s@]+@[^\s@]+\.[^\s@]+$', ); if (!emailRegex.hasMatch(trimmed)) { return 'Enter a valid email address'; } return null; }, ), const SizedBox(height: 16), TextFormField( controller: _passwordCtrl, enabled: !isLoading, obscureText: _obscurePassword, autofillHints: const [AutofillHints.newPassword], textInputAction: TextInputAction.next, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w600, letterSpacing: 0.5, ), decoration: InputDecoration( labelText: 'PASSWORD', prefixIcon: const Icon(Icons.lock_outline), suffixIcon: IconButton( icon: Icon( _obscurePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined, ), onPressed: isLoading ? null : () => setState(() { _obscurePassword = !_obscurePassword; }), ), helperText: 'At least 6 characters', ), validator: (v) { if (v == null || v.isEmpty) { return 'Enter a password'; } if (v.length < 6) { return 'Password must be at least 6 characters'; } return null; }, ), const SizedBox(height: 16), TextFormField( controller: _confirmCtrl, enabled: !isLoading, obscureText: _obscureConfirm, textInputAction: TextInputAction.done, onFieldSubmitted: (_) => _submit(), style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w600, letterSpacing: 0.5, ), decoration: InputDecoration( labelText: 'CONFIRM PASSWORD', prefixIcon: const Icon(Icons.lock_outline), suffixIcon: IconButton( icon: Icon( _obscureConfirm ? Icons.visibility_outlined : Icons.visibility_off_outlined, ), onPressed: isLoading ? null : () => setState(() { _obscureConfirm = !_obscureConfirm; }), ), ), validator: (v) { if (v == null || v.isEmpty) { return 'Confirm your password'; } if (v != _passwordCtrl.text) { return 'Passwords do not match'; } return null; }, ), const SizedBox(height: 20), Text( 'I AM A', style: theme.textTheme.labelSmall?.copyWith( color: colors.onSurfaceVariant, letterSpacing: 1.8, fontWeight: FontWeight.w700, ), ), const SizedBox(height: 8), SegmentedButton( segments: const >[ ButtonSegment( value: UserRole.player, label: Text('PLAYER'), icon: Icon(Icons.sports_soccer), ), ButtonSegment( value: UserRole.manager, label: Text('MANAGER'), icon: Icon(Icons.shield_outlined), ), ], selected: {_selectedRole}, onSelectionChanged: isLoading ? null : (set) => setState( () => _selectedRole = set.first, ), showSelectedIcon: false, ), const SizedBox(height: 8), Text( _selectedRole == UserRole.manager ? 'Managers create and run a team. New teams ' 'require admin approval.' : 'Players have a personal profile and can ' 'request to join a team.', style: theme.textTheme.bodySmall?.copyWith( color: colors.onSurfaceVariant, ), ), const SizedBox(height: 24), FilledButton( onPressed: isLoading ? null : _submit, style: FilledButton.styleFrom( minimumSize: const Size.fromHeight(52), shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(4)), ), ), child: isLoading ? SizedBox( width: 22, height: 22, child: CircularProgressIndicator( strokeWidth: 2.5, color: colors.onPrimary, ), ) : const Text( 'CREATE ACCOUNT', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w900, letterSpacing: 2.0, ), ), ), ], ), ), ), ], ), ), const SizedBox(height: 24), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'ALREADY HAVE AN ACCOUNT? ', style: theme.textTheme.labelMedium?.copyWith( color: colors.onSurfaceVariant, letterSpacing: 1.5, ), ), TextButton( onPressed: isLoading ? null : () => context.go('/login'), style: TextButton.styleFrom( foregroundColor: _purpleLight, textStyle: const TextStyle( fontWeight: FontWeight.w800, letterSpacing: 1.5, ), ), child: const Text('SIGN IN'), ), ], ), ], ), ), ), ), ), ); } }