// ── Usuario ────────────────────────────────────────────────────────────────── class UserModel { final String id; final String nombre; final String apellido; final String email; final String telefono; final List casas; const UserModel({ required this.id, required this.nombre, required this.apellido, required this.email, required this.telefono, this.casas = const [], }); String get nombreCompleto => '$nombre $apellido'; String get iniciales => '${nombre.isNotEmpty ? nombre[0] : ''}${apellido.isNotEmpty ? apellido[0] : ''}' .toUpperCase(); UserModel copyWith({ String? nombre, String? apellido, String? email, String? telefono, List? casas, }) { return UserModel( id: id, nombre: nombre ?? this.nombre, apellido: apellido ?? this.apellido, email: email ?? this.email, telefono: telefono ?? this.telefono, casas: casas ?? this.casas, ); } } // ── Casa ───────────────────────────────────────────────────────────────────── class HouseModel { final String id; final String alias; final String calle; final String colonia; final String codigoPostal; final double latitud; final double longitud; final int radioAlertaMetros; final bool alertaCercana; final bool alertaMedia; final bool recordatorioDiario; final bool activa; const HouseModel({ required this.id, this.alias = 'Casa principal', required this.calle, required this.colonia, required this.codigoPostal, required this.latitud, required this.longitud, this.radioAlertaMetros = 200, this.alertaCercana = true, this.alertaMedia = false, this.recordatorioDiario = true, this.activa = true, }); String get direccionCompleta => '$calle, Col. $colonia, C.P. $codigoPostal'; HouseModel copyWith({ String? alias, String? calle, String? colonia, String? codigoPostal, double? latitud, double? longitud, int? radioAlertaMetros, bool? alertaCercana, bool? alertaMedia, bool? recordatorioDiario, bool? activa, }) { return HouseModel( id: id, alias: alias ?? this.alias, calle: calle ?? this.calle, colonia: colonia ?? this.colonia, codigoPostal: codigoPostal ?? this.codigoPostal, latitud: latitud ?? this.latitud, longitud: longitud ?? this.longitud, radioAlertaMetros: radioAlertaMetros ?? this.radioAlertaMetros, alertaCercana: alertaCercana ?? this.alertaCercana, alertaMedia: alertaMedia ?? this.alertaMedia, recordatorioDiario: recordatorioDiario ?? this.recordatorioDiario, activa: activa ?? this.activa, ); } } // ── Camión ─────────────────────────────────────────────────────────────────── class TruckLocation { final String id; final String ruta; final double latitud; final double longitud; final DateTime ultimaActualizacion; final bool enServicio; const TruckLocation({ required this.id, required this.ruta, required this.latitud, required this.longitud, required this.ultimaActualizacion, this.enServicio = true, }); String get tiempoActualizacion { final diff = DateTime.now().difference(ultimaActualizacion); if (diff.inSeconds < 60) return 'Hace ${diff.inSeconds} s'; if (diff.inMinutes < 60) return 'Hace ${diff.inMinutes} min'; return 'Hace ${diff.inHours} h'; } } // ── Alerta ─────────────────────────────────────────────────────────────────── enum TipoAlerta { cercana, media, recordatorio } class AlertaModel { final String id; final TipoAlerta tipo; final double distanciaMetros; final DateTime fecha; final String direccionCasa; final bool leida; const AlertaModel({ required this.id, required this.tipo, required this.distanciaMetros, required this.fecha, required this.direccionCasa, this.leida = false, }); String get distanciaTexto { if (distanciaMetros < 1000) { return '${distanciaMetros.toStringAsFixed(0)} m'; } return '${(distanciaMetros / 1000).toStringAsFixed(1)} km'; } String get tiempoEstimadoTexto { // ~5 km/h velocidad promedio del camión final segundos = (distanciaMetros / (5000 / 3600)).round(); if (segundos < 60) return 'Menos de 1 min'; final minutos = (segundos / 60).ceil(); return '~$minutos min'; } String get fechaFormateada { final ahora = DateTime.now(); final hoy = DateTime(ahora.year, ahora.month, ahora.day); final fechaDia = DateTime(fecha.year, fecha.month, fecha.day); if (fechaDia == hoy) { return 'Hoy, ${_formatHora(fecha)}'; } final ayer = hoy.subtract(const Duration(days: 1)); if (fechaDia == ayer) return 'Ayer, ${_formatHora(fecha)}'; const dias = ['Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb', 'Dom']; const meses = ['ene','feb','mar','abr','may','jun','jul','ago','sep','oct','nov','dic']; return '${dias[fecha.weekday - 1]} ${fecha.day} ${meses[fecha.month - 1]}, ${_formatHora(fecha)}'; } String get etiquetaFecha { final ahora = DateTime.now(); final hoy = DateTime(ahora.year, ahora.month, ahora.day); final fechaDia = DateTime(fecha.year, fecha.month, fecha.day); if (fechaDia == hoy) return 'Hoy'; const dias = ['Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb', 'Dom']; return dias[fecha.weekday - 1]; } String _formatHora(DateTime dt) { final h = dt.hour > 12 ? dt.hour - 12 : dt.hour == 0 ? 12 : dt.hour; final m = dt.minute.toString().padLeft(2, '0'); final ampm = dt.hour >= 12 ? 'p.m.' : 'a.m.'; return '$h:$m $ampm'; } }