using System; using System.Windows; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using HC_APTBS.Services; namespace HC_APTBS.ViewModels.Dialogs { /// /// ViewModel for the Dashboard "Connect & Auto Test" snackbar. /// Receives transitions and failure reasons from the /// orchestrator, exposes snackbar-friendly bindings (progress, phase text, success), /// and forwards the Cancel click to the orchestrator's CTS. /// Modelled on . /// public sealed partial class AutoTestProgressViewModel : ObservableObject, IDisposable { private readonly IAutoTestOrchestrator _orchestrator; private readonly ILocalizationService _loc; /// Creates the ViewModel and subscribes to orchestrator events. public AutoTestProgressViewModel(IAutoTestOrchestrator orchestrator, ILocalizationService loc) { _orchestrator = orchestrator; _loc = loc; _typeLabel = _loc.GetString("AutoTest.TypeLabel"); _phaseText = _loc.GetString("AutoTest.State.Preflight"); _isCancellable = true; _orchestrator.StateChanged += OnStateChanged; _orchestrator.Failed += OnFailed; } // ── Observable properties ──────────────────────────────────────────────── /// Progress percentage (0–100). Meaningful during Unlocking and test Running phases. [ObservableProperty] private int _progress; /// Leading label (localised "Auto Test"). [ObservableProperty] private string _typeLabel; /// Current phase description shown in the snackbar. [ObservableProperty] private string _phaseText; /// Terminal result text — populated on Completed/Aborted. [ObservableProperty] private string _resultText = string.Empty; /// True once the sequence reaches Completed or Aborted. [NotifyCanExecuteChangedFor(nameof(CloseCommand))] [ObservableProperty] private bool _isComplete; /// True while the Cancel button should be enabled (all non-terminal states). [NotifyCanExecuteChangedFor(nameof(CancelCommand))] [ObservableProperty] private bool _isCancellable; /// Tri-state: null while running, true = success, false = failure. [ObservableProperty] private bool? _isSuccess; // ── Commands ───────────────────────────────────────────────────────────── /// Cancels the orchestrator's current sequence. [RelayCommand(CanExecute = nameof(IsCancellable))] private void Cancel() => _orchestrator.Cancel(); /// Closes the snackbar (emits ). [RelayCommand(CanExecute = nameof(IsComplete))] private void Close() => RequestClose?.Invoke(); // ── Events ─────────────────────────────────────────────────────────────── /// Raised when the snackbar should close itself (after success / user dismiss). public event Action? RequestClose; // ── Orchestrator event handlers ────────────────────────────────────────── private void OnStateChanged(AutoTestState state, string? detail) { Application.Current?.Dispatcher?.Invoke(() => { switch (state) { case AutoTestState.Preflight: PhaseText = _loc.GetString("AutoTest.State.Preflight"); break; case AutoTestState.ConnectingKLine: PhaseText = _loc.GetString("AutoTest.State.Connecting"); break; case AutoTestState.ReadingPump: PhaseText = _loc.GetString("AutoTest.State.Reading"); if (!string.IsNullOrEmpty(detail) && int.TryParse(detail, out int pct)) Progress = pct; break; case AutoTestState.Unlocking: PhaseText = string.IsNullOrEmpty(detail) ? _loc.GetString("AutoTest.State.Unlocking") : string.Format(_loc.GetString("AutoTest.State.UnlockingWithDetail"), detail); break; case AutoTestState.TurningOnBench: PhaseText = _loc.GetString("AutoTest.State.BenchOn"); break; case AutoTestState.StartingOilPump: PhaseText = _loc.GetString("AutoTest.State.OilPump"); break; case AutoTestState.StartingTest: PhaseText = _loc.GetString("AutoTest.State.TestStart"); break; case AutoTestState.Running: PhaseText = string.IsNullOrEmpty(detail) ? _loc.GetString("AutoTest.State.Running") : string.Format(_loc.GetString("AutoTest.State.RunningWithPhase"), detail); break; case AutoTestState.Completed: PhaseText = _loc.GetString("AutoTest.State.Completed"); ResultText = PhaseText; Progress = 100; IsComplete = true; IsCancellable = false; IsSuccess = true; break; case AutoTestState.Aborted: // Detail populated by OnFailed; still terminal. IsComplete = true; IsCancellable = false; IsSuccess = false; break; } }); } private void OnFailed(AutoTestFailureReason reason, string message) { Application.Current?.Dispatcher?.Invoke(() => { string key = "AutoTest.Failure." + reason; string localised = _loc.GetString(key); if (string.IsNullOrEmpty(localised) || localised == key) localised = reason.ToString(); ResultText = string.IsNullOrEmpty(message) ? localised : $"{localised}: {message}"; PhaseText = string.Format(_loc.GetString("AutoTest.State.Aborted"), ResultText); IsComplete = true; IsCancellable = false; IsSuccess = false; }); } // ── IDisposable ────────────────────────────────────────────────────────── /// Unsubscribes from orchestrator events. public void Dispose() { _orchestrator.StateChanged -= OnStateChanged; _orchestrator.Failed -= OnFailed; } } }