import 'package:flutter/foundation.dart'; import 'road_alert_models.dart'; @immutable class SwarmReport { final String id; final RoadAlertCategory category; final String? subtype; 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; final String? source; final RoadAlertStatus status; const SwarmReport({ required this.id, required this.category, this.subtype, 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, this.source, this.status = RoadAlertStatus.active, }); 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); bool get isCommunityConfirmed => confirmations >= 2; Map toJson() => { 'id': id, 'category': category.name, if (subtype != null) 'subtype': subtype, '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, if (source != null) 'source': source, 'status': status.name, }; 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))); final confirmations = (json['confirmations'] as num?)?.toInt() ?? 1; final dismissals = (json['dismissals'] as num?)?.toInt() ?? 0; final rawStatus = json['status']?.toString(); final status = RoadAlertStatus.values.firstWhere( (s) => s.name == rawStatus, orElse: () => confirmations >= 2 ? RoadAlertStatus.confirmed : RoadAlertStatus.active, ); return SwarmReport( id: json['id']?.toString() ?? '', category: category, subtype: json['subtype']?.toString(), 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: confirmations, dismissals: dismissals, groupRideId: json['groupRideId']?.toString(), source: json['source']?.toString(), status: status, ); } RoadAlert toRoadAlert({double? distanceFromUser}) { final isHighSeverity = category == RoadAlertCategory.police || category == RoadAlertCategory.accident || category == RoadAlertCategory.weatherHazard; final sourceName = source ?? (groupRideId != null ? 'Pack Rider Report' : 'MotorSignal Swarm'); return RoadAlert( id: id, providerId: 'swarm_alerts', category: category, subtype: subtype, latitude: latitude, longitude: longitude, heading: heading, roadName: roadName, description: description, reportedAt: reportedAt, expiresAt: expiresAt, lastUpdated: DateTime.now(), confidence: confidence, severity: isHighSeverity ? RoadAlertSeverity.high : RoadAlertSeverity.medium, source: sourceName, sourceType: RoadAlertSourceType.userReported, status: status, confirmations: confirmations, dismissals: dismissals, reporterUid: reporterUid, distanceFromUser: distanceFromUser, ); } SwarmReport copyWith({ String? id, RoadAlertCategory? category, String? subtype, double? latitude, double? longitude, double? heading, String? roadName, String? description, String? reporterUid, DateTime? reportedAt, DateTime? expiresAt, int? confirmations, int? dismissals, String? groupRideId, String? source, RoadAlertStatus? status, }) { return SwarmReport( id: id ?? this.id, category: category ?? this.category, subtype: subtype ?? this.subtype, 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, source: source ?? this.source, status: status ?? this.status, ); } }