import 'road_alert_models.dart'; /// Comprehensive diagnostics telemetry for the road alert pipeline. /// Tracks upstream provider status, ingestion rates, stage-by-stage rejection reasons, /// and map/audio dispatch metrics without leaking credentials or sensitive user data. class RoadAlertDiagnostics { final RoadAlertProviderStatus providerStatus; final String? statusMessage; final DateTime? lastFetchTime; final DateTime? lastSuccessfulFetchTime; final String? lastRequestedArea; final double? lastRequestedRadiusMeters; final bool isLive; final int rawReportsCount; final int normalizedAcceptedCount; final Map rejectedReasons; final int displayedOnMapCount; final int audioCandidateCount; final String? lastError; const RoadAlertDiagnostics({ this.providerStatus = RoadAlertProviderStatus.notConfigured, this.statusMessage, this.lastFetchTime, this.lastSuccessfulFetchTime, this.lastRequestedArea, this.lastRequestedRadiusMeters, this.isLive = true, this.rawReportsCount = 0, this.normalizedAcceptedCount = 0, this.rejectedReasons = const {}, this.displayedOnMapCount = 0, this.audioCandidateCount = 0, this.lastError, }); int get totalRejectedCount => rejectedReasons.values.fold(0, (sum, count) => sum + count); bool get hasQuotaError => providerStatus == RoadAlertProviderStatus.rateLimited; bool get hasAuthError => providerStatus == RoadAlertProviderStatus.authError; bool get isOperational => providerStatus == RoadAlertProviderStatus.connected; RoadAlertDiagnostics copyWith({ RoadAlertProviderStatus? providerStatus, String? statusMessage, DateTime? lastFetchTime, DateTime? lastSuccessfulFetchTime, String? lastRequestedArea, double? lastRequestedRadiusMeters, bool? isLive, int? rawReportsCount, int? normalizedAcceptedCount, Map? rejectedReasons, int? displayedOnMapCount, int? audioCandidateCount, String? lastError, }) { return RoadAlertDiagnostics( providerStatus: providerStatus ?? this.providerStatus, statusMessage: statusMessage ?? this.statusMessage, lastFetchTime: lastFetchTime ?? this.lastFetchTime, lastSuccessfulFetchTime: lastSuccessfulFetchTime ?? this.lastSuccessfulFetchTime, lastRequestedArea: lastRequestedArea ?? this.lastRequestedArea, lastRequestedRadiusMeters: lastRequestedRadiusMeters ?? this.lastRequestedRadiusMeters, isLive: isLive ?? this.isLive, rawReportsCount: rawReportsCount ?? this.rawReportsCount, normalizedAcceptedCount: normalizedAcceptedCount ?? this.normalizedAcceptedCount, rejectedReasons: rejectedReasons ?? this.rejectedReasons, displayedOnMapCount: displayedOnMapCount ?? this.displayedOnMapCount, audioCandidateCount: audioCandidateCount ?? this.audioCandidateCount, lastError: lastError ?? this.lastError, ); } }