import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/widgets/hybrid_static_rec_badge.dart'; import '../../rides/ride_state_machine.dart'; import '../../../models/recording_health_status.dart'; /// Authoritative ride status badge/chip. /// /// Subscribes strictly to /// [rideStateMachineProvider.select((s) => (s.state, s.healthStatus))] - the /// narrowest selection that can render a truthful status claim. RideState decides /// which lifecycle branch applies; the RID-3C-1 health status decides the claim. /// Both fields change only on real lifecycle or health transitions, so this still /// never rebuilds on 20 Hz telemetry ticks. class RideStatusChip extends ConsumerWidget { const RideStatusChip({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { // RID-3C-2: RideState decides WHICH lifecycle branch applies; the RID-3C-1 // health status decides the CLAIM inside an active ride. This widget performs // no health calculation of its own - recordingActive is already the product of // canReportRecording plus the frozen evidence thresholds, so RECORDING can // never be claimed from RideState or service presence alone. // // The select stays narrow (state + healthStatus only), so the chip still does // NOT rebuild on 20 Hz telemetry ticks. healthStatus changes only on real // warm-up / degradation / recovery / finalization transitions. final (state, health) = ref.watch( rideStateMachineProvider.select((s) => (s.state, s.healthStatus)), ); final isActiveRide = state == RideState.riding; final isPaused = state == RideState.paused; final claimsRecording = isActiveRide && health == RecordingHealthStatus.recordingActive; final theme = Theme.of(context); final isDark = theme.brightness == Brightness.dark; final Color? customColor; final String label; if (isActiveRide) { switch (health) { case RecordingHealthStatus.recordingActive: // Healthy active recording: uses canonical recStaticGreen (#10B981) customColor = null; label = 'RECORDING'; break; case RecordingHealthStatus.recordingArmed: case RecordingHealthStatus.recordingStarting: // Ride exists but required evidence has not completed yet. Neutral / // informational; the Stop action remains available elsewhere. customColor = isDark ? AppColors.cyberCyan : AppColors.lightPrimary; label = 'WARMING UP'; break; case RecordingHealthStatus.recordingFinalizing: // RID-3B-1 finalization has priority and is never shown as RECORDING. customColor = isDark ? AppColors.cyberCyan : AppColors.lightPrimary; label = 'FINALIZING'; break; case RecordingHealthStatus.recordingDegraded: case RecordingHealthStatus.recordingFailed: // Warning, NOT danger: DEGRADED means the ride is still active and still // stoppable - not ended, discarded, failed or service-stopped. customColor = AppColors.warningAmber; label = 'DEGRADED'; break; case RecordingHealthStatus.recordingComplete: // Unreachable alongside an active ride; must not present as recording. customColor = null; label = 'STANDBY'; break; } } else if (isPaused) { customColor = null; label = 'PAUSED'; } else if (state == RideState.ready) { customColor = isDark ? AppColors.cyberCyan : AppColors.lightPrimary; label = 'READY'; } else { customColor = null; label = 'STANDBY'; } return HybridStaticRecBadge( isRecording: claimsRecording, isPaused: isPaused, customLabel: label, customColor: customColor, ); } }