using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using LehrerApp.Core.Interfaces; using LehrerApp.Core.Models; using LehrerApp.Core.Services; using LehrerApp.Data; using LehrerApp.Desktop.Services; using LehrerApp.Desktop.ViewModels.Planning; using LehrerApp.Sync; using LehrerApp.Sync.Crypto; using System.Collections.ObjectModel; using System.Globalization; using System.Security.Cryptography; using System.Text.Json; using System.Text.Json.Serialization; namespace LehrerApp.Desktop.ViewModels.Settings; public partial class SettingsViewModel { // ── WebUntis-iCal-Abgleich (Nutzer-Feedback) ────────────────────────────── [ObservableProperty] private bool _untisEnabled; [ObservableProperty] private bool _untisIsConfigured; [ObservableProperty] private string _untisIcalUrlInput = ""; [ObservableProperty] private string _untisUrlError = ""; [ObservableProperty] private string _untisStatusDisplay = ""; [ObservableProperty] private bool _untisFetchBusy; [ObservableProperty] private bool _untisApiIsConfigured; [ObservableProperty] private string _untisSchool = ""; [ObservableProperty] private string _untisHost = ""; [ObservableProperty] private string _untisUsername = ""; [ObservableProperty] private string _untisPassword = ""; [ObservableProperty] private string _untisApiStatus = ""; [ObservableProperty] private bool _untisApiBusy; [ObservableProperty] private string? _untisHomeroomClassName; public string UntisHomeroomClassDisplay => UntisHomeroomClassName is { Length: > 0 } name ? $"Ausgewählt: {name}" : "Keine Klasse ausgewählt."; partial void OnUntisHomeroomClassNameChanged(string? value) => OnPropertyChanged(nameof(UntisHomeroomClassDisplay)); public Func? OnReviewUntisMapping { get; set; } /// Vom Code-Behind gesetzt: öffnet den WebUntis-Klassenauswahldialog, liefert null bei Abbruch. public Func>? OnPickHomeroomClass { get; set; } // ── WebUntis-iCal-Abgleich: Laden / Speichern / Entfernen / Jetzt abrufen ──── // // Wie bei Sync deckt ein Neustart das Registrieren von UntisSyncService ab // (AppBootstrapper registriert es nur einmalig beim Start, wenn URL+Enabled vorliegen). private void LoadUntisSettings() { UntisEnabled = _untisSettings.Enabled; UntisIsConfigured = _untisSettings.IsConfigured; UntisStatusDisplay = _untisSettings.LastSyncAt is { } at ? $"Letzter Abgleich: {at.ToLocalTime():dd.MM.yyyy HH:mm} — {_untisSettings.LastSyncStatus}" : "Noch kein Abgleich durchgeführt."; UntisApiIsConfigured = _untisSettings.ApiIsConfigured; if (_untisSettings.GetApiCredentials() is { } credentials) { UntisSchool = credentials.School; UntisHost = credentials.Host; UntisUsername = credentials.Username; UntisApiStatus = $"API-Zugang für {credentials.Username} ist lokal verschlüsselt gespeichert."; } UntisHomeroomClassName = _untisSettings.HomeroomClassName; } [RelayCommand] private async Task UntisSaveApi() { UntisApiStatus = ""; if (_untisIntegration is null) { UntisApiStatus = "Die WebUntis-Integration ist nicht verfügbar."; return; } if (string.IsNullOrWhiteSpace(UntisSchool) || string.IsNullOrWhiteSpace(UntisUsername) || string.IsNullOrWhiteSpace(UntisPassword)) { UntisApiStatus = "Schule, Benutzername und Passwort sind erforderlich."; return; } UntisApiBusy = true; try { var credentials = new WebUntisCredentials(UntisSchool.Trim(), UntisHost.Trim(), UntisUsername.Trim(), UntisPassword); await _untisIntegration.ConnectAsync(credentials); _untisSettings.SetApiCredentials(credentials); UntisPassword = ""; UntisApiIsConfigured = true; UntisApiStatus = "Anmeldung erfolgreich. Die WebUntis-Session bleibt bei Nutzung bis zu 10 Minuten offen."; } catch (WebUntisIntegrationException ex) { UntisApiStatus = ex.Message; } finally { UntisApiBusy = false; } } [RelayCommand] private async Task UntisRemoveApi() { UntisApiBusy = true; try { if (_untisIntegration is not null) await _untisIntegration.DisconnectAsync(); } catch (WebUntisIntegrationException) { /* lokale Zugangsdaten trotzdem sicher entfernen */ } finally { _untisSettings.ClearApiCredentials(); UntisPassword = ""; UntisApiIsConfigured = false; UntisApiStatus = "WebUntis-API-Zugang entfernt."; UntisApiBusy = false; } } [RelayCommand] private void UntisSaveUrl() { UntisUrlError = ""; if (string.IsNullOrWhiteSpace(UntisIcalUrlInput)) { UntisUrlError = "iCal-URL erforderlich."; return; } if (!Uri.TryCreate(UntisIcalUrlInput, UriKind.Absolute, out _)) { UntisUrlError = "Ungültige URL."; return; } _untisSettings.SetIcalUrl(UntisIcalUrlInput.Trim()); _untisSettings.SetEnabled(true); UntisIcalUrlInput = ""; AppBootstrapper.RestartApplication(); } [RelayCommand] private void UntisRemove() { _untisSettings.ClearIcalUrl(); LoadUntisSettings(); AppBootstrapper.RestartApplication(); } [RelayCommand] private async Task UntisFetchNow() { if (_untisSync is null) { UntisStatusDisplay = "Abgleich nicht aktiv — App neu starten."; return; } UntisFetchBusy = true; try { await _untisSync.PollAsync(); LoadUntisSettings(); } finally { UntisFetchBusy = false; } } [RelayCommand] private async Task UntisReviewMapping() { if (OnReviewUntisMapping is not null) await OnReviewUntisMapping(); } [RelayCommand] private async Task UntisPickHomeroomClass() { if (OnPickHomeroomClass is null) return; var result = await OnPickHomeroomClass(); if (result is null) return; _untisSettings.SetHomeroomClass(result.Value.UntisId, result.Value.Name); LoadUntisSettings(); } [RelayCommand] private void UntisClearHomeroomClass() { _untisSettings.SetHomeroomClass(null, null); LoadUntisSettings(); } }