using LehrerApp.Core.Interfaces; using LehrerApp.Core.Models; namespace LehrerApp.Core.Services; /// Gemeinsame Terminlogik für Verschieben und Trennen von Stunden. public sealed class LessonSchedulingService(ILessonRepository lessons) { public void Move(Lesson lesson, DateOnly newDate, int? newPeriod, bool shiftFollowing) { EnsureTargetIsFree(lesson, newDate, newPeriod); var oldDate = lesson.Date; var delta = newDate.DayNumber - oldDate.DayNumber; if (shiftFollowing && delta != 0) { foreach (var other in lessons.GetByUnit(lesson.UnitId)) { if (other.Id == lesson.Id || other.Status == LessonStatus.Conducted || other.Date <= oldDate) continue; other.Date = other.Date.AddDays(delta); lessons.Save(other); } } lesson.Date = newDate; if (newPeriod.HasValue) lesson.LessonNumber = newPeriod; lessons.Save(lesson); } public Lesson SplitAndMoveSecondPart(Lesson source, int splitAfterMinutes, DateOnly newDate, int newPeriod, TimeOnly? newStartTime) { if (splitAfterMinutes <= 0 || source.Phases.Sum(p => p.DurationMinutes) <= splitAfterMinutes) throw new InvalidOperationException("Der Verlauf reicht nicht über die erste Stunde hinaus."); EnsureTargetIsFree(source, newDate, newPeriod); var first = new List(); var second = new List(); var elapsed = 0; foreach (var phase in source.Phases) { var remainingInFirst = splitAfterMinutes - elapsed; if (remainingInFirst <= 0) second.Add(Clone(phase, phase.DurationMinutes)); else if (phase.DurationMinutes <= remainingInFirst) first.Add(Clone(phase, phase.DurationMinutes)); else { first.Add(Clone(phase, remainingInFirst)); second.Add(Clone(phase, phase.DurationMinutes - remainingInFirst)); } elapsed += phase.DurationMinutes; } source.Phases = first; var continuation = new Lesson { UnitId = source.UnitId, GroupId = source.GroupId, Date = newDate, LessonNumber = newPeriod, StartTime = newStartTime, Topic = string.IsNullOrWhiteSpace(source.Topic) ? "Fortsetzung" : $"{source.Topic} – Fortsetzung", Phases = second, Homework = source.Homework, HomeworkChecked = source.HomeworkChecked, HomeworkCheckDismissed = source.HomeworkCheckDismissed, Reflection = source.Reflection, Status = source.Status == LessonStatus.Conducted ? LessonStatus.Draft : source.Status, }; source.Homework = null; source.HomeworkChecked = false; source.HomeworkCheckDismissed = false; source.Reflection = null; lessons.Save(source); lessons.Save(continuation); return continuation; } private void EnsureTargetIsFree(Lesson source, DateOnly date, int? period) { if (period is null) return; var occupied = lessons.GetByGroupAndDate(source.GroupId, date) .Any(l => l.Id != source.Id && l.LessonNumber == period); if (occupied) throw new InvalidOperationException($"Für die Lerngruppe existiert am {date:dd.MM.yyyy} in der {period}. Stunde bereits eine Planung."); } private static LessonPhaseStep Clone(LessonPhaseStep source, int duration) => new() { Name = source.Name, DurationMinutes = duration, Activity = source.Activity, Material = source.Material, Shorthand = source.Shorthand, AlternativePathId = source.AlternativePathId, }; }