import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../core/api_client.dart'; import '../core/app_theme.dart'; import '../servicios/delivery_repository.dart'; import '../servicios/push_notification_service.dart'; enum _LoginStep { tenant, credentials } class LoginScreen extends StatefulWidget { const LoginScreen({required this.repository, super.key}); final DeliveryRepository repository; @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State { final _ruc = TextEditingController(); final _email = TextEditingController(); final _password = TextEditingController(); _LoginStep _step = _LoginStep.tenant; Map? _tenant; String? _error; bool _busy = false; bool _obscurePassword = true; @override void initState() { super.initState(); _loadSavedTenant(); } Future _loadSavedTenant() async { final prefs = await SharedPreferences.getInstance(); final host = prefs.getString('delivery_tenant_host'); final baseUrl = prefs.getString('delivery_api_base_url'); if (!mounted || host == null || host.isEmpty || baseUrl == null || baseUrl.isEmpty) { return; } setState(() { _tenant = { 'host': host, 'base_url': baseUrl, 'ruc': prefs.getString('delivery_tenant_ruc') ?? '', 'company_name': prefs.getString('delivery_company_name') ?? host, }; _step = _LoginStep.credentials; }); } @override void dispose() { _ruc.dispose(); _email.dispose(); _password.dispose(); super.dispose(); } Future _submit() async { FocusScope.of(context).unfocus(); setState(() => _error = null); if (_step == _LoginStep.tenant) { final ruc = _ruc.text.trim(); if (!RegExp(r'^\d{11}$').hasMatch(ruc)) { setState(() => _error = 'Ingresa un RUC válido de 11 dígitos.'); return; } } else if (_email.text.trim().isEmpty || _password.text.isEmpty) { setState(() => _error = 'Ingresa tu correo y contraseña.'); return; } setState(() => _busy = true); try { if (_step == _LoginStep.tenant) { final tenant = await widget.repository.resolveTenant(_ruc.text.trim()); await widget.repository.saveTenant(tenant); if (!mounted) return; setState(() { _tenant = tenant; _step = _LoginStep.credentials; }); return; } final data = await widget.repository.login( _email.text.trim(), _password.text, ); final user = (data['user'] as Map?)?.cast(); if (user?['delivery_driver'] != true) { throw const ApiException( 403, 'Este usuario no está habilitado como repartidor.', ); } final prefs = await SharedPreferences.getInstance(); await prefs.setString('delivery_api_token', '${data['token']}'); final establishment = (data['establishment'] as Map?) ?.cast(); if (establishment != null) { await prefs.setString( 'delivery_establishment_name', '${establishment['description'] ?? ''}', ); } final push = PushNotificationService(); await push.requestPermission(); await push.registerWithBackend(widget.repository); if (!mounted) return; Navigator.of(context).pushNamedAndRemoveUntil('/orders', (_) => false); } on ApiException catch (error) { if (mounted) setState(() => _error = error.message); } catch (_) { if (mounted) { setState(() => _error = 'No se pudo conectar. Verifica tu conexión.'); } } finally { if (mounted) setState(() => _busy = false); } } void _backToTenant() { setState(() { _step = _LoginStep.tenant; _tenant = null; _error = null; }); } Future _confirmExit() async { final exit = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Salir de SmartPlus Reparto'), content: const Text('¿Deseas cerrar la aplicación?'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Cancelar'), ), FilledButton( onPressed: () => Navigator.pop(context, true), child: const Text('Salir'), ), ], ), ); if (exit == true && mounted) SystemNavigator.pop(); } @override Widget build(BuildContext context) => PopScope( canPop: false, onPopInvokedWithResult: (didPop, result) { if (didPop) return; if (_step == _LoginStep.credentials) { _confirmExit(); } else { _confirmExit(); } }, child: Scaffold( appBar: AppBar(title: const Text('SmartPlus Reparto')), body: SafeArea( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(24), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 430), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Icon( Icons.local_shipping_outlined, size: 64, color: AppTheme.primary, ), const SizedBox(height: 16), Text( _step == _LoginStep.tenant ? 'Identifica tu empresa' : 'Ingresa a tu cuenta', style: Theme.of(context).textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.w700, color: AppTheme.ink, ), textAlign: TextAlign.center, ), const SizedBox(height: 8), Text( _step == _LoginStep.tenant ? 'Primero validaremos el RUC para conectarte al sistema correcto.' : 'Empresa: ${_tenant?['company_name'] ?? ''}', textAlign: TextAlign.center, style: const TextStyle(color: AppTheme.muted), ), const SizedBox(height: 20), LinearProgressIndicator( value: _step == _LoginStep.tenant ? .5 : 1, ), const SizedBox(height: 24), if (_error != null) ...[ Text( _error!, style: TextStyle( color: Theme.of(context).colorScheme.error, ), ), const SizedBox(height: 12), ], if (_step == _LoginStep.tenant) _tenantForm() else _credentialsForm(), const SizedBox(height: 20), FilledButton( onPressed: _busy ? null : _submit, child: _busy ? const SizedBox( height: 22, width: 22, child: CircularProgressIndicator(strokeWidth: 2), ) : Text( _step == _LoginStep.tenant ? 'Continuar' : 'Ingresar', ), ), ], ), ), ), ), ), ), ); Widget _tenantForm() => TextField( controller: _ruc, keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(11), ], decoration: const InputDecoration( labelText: 'RUC', hintText: 'Ingresa el RUC de la empresa', ), ); Widget _credentialsForm() => Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ TextField( controller: _email, keyboardType: TextInputType.emailAddress, autofillHints: const [AutofillHints.username], decoration: const InputDecoration(labelText: 'Correo electrónico'), ), const SizedBox(height: 14), TextField( controller: _password, obscureText: _obscurePassword, autofillHints: const [AutofillHints.password], decoration: InputDecoration( labelText: 'Contraseña', suffixIcon: IconButton( tooltip: _obscurePassword ? 'Mostrar contraseña' : 'Ocultar contraseña', onPressed: () => setState(() => _obscurePassword = !_obscurePassword), icon: Icon( _obscurePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined, ), ), ), ), const SizedBox(height: 8), Align( alignment: Alignment.centerLeft, child: TextButton.icon( onPressed: _busy ? null : _backToTenant, icon: const Icon(Icons.arrow_back, size: 18), label: const Text('Cambiar RUC'), ), ), ], ); }