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/dashboard/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 dashboardEligibleCount; final int audioCandidateCount; final bool activeRouteRelevance; final String locationFreshnessState; // 'fresh', 'stale', 'unavailable' final double? lastLocationAgeSeconds; final double? locationAccuracyMeters; final int directionRejectedCount; final int distanceRejectedCount; final int staleGpsRejectedCount; final String? lastDashboardAlertPresented; final String? lastAudioWarningIssued; 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.dashboardEligibleCount = 0, this.audioCandidateCount = 0, this.activeRouteRelevance = false, this.locationFreshnessState = 'unknown', this.lastLocationAgeSeconds, this.locationAccuracyMeters, this.directionRejectedCount = 0, this.distanceRejectedCount = 0, this.staleGpsRejectedCount = 0, this.lastDashboardAlertPresented, this.lastAudioWarningIssued, 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? dashboardEligibleCount, int? audioCandidateCount, bool? activeRouteRelevance, String? locationFreshnessState, double? lastLocationAgeSeconds, double? locationAccuracyMeters, int? directionRejectedCount, int? distanceRejectedCount, int? staleGpsRejectedCount, String? lastDashboardAlertPresented, String? lastAudioWarningIssued, 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, dashboardEligibleCount: dashboardEligibleCount ?? this.dashboardEligibleCount, audioCandidateCount: audioCandidateCount ?? this.audioCandidateCount, activeRouteRelevance: activeRouteRelevance ?? this.activeRouteRelevance, locationFreshnessState: locationFreshnessState ?? this.locationFreshnessState, lastLocationAgeSeconds: lastLocationAgeSeconds ?? this.lastLocationAgeSeconds, locationAccuracyMeters: locationAccuracyMeters ?? this.locationAccuracyMeters, directionRejectedCount: directionRejectedCount ?? this.directionRejectedCount, distanceRejectedCount: distanceRejectedCount ?? this.distanceRejectedCount, staleGpsRejectedCount: staleGpsRejectedCount ?? this.staleGpsRejectedCount, lastDashboardAlertPresented: lastDashboardAlertPresented ?? this.lastDashboardAlertPresented, lastAudioWarningIssued: lastAudioWarningIssued ?? this.lastAudioWarningIssued, lastError: lastError ?? this.lastError, ); } }