import 'package:flutter/foundation.dart'; import 'road_alert_models.dart'; @immutable class SwarmReport { final String id; final RoadAlertCategory category; final double latitude; final double longitude; final double? heading; final String? roadName; final String description; final String reporterUid; final DateTime reportedAt; final DateTime expiresAt; final int confirmations; final int dismissals; final String? groupRideId; const SwarmReport({ required this.id, required this.category, required this.latitude, required this.longitude, this.heading, this.roadName, required this.description, required this.reporterUid, required this.reportedAt, required this.expiresAt, this.confirmations = 1, this.dismissals = 0, this.groupRideId, }); double get confidence { final total = confirmations + dismissals; if (total == 0) return 0.8; return (confirmations / total).clamp(0.1, 1.0); } bool get isExpired => DateTime.now().isAfter(expiresAt); Map toJson() => { 'id': id, 'category': category.name, 'latitude': latitude, 'longitude': longitude, if (heading != null) 'heading': heading, if (roadName != null) 'roadName': roadName, 'description': description, 'reporterUid': reporterUid, 'reportedAt': reportedAt.millisecondsSinceEpoch, 'expiresAt': expiresAt.millisecondsSinceEpoch, 'confirmations': confirmations, 'dismissals': dismissals, if (groupRideId != null) 'groupRideId': groupRideId, }; factory SwarmReport.fromJson(Map json) { final catName = json['category']?.toString() ?? 'other'; final category = RoadAlertCategory.values.firstWhere( (c) => c.name == catName, orElse: () => RoadAlertCategory.other, ); final rawReportedAt = json['reportedAt']; final reportedAt = rawReportedAt is num ? DateTime.fromMillisecondsSinceEpoch(rawReportedAt.toInt()) : (DateTime.tryParse(rawReportedAt?.toString() ?? '') ?? DateTime.now()); final rawExpiresAt = json['expiresAt']; final expiresAt = rawExpiresAt is num ? DateTime.fromMillisecondsSinceEpoch(rawExpiresAt.toInt()) : (DateTime.tryParse(rawExpiresAt?.toString() ?? '') ?? reportedAt.add(const Duration(hours: 1))); return SwarmReport( id: json['id']?.toString() ?? '', category: category, latitude: (json['latitude'] as num).toDouble(), longitude: (json['longitude'] as num).toDouble(), heading: json['heading'] != null ? (json['heading'] as num).toDouble() : null, roadName: json['roadName']?.toString(), description: json['description']?.toString() ?? 'Swarm Report', reporterUid: json['reporterUid']?.toString() ?? 'anonymous', reportedAt: reportedAt, expiresAt: expiresAt, confirmations: (json['confirmations'] as num?)?.toInt() ?? 1, dismissals: (json['dismissals'] as num?)?.toInt() ?? 0, groupRideId: json['groupRideId']?.toString(), ); } RoadAlert toRoadAlert({double? distanceFromUser}) { final isPoliceOrAccident = category == RoadAlertCategory.police || category == RoadAlertCategory.accident; final sourceName = groupRideId != null ? 'Pack Rider Report' : 'MotorSignal Swarm'; return RoadAlert( id: id, providerId: 'swarm_alerts', category: category, latitude: latitude, longitude: longitude, heading: heading, roadName: roadName, description: description, reportedAt: reportedAt, expiresAt: expiresAt, lastUpdated: DateTime.now(), confidence: confidence, severity: isPoliceOrAccident ? RoadAlertSeverity.high : RoadAlertSeverity.medium, source: sourceName, sourceType: RoadAlertSourceType.userReported, distanceFromUser: distanceFromUser, ); } SwarmReport copyWith({ String? id, RoadAlertCategory? category, double? latitude, double? longitude, double? heading, String? roadName, String? description, String? reporterUid, DateTime? reportedAt, DateTime? expiresAt, int? confirmations, int? dismissals, String? groupRideId, }) { return SwarmReport( id: id ?? this.id, category: category ?? this.category, latitude: latitude ?? this.latitude, longitude: longitude ?? this.longitude, heading: heading ?? this.heading, roadName: roadName ?? this.roadName, description: description ?? this.description, reporterUid: reporterUid ?? this.reporterUid, reportedAt: reportedAt ?? this.reportedAt, expiresAt: expiresAt ?? this.expiresAt, confirmations: confirmations ?? this.confirmations, dismissals: dismissals ?? this.dismissals, groupRideId: groupRideId ?? this.groupRideId, ); } }