using Modules.Library.Domain.Entities.MediaContent.CommonProperties; namespace Modules.Library.Domain.Entities.MediaContent.Items.Anime; public class AnimeTitle : Entity { public CommonProperties.CommonProperties CommonProperties { get; protected set; } = default!; private readonly List _items = []; public IReadOnlyCollection Items => _items.ToList().AsReadOnly(); public bool Completed { get; private set; } public TimeSpan ExpirationTime { get; private set; } private AnimeTitle() { } private AnimeTitle(Models.AnimeTitle title) { Id = title.Id; Deleted = title.Deleted; CommonProperties = MediaContent.CommonProperties.CommonProperties.New(title.CommonProperties); Completed = title.Completed; ExpirationTime = title.ExpirationTime; foreach(var item in title.Items) { if (item is Models.AnimeEpisode episode) { _items.Add(AnimeEpisode.New(this, episode)); } else if (item is Models.AnimeSeason season) { _items.Add(AnimeSeason.New(this, season)); } } } public AnimeTitle(IEnumerable names) { CommonProperties = new CommonProperties.CommonProperties(names); } public static AnimeTitle New(Models.AnimeTitle title) => new(title); public AnimeEpisode AddEpisode(AnimeEpisodeType type, IEnumerable names, int? number = null) => CreateAndAddEpisode(type, names, number: number); public AnimeEpisode AddEpisodeAsVariant(AnimeEpisodeType type, IEnumerable names, ushort order, int? number = null) => CreateAndAddEpisode(type, names, order, number:number); private AnimeEpisode CreateAndAddEpisode(AnimeEpisodeType type, IEnumerable names, ushort? order = null, ushort? variant = null, int? number = null) { StructureAction(() => { (order, variant) = AnimeItem.GetOrderAndVariant(_items, order, variant); number ??= _items.OfType().Select(q => q.Number).DefaultIfEmpty(0).Max() + 1; _items.Add(new AnimeEpisode(this, null, type, names, order.Value, variant.Value, number)); CheckIfCompleted(); }); return _items.OfType().OrderByDescending(q => q.Order).ThenByDescending(q => q.Variant).First(); } public void RemoveEpisode(ushort order, ushort variant) { var episode = _items.OfType().FirstOrDefault(q => q.Order == order && q.Variant == variant) ?? throw new Exception("EpisodeNotFoundException"); _items.Remove(episode); } public AnimeSeason AddSeason() { StructureAction(() => { (ushort order, _) = AnimeItem.GetOrderAndVariant(_items); //var number = _items.OfType().Select(q => q.Number).DefaultIfEmpty(-1).Max() + 1; var number = _items.OfType().Select(q => q.Number).DefaultIfEmpty(0).Max() + 1; var season = new AnimeSeason(this, order, number); _items.Add(season); }); return _items.OfType().OrderByDescending(q => q.Order).ThenByDescending(q => q.Variant).First(); } public AnimeSeason AddSeason(IEnumerable names, int? number = null) { StructureAction(() => { (ushort order, _) = AnimeItem.GetOrderAndVariant(_items); number ??= _items.OfType().Select(q => q.Number).DefaultIfEmpty(0).Max() + 1; var season = new AnimeSeason(this, names, order, number); _items.Add(season); }); return _items.OfType().OrderByDescending(q => q.Order).ThenByDescending(q => q.Variant).First(); } /* public void AddSeasonAsVariant(AnimeEpisodeType episodeType, ushort order) { if (order < 0 || order > _shadow.items.Select(q => q.order).DefaultIfEmpty(0).Max()) throw new ArgumentOutOfRangeException(nameof(order)); var episode = new AnimeEpisodeShadow { type = episodeType, order = order, variant = AnimeItem.GetNewVariant(_shadow.items, order), number = _shadow.items.OfType().FirstOrDefault(q => q.order == order)?.number, }; _shadow.items.Add(episode); CheckIsCompleted(); } */ //public void RemoveSeason(ushort order, ushort variant) public void RemoveSeason(ushort order) { var season = _items.OfType().FirstOrDefault(q => q.Order == order) ?? throw new Exception("SeasonNotFoundException"); _items.Remove(season); } public void SetCompleted() => Completed = true; public void SetNotCompleted() => Completed = false; public void SetExpirationTime(TimeSpan value) => StructureAction(() => ExpirationTime = value); public void Delete() => Deleted = true; internal void CheckIfCompleted() { var itemsQuery = Items.AsQueryable(); var ucompletedSeasons = itemsQuery .Any(q => !q.CommonProperties.ReleaseDate.HasValue); var lastEpisodeReleaseDate = itemsQuery .OrderByDescending(q => q.CommonProperties.ReleaseDate) .FirstOrDefault()?.CommonProperties.ReleaseDate; if (ucompletedSeasons || lastEpisodeReleaseDate >= DateTime.UtcNow - ExpirationTime) SetNotCompleted(); else SetCompleted(); } protected void StructureAction(Action action) { action(); CheckIfCompleted(); } }