import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../core/app_colors.dart'; import '../../services/auth_service.dart'; import '../../services/route_simulator_service.dart'; import '../../database/db_helper.dart'; import '../../models/models.dart'; import '../../data/routes_data.dart'; import '../../widgets/route_map_widget.dart'; import 'citizen_guia_screen.dart'; import 'citizen_reporte_screen.dart'; class CitizenHomeScreen extends StatefulWidget { const CitizenHomeScreen({super.key}); @override State createState() => _CitizenHomeScreenState(); } class _CitizenHomeScreenState extends State { int _tab = 0; @override Widget build(BuildContext context) { final auth = context.watch(); final sim = context.watch(); final dom = auth.primaryDomicilio; // domicilio del ciudadano final last = dom != null ? sim.getNotificationForRoute(dom.routeId) : null; final tabs = [ _HomeTab(auth: auth, sim: sim), const CitizenGuiaScreen(), const CitizenReporteScreen(), ]; return Scaffold( backgroundColor: AppColors.grisFondo, body: Stack(children: [ tabs[_tab], if (last != null) Positioned( top: MediaQuery.of(context).padding.top + 8, left: 0, right: 0, child: _NotifBanner(notif: last, onDismiss: () => sim.dismissRouteNotification(dom?.routeId ?? '')), ), ]), bottomNavigationBar: NavigationBar( selectedIndex: _tab, onDestinationSelected: (i) => setState(() => _tab = i), backgroundColor: Colors.white, indicatorColor: AppColors.guindaPrimary.withOpacity(0.15), destinations: const [ NavigationDestination(icon: Icon(Icons.home_outlined), selectedIcon: Icon(Icons.home, color: AppColors.guindaPrimary), label: 'Inicio'), NavigationDestination(icon: Icon(Icons.eco_outlined), selectedIcon: Icon(Icons.eco, color: AppColors.guindaPrimary), label: 'Guía'), NavigationDestination(icon: Icon(Icons.report_outlined), selectedIcon: Icon(Icons.report, color: AppColors.guindaPrimary), label: 'Reportar'), ], ), ); } } // ── Tab principal (StatefulWidget para cargar status de ruta) ───────────── class _HomeTab extends StatefulWidget { final AuthService auth; final RouteSimulatorService sim; const _HomeTab({required this.auth, required this.sim}); @override State<_HomeTab> createState() => _HomeTabState(); } class _HomeTabState extends State<_HomeTab> { RouteStatusModel? _routeStatus; @override void initState() { super.initState(); _loadStatus(); } Future _loadStatus() async { final dom = widget.auth.primaryDomicilio; if (dom == null) return; final s = await DbHelper.getRouteStatus(dom.routeId); if (mounted) setState(() => _routeStatus = s); } bool get _isRouteProblematic { final s = _routeStatus?.status ?? RouteStatus.enRuta; return s == RouteStatus.cancelada || s == RouteStatus.fallaMecanica || s == RouteStatus.retrasada; } @override Widget build(BuildContext context) { final dom = widget.auth.primaryDomicilio; final routeId = dom?.routeId ?? ''; final route = dom != null ? getRouteById(dom.routeId) : null; final isTruckClose = widget.sim.isTruckClose(routeId); final status = _routeStatus?.status ?? RouteStatus.enRuta; return RefreshIndicator( onRefresh: _loadStatus, child: CustomScrollView(slivers: [ SliverAppBar( expandedHeight: 120, pinned: true, backgroundColor: AppColors.guindaPrimary, bottom: PreferredSize(preferredSize: const Size.fromHeight(4), child: Container(height: 4, color: AppColors.dorado)), flexibleSpace: FlexibleSpaceBar( background: Container( color: AppColors.guindaPrimary, padding: const EdgeInsets.fromLTRB(20, 50, 20, 16), child: Row(children: [ const Icon(Icons.delete_sweep_rounded, color: AppColors.dorado, size: 30), const SizedBox(width: 12), Expanded(child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Hola, ${widget.auth.currentUser?.nombre.split(' ').first ?? ''}', style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)), const Text('Celaya Limpia', style: TextStyle(color: AppColors.dorado, fontSize: 12)), ], )), IconButton( icon: const Icon(Icons.logout, color: Colors.white70), onPressed: () async { await widget.auth.logout(); if (context.mounted) Navigator.pushReplacementNamed(context, '/login'); }, ), ]), ), ), ), SliverPadding( padding: const EdgeInsets.all(16), sliver: SliverList(delegate: SliverChildListDelegate([ // ── Si la ruta tiene problema → mostrar alerta en vez de ETA/mapa if (_isRouteProblematic) ...[ _RouteStatusBanner(status: _routeStatus!), const SizedBox(height: 12), ] else ...[ // ETA Card normal _EtaCard(sim: widget.sim, routeId: routeId, dom: dom, route: route), const SizedBox(height: 12), // Mapa solo cuando camión está cerca if (isTruckClose && route != null) ...[ Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.orange.shade50, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.orange.shade300), ), child: const Row(children: [ Icon(Icons.location_on, color: Colors.orange, size: 18), SizedBox(width: 6), Expanded(child: Text('📍 El camión está cerca — mapa activado', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.orange, fontSize: 12))), ]), ), const SizedBox(height: 8), RouteMapWidget(route: route, simulator: widget.sim, height: 220), const SizedBox(height: 12), ], ], // Aviso privacidad Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.amber.shade50, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.amber.shade300), ), child: const Row(children: [ Icon(Icons.shield_outlined, color: Colors.amber, size: 18), SizedBox(width: 6), Expanded(child: Text('🔒 Solo ves la información de tu ruta asignada.', style: TextStyle(fontSize: 11, color: Colors.black87))), ]), ), const SizedBox(height: 12), // Info domicilio if (dom != null) Card(child: Padding( padding: const EdgeInsets.all(14), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row(children: [ Icon(Icons.location_on, color: AppColors.guindaPrimary, size: 16), SizedBox(width: 6), Text('Mi Domicilio', style: TextStyle(fontWeight: FontWeight.bold, color: AppColors.guindaPrimary)), ]), const Divider(), Text(dom.calle, style: const TextStyle(fontSize: 13)), Text('${dom.colonia} — ${dom.routeId}', style: const TextStyle(color: AppColors.grisTexto, fontSize: 12)), Text(dom.horarioEstimado, style: const TextStyle(color: AppColors.grisTexto, fontSize: 11)), ]), )), // Historial notificaciones if (widget.sim.history.isNotEmpty) ...[ const SizedBox(height: 12), Card(child: Padding( padding: const EdgeInsets.all(14), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Alertas recientes', style: TextStyle(fontWeight: FontWeight.bold, color: AppColors.guindaPrimary)), const Divider(), ...widget.sim.history.take(4).map((n) { final color = n.event == NotifEvent.truckProximity ? AppColors.naranjaAlerta : n.event == NotifEvent.routeCompleted ? AppColors.verdeExito : n.event == NotifEvent.routeCancelled ? AppColors.rojoError : AppColors.azulInfo; final icon = n.event == NotifEvent.truckProximity ? Icons.warning_amber : n.event == NotifEvent.routeCompleted ? Icons.check_circle : n.event == NotifEvent.routeCancelled ? Icons.cancel : Icons.local_shipping; return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row(children: [ Icon(icon, size: 14, color: color), const SizedBox(width: 6), Expanded(child: Text(n.title, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500))), Text( '${n.timestamp.hour.toString().padLeft(2, '0')}:${n.timestamp.minute.toString().padLeft(2, '0')}', style: const TextStyle(fontSize: 10, color: AppColors.grisTexto), ), ]), ); }), ]), )), ], const SizedBox(height: 80), ])), ), ]), ); } } // ── Banner de ruta con problema ─────────────────────────────────────────── class _RouteStatusBanner extends StatelessWidget { final RouteStatusModel status; const _RouteStatusBanner({required this.status}); @override Widget build(BuildContext context) { final isCancelled = status.status == RouteStatus.cancelada; final isFalla = status.status == RouteStatus.fallaMecanica; final isRetrasada = status.status == RouteStatus.retrasada; final color = isCancelled ? AppColors.rojoError : isFalla ? Colors.red.shade800 : AppColors.naranjaAlerta; final icon = isCancelled ? Icons.cancel : isFalla ? Icons.build : Icons.access_time; final titulo = isCancelled ? '❌ Ruta Cancelada Hoy' : isFalla ? '🔧 Falla Mecánica en Servicio' : '⏱️ Servicio con Retraso'; final descripcion = isCancelled ? 'El servicio de recolección de tu colonia no se realizará hoy. Favor de guardar tus residuos para la próxima jornada.' : isFalla ? 'El camión asignado a tu sector presentó una falla mecánica. El Ayuntamiento está atendiendo la situación.' : 'El camión de tu sector presenta un retraso en su recorrido. El servicio se realizará, pero con demora.'; return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ // Alerta principal Container( width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(12), boxShadow: [BoxShadow(color: color.withOpacity(0.4), blurRadius: 8, offset: const Offset(0, 4))], ), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Row(children: [ Icon(icon, color: Colors.white, size: 26), const SizedBox(width: 10), Expanded(child: Text(titulo, style: const TextStyle(color: Colors.white, fontSize: 17, fontWeight: FontWeight.bold))), ]), const SizedBox(height: 10), Text(descripcion, style: const TextStyle(color: Colors.white, fontSize: 13, height: 1.4)), ]), ), // Mensaje del administrador (posible solución) if (status.mensaje != null && status.mensaje!.isNotEmpty) ...[ const SizedBox(height: 10), Container( width: double.infinity, padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), border: Border.all(color: color.withOpacity(0.4)), ), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Row(children: [ Icon(Icons.admin_panel_settings, color: color, size: 16), const SizedBox(width: 6), Text('Mensaje del Ayuntamiento', style: TextStyle(fontWeight: FontWeight.bold, color: color, fontSize: 13)), ]), const SizedBox(height: 6), Text(status.mensaje!, style: const TextStyle(fontSize: 13, color: AppColors.negroTexto, height: 1.4)), ]), ), ], // Consejo ciudadano const SizedBox(height: 10), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.grey.shade100, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey.shade300), ), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('💡 Recomendaciones:', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12, color: AppColors.grisTexto)), const SizedBox(height: 4), if (isCancelled) const Text('• Guarda tus bolsas en un lugar cerrado\n' '• No dejes residuos en la acera\n' '• Revisa la app mañana para el horario actualizado', style: TextStyle(fontSize: 12, color: AppColors.grisTexto)), if (isFalla) const Text('• Espera confirmación del Ayuntamiento\n' '• Puede enviarse una unidad de reemplazo\n' '• Revisa las alertas en esta pantalla', style: TextStyle(fontSize: 12, color: AppColors.grisTexto)), if (isRetrasada) const Text('• Tu basura será recogida hoy, con demora\n' '• Puedes sacar tus bolsas cuando recibas la alerta\n' '• Recibirás notificación cuando el camión se acerque', style: TextStyle(fontSize: 12, color: AppColors.grisTexto)), ]), ), ]); } } // ── ETA Card ────────────────────────────────────────────────────────────── class _EtaCard extends StatelessWidget { final RouteSimulatorService sim; final String routeId; final dom; final route; const _EtaCard({required this.sim, required this.routeId, required this.dom, required this.route}); @override Widget build(BuildContext context) => Container( decoration: BoxDecoration( gradient: const LinearGradient(colors: [AppColors.guindaPrimary, AppColors.guindaDark], begin: Alignment.topLeft, end: Alignment.bottomRight), borderRadius: BorderRadius.circular(14), boxShadow: [BoxShadow(color: AppColors.guindaDark.withOpacity(0.4), blurRadius: 8, offset: const Offset(0, 4))], ), padding: const EdgeInsets.all(18), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Row(children: [ const Icon(Icons.local_shipping, color: AppColors.dorado, size: 22), const SizedBox(width: 8), Expanded(child: Text(route?.name ?? 'Ruta asignada', style: const TextStyle(color: AppColors.dorado, fontSize: 13, fontWeight: FontWeight.w600))), ]), const SizedBox(height: 8), Text(sim.getEtaText(routeId), style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold)), const SizedBox(height: 8), if (dom != null) Text('⏰ ${dom.horarioEstimado}', style: const TextStyle(color: Colors.white60, fontSize: 11)), const SizedBox(height: 10), LinearProgressIndicator( value: route != null ? (sim.getPositionIndex(routeId) + 1) / route.positions.length : 0, backgroundColor: Colors.white24, valueColor: const AlwaysStoppedAnimation(AppColors.dorado), ), ]), ); } // ── Banner notificación ─────────────────────────────────────────────────── class _NotifBanner extends StatelessWidget { final AppNotification notif; final VoidCallback onDismiss; const _NotifBanner({required this.notif, required this.onDismiss}); @override Widget build(BuildContext context) { final color = notif.event == NotifEvent.truckProximity ? AppColors.naranjaAlerta : notif.event == NotifEvent.routeCompleted ? AppColors.verdeExito : notif.event == NotifEvent.routeCancelled ? AppColors.rojoError : notif.event == NotifEvent.gpsLost ? Colors.red.shade800 : AppColors.azulInfo; return Material( color: Colors.transparent, child: Container( margin: const EdgeInsets.all(12), decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(12), boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 8, offset: Offset(0, 4))]), child: Padding( padding: const EdgeInsets.all(12), child: Row(children: [ const Icon(Icons.notifications_active, color: Colors.white, size: 24), const SizedBox(width: 10), Expanded(child: Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text(notif.title, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13)), Text(notif.body, style: const TextStyle(color: Colors.white70, fontSize: 11), maxLines: 2, overflow: TextOverflow.ellipsis), ])), IconButton(icon: const Icon(Icons.close, color: Colors.white, size: 18), onPressed: onDismiss), ]), ), ), ); } }