import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; import '../../core/theme/app_theme.dart'; import '../../core/models/ui_models.dart'; import 'colonias_data.dart'; import '../../core/widgets/app_widgets.dart'; class MyHouseScreen extends StatefulWidget { const MyHouseScreen({super.key}); @override State createState() => _MyHouseScreenState(); } class _MyHouseScreenState extends State { bool _isLoading = true; UIHouseModel? _casa; @override void initState() { super.initState(); _cargarDomicilio(); } Future _cargarDomicilio() async { try { const storage = FlutterSecureStorage(); final token = await storage.read(key: 'token') ?? ''; if (token.isEmpty) { setState(() => _isLoading = false); return; } final dio = Dio( BaseOptions( baseUrl: const String.fromEnvironment( 'API_BASE_URL', defaultValue: 'http://localhost:8000', ), headers: {'Authorization': 'Bearer $token'}, ), ); final res = await dio.get('/addresses'); if (res.data is List && (res.data as List).isNotEmpty) { final addr = res.data[0]; setState(() { _casa = UIHouseModel.fromJson(addr); _isLoading = false; }); } else { setState(() => _isLoading = false); } } catch (e) { setState(() => _isLoading = false); debugPrint('Error al cargar domicilio: $e'); } } @override Widget build(BuildContext context) { if (_isLoading) { return const Scaffold( backgroundColor: AppTheme.background, body: Center(child: CircularProgressIndicator()), ); } if (_casa == null) { return Scaffold( backgroundColor: AppTheme.background, appBar: AppBar(title: const Text('Mi casa')), body: const Center(child: Text('No tienes un domicilio registrado.')), ); } return Scaffold( backgroundColor: AppTheme.background, appBar: AppBar( title: const Text('Mi casa'), actions: [ IconButton( icon: const Icon(Icons.edit_outlined), onPressed: () => _mostrarEditarDireccion(context), tooltip: 'Editar dirección', ), ], ), body: ListView( padding: const EdgeInsets.all(16), children: [ _CasaCard(casa: _casa!), const SizedBox(height: 16), const AppSectionTitle(title: 'Mapa del Sector (Restringido)'), _MapaColoniaRestringido(colonia: _casa!.colonia), const SizedBox(height: 16), const AppSectionTitle(title: 'Radio de alerta'), _RadioAlertaCard( radioActual: _casa!.radioAlertaMetros, onChanged: (v) => setState(() => _casa = _casa!.copyWith(radioAlertaMetros: v)), ), const SizedBox(height: 16), const AppSectionTitle(title: 'Notificaciones'), _NotificacionesCard( casa: _casa!, onAlertaCercanaChanged: (v) => setState(() => _casa = _casa!.copyWith(alertaCercana: v)), onAlertaMediaChanged: (v) => setState(() => _casa = _casa!.copyWith(alertaMedia: v)), onRecordatorioChanged: (v) => setState(() => _casa = _casa!.copyWith(recordatorioDiario: v)), ), const SizedBox(height: 16), const AppSectionTitle(title: 'Horario del camión'), _HorarioCard(), const SizedBox(height: 16), GestureDetector( onTap: () => ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Funcionalidad próximamente disponible'), behavior: SnackBarBehavior.floating, backgroundColor: AppTheme.primary, ), ), child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppTheme.surface, borderRadius: BorderRadius.circular(AppTheme.radiusLg), border: Border.all(color: AppTheme.primaryMid), boxShadow: AppTheme.softShadow, ), child: const Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.add_home_outlined, color: AppTheme.primary, size: 20, ), SizedBox(width: 8), Text( 'Agregar otra dirección', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: AppTheme.primary, ), ), ], ), ), ), const SizedBox(height: 24), ], ), ); } void _mostrarEditarDireccion(BuildContext context) { showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: AppTheme.surface, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(AppTheme.radiusXl), ), ), builder: (_) => _EditarDireccionSheet(casa: _casa!), ); } } // ── Tarjeta de la casa ──────────────────────────────────────────────────────── class _CasaCard extends StatelessWidget { final UIHouseModel casa; const _CasaCard({required this.casa}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppTheme.surface, borderRadius: BorderRadius.circular(AppTheme.radiusLg), border: Border.all(color: AppTheme.primaryMid, width: 0.8), boxShadow: AppTheme.softShadow, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: AppTheme.primaryLight, borderRadius: BorderRadius.circular(12), ), child: const Icon( Icons.home_outlined, color: AppTheme.primary, size: 24, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( casa.alias, style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), const SizedBox(height: 4), AppStatusBadge.green(casa.activa ? 'Activa' : 'Inactiva'), ], ), ), ], ), const SizedBox(height: 14), const Divider(color: AppTheme.borderLight), const SizedBox(height: 10), _DetailRow( icon: Icons.location_on_outlined, text: casa.direccionCompleta, ), const SizedBox(height: 8), _DetailRow( icon: Icons.radar_outlined, text: 'Alerta a ${casa.radioAlertaMetros} m de distancia', ), ], ), ); } } // ── Mapa de Colonia (Restringido para Privacidad) ────────────────────────────── class _MapaColoniaRestringido extends StatelessWidget { final String colonia; const _MapaColoniaRestringido({required this.colonia}); @override Widget build(BuildContext context) { // Usa las coordenadas del archivo centralizado de datos de colonias. final center = kColoniasCoordinates[colonia] ?? const LatLng(20.5222, -100.8123); // Creamos una "caja" o límite geográfico de aprox 1km a la redonda final bounds = LatLngBounds( LatLng(center.latitude - 0.01, center.longitude - 0.01), LatLng(center.latitude + 0.01, center.longitude + 0.01), ); return Container( height: 200, decoration: BoxDecoration( borderRadius: BorderRadius.circular(AppTheme.radiusLg), border: Border.all(color: AppTheme.border, width: 1), ), clipBehavior: Clip.hardEdge, child: FlutterMap( options: MapOptions( initialCameraFit: CameraFit.bounds(bounds: bounds), // ESTO ES LA MAGIA DE LA PRIVACIDAD: Bloquea el mapa a esta caja cameraConstraint: CameraConstraint.contain(bounds: bounds), interactionOptions: const InteractionOptions( flags: InteractiveFlag.drag | InteractiveFlag.pinchZoom, ), ), children: [ TileLayer( urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', userAgentPackageName: 'com.onlineshack.recolecta', ), CircleLayer( circles: [ CircleMarker( point: center, color: AppTheme.primary.withValues(alpha: 0.2), borderColor: AppTheme.primary, borderStrokeWidth: 2, radius: 350, // 350 metros a la redonda remarcados useRadiusInMeter: true, ), ], ), ], ), ); } } class _DetailRow extends StatelessWidget { final IconData icon; final String text; const _DetailRow({required this.icon, required this.text}); @override Widget build(BuildContext context) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 15, color: AppTheme.textSecondary), const SizedBox(width: 8), Expanded( child: Text( text, style: const TextStyle( fontSize: 13, color: AppTheme.textSecondary, height: 1.4, ), ), ), ], ); } } // ── Radio de alerta ─────────────────────────────────────────────────────────── class _RadioAlertaCard extends StatelessWidget { final int radioActual; final ValueChanged onChanged; const _RadioAlertaCard({required this.radioActual, required this.onChanged}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppTheme.surface, borderRadius: BorderRadius.circular(AppTheme.radiusLg), border: Border.all(color: AppTheme.border, width: 0.5), boxShadow: AppTheme.softShadow, ), child: Column( children: [200, 400, 600].map((dist) { final selected = dist == radioActual; return GestureDetector( onTap: () => onChanged(dist), child: Container( margin: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 11), decoration: BoxDecoration( color: selected ? AppTheme.primaryLight : AppTheme.background, borderRadius: BorderRadius.circular(AppTheme.radiusSm), border: Border.all( color: selected ? AppTheme.primary : AppTheme.border, width: selected ? 1.5 : 0.5, ), ), child: Row( children: [ Icon( selected ? Icons.radio_button_checked : Icons.radio_button_unchecked, color: selected ? AppTheme.primary : AppTheme.border, size: 18, ), const SizedBox(width: 10), Expanded( child: Text( '$dist metros', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: selected ? AppTheme.primaryDark : AppTheme.textPrimary, ), ), ), if (selected) Text( dist == 200 ? '~2-3 min' : dist == 400 ? '~4-5 min' : '~6-8 min', style: const TextStyle( fontSize: 12, color: AppTheme.primary, fontWeight: FontWeight.w500, ), ), ], ), ), ); }).toList(), ), ); } } // ── Notificaciones ──────────────────────────────────────────────────────────── class _NotificacionesCard extends StatelessWidget { final UIHouseModel casa; final ValueChanged onAlertaCercanaChanged; final ValueChanged onAlertaMediaChanged; final ValueChanged onRecordatorioChanged; const _NotificacionesCard({ required this.casa, required this.onAlertaCercanaChanged, required this.onAlertaMediaChanged, required this.onRecordatorioChanged, }); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: AppTheme.surface, borderRadius: BorderRadius.circular(AppTheme.radiusLg), border: Border.all(color: AppTheme.border, width: 0.5), boxShadow: AppTheme.softShadow, ), child: Column( children: [ AppLabeledSwitch( label: 'Alerta cuando el camión esté cerca', value: casa.alertaCercana, onChanged: onAlertaCercanaChanged, ), const Divider(height: 1, color: AppTheme.borderLight), AppLabeledSwitch( label: 'Alerta a distancia media', value: casa.alertaMedia, onChanged: onAlertaMediaChanged, ), const Divider(height: 1, color: AppTheme.borderLight), AppLabeledSwitch( label: 'Recordatorio diario del horario', value: casa.recordatorioDiario, onChanged: onRecordatorioChanged, ), ], ), ); } } // ── Horario ─────────────────────────────────────────────────────────────────── class _HorarioCard extends StatelessWidget { final List<_HorarioDia> _dias = const [ _HorarioDia(dia: 'Lunes', hora: '8:00 – 10:00 a.m.', activo: true), _HorarioDia(dia: 'Martes', hora: '8:00 – 10:00 a.m.', activo: true), _HorarioDia(dia: 'Miércoles', hora: 'Sin servicio', activo: false), _HorarioDia(dia: 'Jueves', hora: '8:00 – 10:00 a.m.', activo: true), _HorarioDia(dia: 'Viernes', hora: '8:00 – 10:00 a.m.', activo: true), _HorarioDia(dia: 'Sábado', hora: '9:00 – 11:00 a.m.', activo: true), _HorarioDia(dia: 'Domingo', hora: 'Sin servicio', activo: false), ]; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppTheme.surface, borderRadius: BorderRadius.circular(AppTheme.radiusLg), border: Border.all(color: AppTheme.border, width: 0.5), boxShadow: AppTheme.softShadow, ), child: Column( children: _dias.map((d) { return Padding( padding: const EdgeInsets.symmetric(vertical: 7), child: Row( children: [ Text( d.dia, style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: d.activo ? AppTheme.textPrimary : AppTheme.textSecondary, ), ), const Spacer(), Text( d.hora, style: TextStyle( fontSize: 13, color: d.activo ? AppTheme.primary : AppTheme.textSecondary, ), ), ], ), ); }).toList(), ), ); } } class _HorarioDia { final String dia; final String hora; final bool activo; const _HorarioDia({ required this.dia, required this.hora, required this.activo, }); } // ── Sheet editar dirección ──────────────────────────────────────────────────── class _EditarDireccionSheet extends StatelessWidget { final UIHouseModel casa; const _EditarDireccionSheet({required this.casa}); @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.only( left: 24, right: 24, top: 24, bottom: MediaQuery.of(context).viewInsets.bottom + 24, ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Center( child: Container( width: 36, height: 4, decoration: BoxDecoration( color: AppTheme.border, borderRadius: BorderRadius.circular(4), ), ), ), const SizedBox(height: 20), const Text( 'Editar dirección', style: TextStyle( fontSize: 17, fontWeight: FontWeight.w700, color: AppTheme.textPrimary, ), ), const SizedBox(height: 20), AppFormField(label: 'Calle y número', initialValue: casa.calle), const SizedBox(height: 14), AppFormField(label: 'Colonia', initialValue: casa.colonia), const SizedBox(height: 24), SizedBox( width: double.infinity, height: 50, child: ElevatedButton( onPressed: () => Navigator.pop(context), child: const Text('Guardar cambios'), ), ), ], ), ); } }