using System.ComponentModel; using System.Text; using LehrerApp.Core.Interfaces; using LehrerApp.Core.Models; namespace LehrerApp.Desktop.Services.Mcp.Tools; /// MCP-Write-Tool "update_student_group_assignment" (Phase 2, siehe Planungsdokument). /// Legt eine an, falls noch keine für Schüler+Gruppe existiert, sonst /// werden nur die übergebenen (nicht-null) Felder überschrieben. public class GroupMembershipTools( IGroupMembershipRepository memberships, IStudentRepository students, IGroupRepository groups, IMcpConfirmationService confirmation) { [Description("Legt eine Gruppenmitgliedschaft eines Schülers an oder ändert Niveau/Zeitraum einer bestehenden. Muss der Nutzer erst in einem Dialog in LehrerApp bestätigen.")] public async Task UpdateStudentGroupAssignment( [Description("Schüler-ID.")] Guid studentId, [Description("Lerngruppen-ID.")] Guid groupId, [Description("Niveau: E, G oder Foerder. Unverändert lassen: weglassen.")] Niveau? niveau = null, [Description("Zeitraum: FullYear, H1Only, H2Only oder Custom. Unverändert lassen: weglassen.")] MembershipPeriod? period = null, [Description("Beitrittsdatum bei Custom-Zeitraum, Format YYYY-MM-DD.")] DateOnly? joinedAt = null, [Description("Austrittsdatum bei Custom-Zeitraum, Format YYYY-MM-DD.")] DateOnly? leftAt = null, CancellationToken ct = default) { var student = students.GetById(studentId); var group = groups.GetById(groupId); if (student is null || group is null) return new WriteResultDto(false, null, "Unbekannte Schüler- oder Gruppen-ID."); var existing = memberships.GetByStudentAndGroup(studentId, groupId); var target = existing is null ? new GroupMembership { StudentId = studentId, GroupId = groupId } : Clone(existing); var changes = new StringBuilder(); if (niveau is not null && niveau != target.Niveau) { changes.AppendLine($"Niveau: {NiveauName(target.Niveau)} → {NiveauName(niveau)}"); target.Niveau = niveau; } if (period is not null && period != target.Period) { changes.AppendLine($"Zeitraum: {target.Period} → {period}"); target.Period = period.Value; } if (joinedAt is not null && joinedAt != target.JoinedAt) { changes.AppendLine($"Beitritt: {target.JoinedAt:dd.MM.yyyy} → {joinedAt:dd.MM.yyyy}"); target.JoinedAt = joinedAt; } if (leftAt is not null && leftAt != target.LeftAt) { changes.AppendLine($"Austritt: {target.LeftAt:dd.MM.yyyy} → {leftAt:dd.MM.yyyy}"); target.LeftAt = leftAt; } if (existing is not null && changes.Length == 0) return new WriteResultDto(true, existing.Id, "Keine Änderung nötig, Mitgliedschaft besteht bereits unverändert."); var title = existing is null ? "Gruppenmitgliedschaft anlegen?" : "Gruppenmitgliedschaft ändern?"; var message = $"{student.FullName} — {group.Name}" + (existing is null ? "\nNeue Mitgliedschaft anlegen." : "") + (changes.Length > 0 ? "\n" + changes.ToString().TrimEnd() : ""); if (!await confirmation.ConfirmAsync(title, message, ct)) return new WriteResultDto(false, null, "Vom Nutzer abgelehnt oder nicht bestätigt."); memberships.Save(target); return new WriteResultDto(true, target.Id, "Gruppenmitgliedschaft gespeichert."); } private static GroupMembership Clone(GroupMembership m) => new() { Id = m.Id, StudentId = m.StudentId, GroupId = m.GroupId, AddedOn = m.AddedOn, Period = m.Period, JoinedAt = m.JoinedAt, LeftAt = m.LeftAt, Niveau = m.Niveau, }; private static string NiveauName(Niveau? n) => n switch { Core.Models.Niveau.E => "E-Niveau", Core.Models.Niveau.G => "G-Niveau", Core.Models.Niveau.Foerder => "Förderniveau", _ => "–", }; }