using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using OTSSignsOrchestrator.Core.Data; using OTSSignsOrchestrator.Core.Models.Entities; using OTSSignsOrchestrator.Core.Services; using OTSSignsOrchestrator.Desktop.Services; namespace OTSSignsOrchestrator.Desktop.ViewModels; /// /// ViewModel for listing, viewing, and managing CMS instances. /// public partial class InstancesViewModel : ObservableObject { private readonly IServiceProvider _services; [ObservableProperty] private ObservableCollection _instances = new(); [ObservableProperty] private CmsInstance? _selectedInstance; [ObservableProperty] private string _statusMessage = string.Empty; [ObservableProperty] private bool _isBusy; [ObservableProperty] private string _filterText = string.Empty; [ObservableProperty] private ObservableCollection _remoteStacks = new(); [ObservableProperty] private ObservableCollection _selectedServices = new(); // Available SSH hosts for the dropdown [ObservableProperty] private ObservableCollection _availableHosts = new(); [ObservableProperty] private SshHost? _selectedSshHost; public InstancesViewModel(IServiceProvider services) { _services = services; _ = InitAsync(); } private async Task InitAsync() { await LoadHostsAsync(); await LoadInstancesAsync(); } [RelayCommand] private async Task LoadHostsAsync() { using var scope = _services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var hosts = await db.SshHosts.OrderBy(h => h.Label).ToListAsync(); AvailableHosts = new ObservableCollection(hosts); } [RelayCommand] private async Task LoadInstancesAsync() { IsBusy = true; try { using var scope = _services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var query = db.CmsInstances.Include(i => i.SshHost).AsQueryable(); if (!string.IsNullOrWhiteSpace(FilterText)) { query = query.Where(i => i.CustomerName.Contains(FilterText) || i.StackName.Contains(FilterText)); } var items = await query.OrderByDescending(i => i.CreatedAt).ToListAsync(); Instances = new ObservableCollection(items); StatusMessage = $"Loaded {items.Count} instance(s)."; } catch (Exception ex) { StatusMessage = $"Error: {ex.Message}"; } finally { IsBusy = false; } } [RelayCommand] private async Task RefreshRemoteStacksAsync() { if (SelectedSshHost == null) { StatusMessage = "Select an SSH host first."; return; } IsBusy = true; StatusMessage = $"Listing stacks on {SelectedSshHost.Label}..."; try { var dockerCli = _services.GetRequiredService(); dockerCli.SetHost(SelectedSshHost); var stacks = await dockerCli.ListStacksAsync(); RemoteStacks = new ObservableCollection(stacks); StatusMessage = $"Found {stacks.Count} stack(s) on {SelectedSshHost.Label}."; } catch (Exception ex) { StatusMessage = $"Error listing stacks: {ex.Message}"; } finally { IsBusy = false; } } [RelayCommand] private async Task InspectInstanceAsync() { if (SelectedInstance == null) return; if (SelectedSshHost == null && SelectedInstance.SshHost == null) { StatusMessage = "No SSH host associated with this instance."; return; } IsBusy = true; try { var host = SelectedInstance.SshHost ?? SelectedSshHost!; var dockerCli = _services.GetRequiredService(); dockerCli.SetHost(host); var services = await dockerCli.InspectStackServicesAsync(SelectedInstance.StackName); SelectedServices = new ObservableCollection(services); StatusMessage = $"Found {services.Count} service(s) in stack '{SelectedInstance.StackName}'."; } catch (Exception ex) { StatusMessage = $"Error inspecting: {ex.Message}"; } finally { IsBusy = false; } } [RelayCommand] private async Task DeleteInstanceAsync() { if (SelectedInstance == null) return; IsBusy = true; StatusMessage = $"Deleting {SelectedInstance.StackName}..."; try { var host = SelectedInstance.SshHost ?? SelectedSshHost; if (host == null) { StatusMessage = "No SSH host available for deletion."; return; } // Wire up SSH-based docker services var dockerCli = _services.GetRequiredService(); dockerCli.SetHost(host); var dockerSecrets = _services.GetRequiredService(); dockerSecrets.SetHost(host); using var scope = _services.CreateScope(); var instanceSvc = scope.ServiceProvider.GetRequiredService(); var result = await instanceSvc.DeleteInstanceAsync(SelectedInstance.Id); StatusMessage = result.Success ? $"Instance '{SelectedInstance.StackName}' deleted." : $"Delete failed: {result.ErrorMessage}"; await LoadInstancesAsync(); } catch (Exception ex) { StatusMessage = $"Error deleting: {ex.Message}"; } finally { IsBusy = false; } } }