import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/theme/app_theme.dart'; import '../../core/widgets/app_widgets.dart'; import 'data/profile_service.dart'; import 'providers/profile_providers.dart'; class EditProfileScreen extends ConsumerStatefulWidget { const EditProfileScreen({super.key}); @override ConsumerState createState() => _EditProfileScreenState(); } class _EditProfileScreenState extends ConsumerState { final _formKey = GlobalKey(); final _nameCtrl = TextEditingController(); final _emailCtrl = TextEditingController(); final _phoneCtrl = TextEditingController(); final _currentPasswordCtrl = TextEditingController(); final _newPasswordCtrl = TextEditingController(); final _confirmPasswordCtrl = TextEditingController(); bool _saving = false; bool _prefilled = false; @override void dispose() { _nameCtrl.dispose(); _emailCtrl.dispose(); _phoneCtrl.dispose(); _currentPasswordCtrl.dispose(); _newPasswordCtrl.dispose(); _confirmPasswordCtrl.dispose(); super.dispose(); } void _prefill(Map data) { if (_prefilled) return; _nameCtrl.text = data['name'] ?? ''; _emailCtrl.text = data['email'] ?? ''; _phoneCtrl.text = _formatPhoneInitial(data['phone']); _prefilled = true; } String _formatPhoneInitial(String? raw) { if (raw == null || raw.isEmpty) return ''; final digits = raw.replaceAll(RegExp(r'\D'), ''); final last10 = digits.length > 10 ? digits.substring(digits.length - 10) : digits; if (last10.length <= 3) return last10; if (last10.length <= 6) { return '${last10.substring(0, 3)}-${last10.substring(3)}'; } return '${last10.substring(0, 3)}-${last10.substring(3, 6)}-${last10.substring(6)}'; } String _friendly(Object e) { if (e is DioException) { final data = e.response?.data; if (data is Map && data['detail'] != null) { return data['detail'].toString(); } return e.message ?? 'Error de red'; } return e.toString(); } bool get _wantsPasswordChange => _currentPasswordCtrl.text.isNotEmpty || _newPasswordCtrl.text.isNotEmpty || _confirmPasswordCtrl.text.isNotEmpty; Future _save() async { if (!_formKey.currentState!.validate()) return; setState(() => _saving = true); final messenger = ScaffoldMessenger.of(context); try { await ref .read(profileServiceProvider) .updateMe( name: _nameCtrl.text.trim(), email: _emailCtrl.text.trim().isEmpty ? null : _emailCtrl.text.trim(), phone: _phoneCtrl.text.trim().isEmpty ? null : _phoneCtrl.text.trim(), ); if (_wantsPasswordChange) { await ref .read(profileServiceProvider) .changePassword( currentPassword: _currentPasswordCtrl.text, newPassword: _newPasswordCtrl.text, ); _currentPasswordCtrl.clear(); _newPasswordCtrl.clear(); _confirmPasswordCtrl.clear(); } ref.invalidate(currentUserProvider); if (!mounted) return; messenger.showSnackBar( SnackBar( content: Text( _wantsPasswordChange ? 'Perfil y contraseña actualizados' : 'Perfil actualizado', ), ), ); } catch (e) { if (!mounted) return; messenger.showSnackBar(SnackBar(content: Text('Error: ${_friendly(e)}'))); } finally { if (mounted) setState(() => _saving = false); } } @override Widget build(BuildContext context) { final userAsync = ref.watch(currentUserProvider); return Scaffold( backgroundColor: AppTheme.background, body: userAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (e, _) => Center( child: Padding( padding: const EdgeInsets.all(24), child: Text( 'No se pudo cargar el perfil:\n${_friendly(e)}', textAlign: TextAlign.center, style: const TextStyle(color: AppTheme.danger), ), ), ), data: (user) { _prefill({ 'name': user.name, 'email': user.email, 'phone': user.phone, }); return Form( key: _formKey, child: CustomScrollView( slivers: [ SliverToBoxAdapter(child: _buildPageHeader(context)), SliverPadding( padding: const EdgeInsets.fromLTRB(24, 24, 24, 0), sliver: SliverList( delegate: SliverChildListDelegate([ const AppSectionTitle(title: 'Datos personales'), AppFormCard( icon: Icons.person_outline, title: 'Información personal', child: Column( children: [ AppFormField( label: 'Nombre', controller: _nameCtrl, validator: (v) => (v == null || v.trim().isEmpty) ? 'Ingresa tu nombre' : null, ), const SizedBox(height: 12), AppFormField( label: 'Correo electrónico', controller: _emailCtrl, keyboardType: TextInputType.emailAddress, validator: (v) { if (v == null || v.trim().isEmpty) return null; if (!v.contains('@')) return 'Correo inválido'; return null; }, ), const SizedBox(height: 12), _PhoneField(controller: _phoneCtrl), ], ), ), const SizedBox(height: 20), const AppSectionTitle(title: 'Cambiar contraseña'), AppFormCard( icon: Icons.lock_outline, title: 'Seguridad', child: Column( children: [ AppFormField( label: 'Contraseña actual', controller: _currentPasswordCtrl, obscureText: true, validator: (v) { if (!_wantsPasswordChange) return null; if (v == null || v.length < 6) { return 'Mínimo 6 caracteres'; } return null; }, ), const SizedBox(height: 12), AppFormField( label: 'Nueva contraseña', controller: _newPasswordCtrl, obscureText: true, validator: (v) { if (!_wantsPasswordChange) return null; if (v == null || v.length < 6) { return 'Mínimo 6 caracteres'; } return null; }, ), const SizedBox(height: 12), AppFormField( label: 'Confirmar nueva contraseña', controller: _confirmPasswordCtrl, obscureText: true, validator: (v) { if (!_wantsPasswordChange) return null; if (v == null || v.isEmpty) { return 'Confirma la contraseña'; } if (v != _newPasswordCtrl.text) { return 'No coincide'; } return null; }, ), const SizedBox(height: 8), const Align( alignment: Alignment.centerLeft, child: Text( 'Déjalo en blanco si no deseas cambiarla.', style: TextStyle( fontSize: 12, color: AppTheme.textSecondary, ), ), ), ], ), ), const SizedBox(height: 32), ]), ), ), ], ), ); }, ), bottomNavigationBar: _buildSaveButton(), ); } Widget _buildPageHeader(BuildContext context) { return Container( padding: EdgeInsets.fromLTRB( 20, MediaQuery.of(context).padding.top + 12, 20, 24, ), decoration: const BoxDecoration( gradient: LinearGradient( colors: [Color(0xFF4A0E26), Color(0xFF9B1B4A)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(28), bottomRight: Radius.circular(28), ), ), child: Row( children: [ GestureDetector( onTap: () => Navigator.of(context).pop(), child: Container( width: 36, height: 36, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(10), ), child: const Icon( Icons.arrow_back, color: Colors.white, size: 20, ), ), ), const SizedBox(width: 16), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Editar perfil', style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, color: Colors.white, ), ), SizedBox(height: 2), Text( 'Actualiza tu información personal', style: TextStyle( fontSize: 13, color: Colors.white70, ), ), ], ), ), GestureDetector( onTap: _saving ? null : _save, child: Container( width: 36, height: 36, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(10), ), child: _saving ? const Padding( padding: EdgeInsets.all(8), child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Icon(Icons.save_outlined, color: Colors.white, size: 20), ), ), ], ), ); } Widget _buildSaveButton() { return SafeArea( child: Padding( padding: const EdgeInsets.fromLTRB(24, 12, 24, 16), child: SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _saving ? null : _save, child: _saving ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Text('Guardar cambios'), ), ), ), ); } } // ── Campo de teléfono con lada +52 y formato 000-000-0000 ───────────────────── class _PhoneField extends StatelessWidget { final TextEditingController controller; const _PhoneField({required this.controller}); static const _ladas = [(flag: '🇲🇽', code: '+52', name: 'México')]; @override Widget build(BuildContext context) { final lada = _ladas.first; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Teléfono', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, color: AppTheme.textSecondary, ), ), const SizedBox(height: 6), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( height: 50, padding: const EdgeInsets.symmetric(horizontal: 12), decoration: BoxDecoration( color: AppTheme.background, borderRadius: BorderRadius.circular(AppTheme.radiusSm), border: Border.all(color: AppTheme.border), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text(lada.flag, style: const TextStyle(fontSize: 20)), const SizedBox(width: 6), Text( lada.code, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), ], ), ), const SizedBox(width: 8), Expanded( child: TextFormField( controller: controller, keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(10), _PhoneInputFormatter(), ], style: const TextStyle( fontSize: 14, color: AppTheme.textPrimary, ), decoration: InputDecoration( hintText: '000-000-0000', hintStyle: const TextStyle( color: AppTheme.textSecondary, fontSize: 14, ), filled: true, fillColor: AppTheme.background, contentPadding: const EdgeInsets.symmetric( horizontal: 14, vertical: 15, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(AppTheme.radiusSm), borderSide: const BorderSide(color: AppTheme.border), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(AppTheme.radiusSm), borderSide: const BorderSide(color: AppTheme.border), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(AppTheme.radiusSm), borderSide: const BorderSide( color: AppTheme.primary, width: 1.5, ), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(AppTheme.radiusSm), borderSide: const BorderSide(color: AppTheme.danger), ), focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(AppTheme.radiusSm), borderSide: const BorderSide( color: AppTheme.danger, width: 1.5, ), ), ), validator: (v) { if (v == null || v.isEmpty) return null; final digits = v.replaceAll('-', ''); if (digits.length != 10) { return 'Ingresa exactamente 10 dígitos'; } return null; }, ), ), ], ), ], ); } } class _PhoneInputFormatter extends TextInputFormatter { @override TextEditingValue formatEditUpdate( TextEditingValue oldValue, TextEditingValue newValue, ) { final digits = newValue.text.replaceAll(RegExp(r'\D'), ''); final String formatted; if (digits.length <= 3) { formatted = digits; } else if (digits.length <= 6) { formatted = '${digits.substring(0, 3)}-${digits.substring(3)}'; } else { formatted = '${digits.substring(0, 3)}-${digits.substring(3, 6)}-${digits.substring(6)}'; } return TextEditingValue( text: formatted, selection: TextSelection.collapsed(offset: formatted.length), ); } }