using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using LehrerApp.Desktop.Services;
namespace LehrerApp.Desktop.ViewModels.ClassTeacher;
/// Eine Zeile der Klassenlehrer-Übersicht ("auf einen Blick", Nutzer-Feedback) - pro
/// Schüler*in ein Ampel-Symbol für "heute laut WebUntis keine gemeldete Fehlzeit" und ein Badge,
/// falls in den letzten Tagen ein Klassenbucheintrag einer anderen Lehrkraft angelegt wurde.
/// Bewusst kein Bezug zu eigenem Unterricht/eigener Anwesenheitserfassung - genau der Fall, den es
/// abzudecken gilt, ist der Tag ohne eigenen Unterricht mit der Klasse.
public sealed record ClassTeacherRosterRow(string StudentName, int? ExternKey, bool HasAbsenceToday,
string? AbsenceTooltip, bool HasRecentClassRegisterEntry)
{
public string StatusIcon => HasAbsenceToday ? "⚠" : "✓";
/// Führt die WebUntis-Schülerliste der Klasse mit den heutigen Fehlzeiten und den
/// jüngsten Klassenbucheinträgen zusammen. Zuordnung primär über ExternKey (beide
/// stammen aus WebUntis, gleiche Kennung), Fallback auf reihenfolge-unabhängigen Namensabgleich
/// () für den Klassenbuch-Bericht, der keinen ExternKey liefert
/// (siehe UntisForeignClassRegisterEventDto) — WebUntis liefert Namen dort in anderer
/// Reihenfolge als im Schülerreport.
public static IReadOnlyList Build(
IReadOnlyList students,
IReadOnlyList todayAbsences,
IReadOnlyList recentClassRegisterEntries)
{
var absenceByKey = todayAbsences
.Where(a => a.ExternKey is not null)
.GroupBy(a => a.ExternKey!.Value)
.ToDictionary(g => g.Key, g => g.First());
var absenceByName = todayAbsences
.GroupBy(a => UntisNameMatching.NameKey(a.StudentName))
.ToDictionary(g => g.Key, g => g.First());
var recentNames = recentClassRegisterEntries
.Select(e => UntisNameMatching.NameKey(e.StudentName))
.ToHashSet();
return students
.Select(s =>
{
var nameKey = UntisNameMatching.NameKey(s.DisplayName);
var absence = (s.ExternKey is { } key ? absenceByKey.GetValueOrDefault(key) : null)
?? absenceByName.GetValueOrDefault(nameKey);
return new ClassTeacherRosterRow(s.DisplayName, s.ExternKey, absence is not null,
absence is null ? null : $"{absence.TotalAbsentPeriods} Stunde(n) — {absence.StatusLabel}",
recentNames.Contains(nameKey));
})
.OrderBy(r => r.StudentName)
.ToList();
}
}
///
/// Top-Level-Bereich "Klassenlehrer" (eigener Sidebar-Eintrag, siehe MainWindowViewModel) - bewusst
/// nicht an eine LearningGroup gehängt, da man Klassenlehrer für eine ganze Klasse ist, unabhängig
/// vom eigenen Unterricht (Nutzer-Feedback zur ursprünglichen Umsetzung als Gruppen-Tab). Die
/// Klasse selbst kommt aus den WebUntis-Einstellungen ().
/// Zwei Ebenen: Übersicht (dieses ViewModel, kompaktes Roster) und Details
/// (, die bisherigen Rohdaten-Listen) - ein Klick auf
/// eine Roster-Zeile springt gefiltert in die Details.
///
public partial class ClassTeacherOverviewViewModel : ObservableObject
{
private readonly WebUntisSettingsService _settings;
private readonly WebUntisIntegrationService _untis;
private readonly UntisReportCacheService _cache;
public ClassTeacherDetailsViewModel DetailsTab { get; }
public ObservableCollection Roster { get; } = [];
[ObservableProperty] private string? _homeroomClassName;
[ObservableProperty] private int _activeTabIndex;
[ObservableProperty] private string _status = "";
[ObservableProperty] private bool _busy;
public bool HomeroomClassConfigured => !string.IsNullOrWhiteSpace(HomeroomClassName);
/// Vom Code-Behind/MainWindowViewModel gesetzt: springt in die WebUntis-Einstellungen, wenn noch
/// keine Klasse gewählt ist.
public Func? OnNavigateToSettings { get; set; }
public ClassTeacherOverviewViewModel(WebUntisSettingsService settings, WebUntisIntegrationService untis,
UntisReportCacheService cache, ClassTeacherDetailsViewModel detailsTab)
{
_settings = settings; _untis = untis; _cache = cache; DetailsTab = detailsTab;
}
partial void OnHomeroomClassNameChanged(string? value) => OnPropertyChanged(nameof(HomeroomClassConfigured));
[RelayCommand]
private async Task Load()
{
HomeroomClassName = _settings.HomeroomClassName;
ActiveTabIndex = 0;
Roster.Clear();
if (!HomeroomClassConfigured)
{
Status = "Noch keine Klasse ausgewählt.";
return;
}
var className = HomeroomClassName!;
DetailsTab.Initialize(className);
Busy = true;
try
{
var today = DateOnly.FromDateTime(DateTime.Today);
var studentsTask = _untis.GetStudentsAsync(className);
var absencesTask = _cache.GetAbsencesAsync(className, today, today);
var classRegisterTask = _cache.GetClassRegisterEventsAsync(className, today.AddDays(-7), today);
await Task.WhenAll(studentsTask, absencesTask, classRegisterTask);
var todayAbsences = ClassAbsenceDaySummaryRow.GroupByStudentAndDay(absencesTask.Result);
foreach (var row in ClassTeacherRosterRow.Build(studentsTask.Result.Students, todayAbsences,
classRegisterTask.Result))
Roster.Add(row);
Status = $"{Roster.Count} Schüler*innen · Stand heute.";
}
catch (WebUntisIntegrationException ex) { Status = ex.Message; }
finally { Busy = false; }
}
[RelayCommand]
private void ShowDetailsForStudent(ClassTeacherRosterRow? row)
{
if (row is null) return;
DetailsTab.StudentFilter = row.StudentName;
ActiveTabIndex = 1;
DetailsTab.LoadCommand.Execute(null);
}
[RelayCommand]
private async Task GoToSettings()
{
if (OnNavigateToSettings is not null) await OnNavigateToSettings();
}
}