import 'road_alert_models.dart'; class CommunityTrafficParser { static List parseAlertsJson( Map json, { required String providerId, required String providerName, }) { final List alerts = []; final rawAlerts = json['alerts'] ?? json['incidents'] ?? []; if (rawAlerts is! List) return alerts; for (var item in rawAlerts) { if (item is! Map) continue; final id = item['id']?.toString() ?? item['uuid']?.toString() ?? '${item['latitude'] ?? item['locationY']}_${item['longitude'] ?? item['locationX']}'; final typeStr = (item['type'] ?? item['category'] ?? item['typeGroup'] ?? '').toString().toUpperCase(); final subTypeStr = (item['subType'] ?? item['subtype'] ?? item['subCategory'] ?? '').toString().toUpperCase(); final category = mapTypeToCategory(typeStr, subTypeStr); final double? lat = _toDouble(item['latitude'] ?? item['lat'] ?? item['locationY'] ?? (item['location'] != null ? (item['location']['lat'] ?? item['location']['y']) : null)); final double? lng = _toDouble(item['longitude'] ?? item['lon'] ?? item['lng'] ?? item['locationX'] ?? (item['location'] != null ? (item['location']['lng'] ?? item['location']['x']) : null)); if (lat == null || lng == null) continue; final double? heading = _toDouble(item['heading'] ?? item['magvar'] ?? item['bearing'] ?? item['direction']); final String? street = item['street']?.toString() ?? item['road']?.toString() ?? item['locationName']?.toString(); final String description = _buildDescription(typeStr, subTypeStr, item['reportDescription'] ?? item['description']); final now = DateTime.now(); final dynamic rawTs = item['timestamp'] ?? item['pubMillis']; final int nMinutesOld = rawTs != null ? ((now.millisecondsSinceEpoch - (rawTs is num ? rawTs.toInt() : (int.tryParse(rawTs.toString()) ?? now.millisecondsSinceEpoch))) / 60000).clamp(0, 180).toInt() : 5; final reportedAt = now.subtract(Duration(minutes: nMinutesOld)); final expiresAt = now.add(const Duration(minutes: 30)); final double? confidence = _toDouble(item['confidence'] ?? item['reliability'] ?? item['nThumbsUp'] ?? item['thumbs_up']); final severity = _mapSeverity(item['severity'] ?? item['reliability'] ?? item['reportRating']); alerts.add(RoadAlert( id: id, providerId: providerId, category: category, latitude: lat, longitude: lng, heading: heading, roadName: street, description: description, reportedAt: reportedAt, expiresAt: expiresAt, lastUpdated: now, confidence: confidence != null ? (confidence > 1.0 ? confidence / 10.0 : confidence) : 0.8, severity: severity, source: providerName, sourceType: RoadAlertSourceType.userReported, )); } return alerts; } static RoadAlertCategory mapTypeToCategory(String type, String subtype) { final combined = '${type}_$subtype'; if (combined.contains('POLICE') || type == 'POLICE' || subtype.contains('POLICE_VISIBLE') || subtype.contains('POLICE_HIDDEN') || subtype.contains('POLICE_TRAP')) { return RoadAlertCategory.police; } if (combined.contains('SPEED_CAM') || combined.contains('CAMERA') || subtype.contains('SPEED_CAMERA') || subtype.contains('RED_LIGHT_CAMERA')) { return RoadAlertCategory.speedCamera; } if (combined.contains('ACCIDENT') || type == 'ACCIDENT') { return RoadAlertCategory.accident; } if (combined.contains('HAZARD') || type == 'HAZARD' || subtype.contains('DEBRIS') || subtype.contains('WEATHER')) { return RoadAlertCategory.hazard; } if (combined.contains('ROAD_CLOSED') || combined.contains('CLOSURE') || type == 'ROAD_CLOSED') { return RoadAlertCategory.roadClosure; } if (combined.contains('CONSTRUCTION') || combined.contains('ROAD_WORKS') || type == 'CONSTRUCTION') { return RoadAlertCategory.construction; } if (combined.contains('JAM') || combined.contains('TRAFFIC') || type == 'JAM') { return RoadAlertCategory.traffic; } return RoadAlertCategory.other; } static String _buildDescription(String type, String subtype, dynamic rawDesc) { if (rawDesc != null && rawDesc.toString().trim().isNotEmpty) { return rawDesc.toString(); } if (subtype.contains('POLICE_HIDDEN')) return 'Police reported hidden on shoulder'; if (subtype.contains('POLICE_VISIBLE')) return 'Police reported visible ahead'; if (type == 'POLICE') return 'Police report in area'; if (subtype.contains('SPEED_CAMERA')) return 'Speed enforcement camera reported'; if (type == 'ACCIDENT') return 'Traffic accident reported'; if (type == 'HAZARD') return 'Road hazard reported'; if (type == 'ROAD_CLOSED') return 'Road closed'; if (type == 'CONSTRUCTION') return 'Construction zone'; return 'Community traffic report'; } static RoadAlertSeverity _mapSeverity(dynamic val) { if (val == null) return RoadAlertSeverity.medium; final num n = (val is num) ? val : num.tryParse(val.toString()) ?? 5; if (n >= 8) return RoadAlertSeverity.critical; if (n >= 5) return RoadAlertSeverity.high; if (n >= 3) return RoadAlertSeverity.medium; return RoadAlertSeverity.low; } static double? _toDouble(dynamic val) { if (val == null) return null; if (val is num) return val.toDouble(); return double.tryParse(val.toString()); } }