using System.Collections.ObjectModel; using System.Windows; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.Extensions.Logging; using Serilog; using SharepointToolbox.Core.Models; using SharepointToolbox.Infrastructure.Persistence; using SharepointToolbox.Services; namespace SharepointToolbox.ViewModels.Tabs; public partial class TemplatesViewModel : FeatureViewModelBase { private readonly ITemplateService _templateService; private readonly TemplateRepository _templateRepo; private readonly ISessionManager _sessionManager; private readonly ILogger _logger; private TenantProfile? _currentProfile; // Template list private ObservableCollection _templates = new(); public ObservableCollection Templates { get => _templates; private set { _templates = value; OnPropertyChanged(); } } [ObservableProperty] private SiteTemplate? _selectedTemplate; // Capture options [ObservableProperty] private string _captureSiteUrl = string.Empty; [ObservableProperty] private string _templateName = string.Empty; [ObservableProperty] private bool _captureLibraries = true; [ObservableProperty] private bool _captureFolders = true; [ObservableProperty] private bool _capturePermissions = true; [ObservableProperty] private bool _captureLogo = true; [ObservableProperty] private bool _captureSettings = true; // Apply options [ObservableProperty] private string _newSiteTitle = string.Empty; [ObservableProperty] private string _newSiteAlias = string.Empty; public IAsyncRelayCommand CaptureCommand { get; } public IAsyncRelayCommand ApplyCommand { get; } public IAsyncRelayCommand RenameCommand { get; } public IAsyncRelayCommand DeleteCommand { get; } public IAsyncRelayCommand RefreshCommand { get; } public TenantProfile? CurrentProfile => _currentProfile; // Factory for rename dialog — set by View code-behind public Func? RenameDialogFactory { get; set; } public TemplatesViewModel( ITemplateService templateService, TemplateRepository templateRepo, ISessionManager sessionManager, ILogger logger) : base(logger) { _templateService = templateService; _templateRepo = templateRepo; _sessionManager = sessionManager; _logger = logger; CaptureCommand = new AsyncRelayCommand(CaptureAsync, () => !IsRunning); ApplyCommand = new AsyncRelayCommand(ApplyAsync, () => !IsRunning && SelectedTemplate != null); RenameCommand = new AsyncRelayCommand(RenameAsync, () => SelectedTemplate != null); DeleteCommand = new AsyncRelayCommand(DeleteAsync, () => SelectedTemplate != null); RefreshCommand = new AsyncRelayCommand(RefreshListAsync); } protected override async Task RunOperationAsync(CancellationToken ct, IProgress progress) { // Not used directly — Capture and Apply have their own async commands await Task.CompletedTask; } private async Task CaptureAsync() { if (_currentProfile == null) throw new InvalidOperationException("No tenant connected."); if (string.IsNullOrWhiteSpace(CaptureSiteUrl)) throw new InvalidOperationException("Site URL is required."); if (string.IsNullOrWhiteSpace(TemplateName)) throw new InvalidOperationException("Template name is required."); try { IsRunning = true; StatusMessage = "Capturing template..."; var profile = new TenantProfile { Name = _currentProfile.Name, TenantUrl = CaptureSiteUrl, ClientId = _currentProfile.ClientId, }; var ctx = await _sessionManager.GetOrCreateContextAsync(profile, CancellationToken.None); var options = new SiteTemplateOptions { CaptureLibraries = CaptureLibraries, CaptureFolders = CaptureFolders, CapturePermissionGroups = CapturePermissions, CaptureLogo = CaptureLogo, CaptureSettings = CaptureSettings, }; var progress = new Progress(p => StatusMessage = p.Message); var template = await _templateService.CaptureTemplateAsync(ctx, options, progress, CancellationToken.None); template.Name = TemplateName; await _templateRepo.SaveAsync(template); Log.Information("Template captured: {Name} from {Url}", template.Name, CaptureSiteUrl); await RefreshListAsync(); StatusMessage = $"Template captured successfully."; } catch (Exception ex) { StatusMessage = $"Capture failed: {ex.Message}"; Log.Error(ex, "Template capture failed"); } finally { IsRunning = false; } } private async Task ApplyAsync() { if (_currentProfile == null || SelectedTemplate == null) return; if (string.IsNullOrWhiteSpace(NewSiteTitle)) throw new InvalidOperationException("New site title is required."); if (string.IsNullOrWhiteSpace(NewSiteAlias)) throw new InvalidOperationException("New site alias is required."); try { IsRunning = true; StatusMessage = $"Applying template..."; var ctx = await _sessionManager.GetOrCreateContextAsync(_currentProfile, CancellationToken.None); var progress = new Progress(p => StatusMessage = p.Message); var siteUrl = await _templateService.ApplyTemplateAsync( ctx, SelectedTemplate, NewSiteTitle, NewSiteAlias, progress, CancellationToken.None); StatusMessage = $"Template applied. Site created at: {siteUrl}"; Log.Information("Template applied. New site: {Url}", siteUrl); } catch (Exception ex) { StatusMessage = $"Apply failed: {ex.Message}"; Log.Error(ex, "Template apply failed"); } finally { IsRunning = false; } } private async Task RenameAsync() { if (SelectedTemplate == null) return; if (RenameDialogFactory != null) { var newName = RenameDialogFactory(SelectedTemplate.Name); if (!string.IsNullOrWhiteSpace(newName)) { await _templateRepo.RenameAsync(SelectedTemplate.Id, newName); await RefreshListAsync(); Log.Information("Template renamed."); } } } private async Task DeleteAsync() { if (SelectedTemplate == null) return; await _templateRepo.DeleteAsync(SelectedTemplate.Id); await RefreshListAsync(); Log.Information("Template deleted."); } private async Task RefreshListAsync() { var templates = await _templateRepo.GetAllAsync(); await Application.Current.Dispatcher.InvokeAsync(() => { Templates = new ObservableCollection(templates); }); } protected override void OnTenantSwitched(TenantProfile profile) { _currentProfile = profile; CaptureSiteUrl = string.Empty; TemplateName = string.Empty; NewSiteTitle = string.Empty; NewSiteAlias = string.Empty; StatusMessage = string.Empty; _ = RefreshListAsync(); } partial void OnSelectedTemplateChanged(SiteTemplate? value) { ApplyCommand.NotifyCanExecuteChanged(); RenameCommand.NotifyCanExecuteChanged(); DeleteCommand.NotifyCanExecuteChanged(); } }