using Modules.Library.Domain.Entities; using Modules.Library.Domain.Entities.MediaContent.CommonProperties; using Modules.Library.Domain.Entities.MediaContent.Items.Anime; using Modules.Library.Domain.Exceptions.Genre; using Modules.Library.Domain.Gateways; namespace Modules.Library.Domain.Interactors.MediaContent.Anime; internal class CommonPropertiesService(ILanguageGateway languageGateway, IGenreGateway genreGateway) { #region Name internal async Task AddName(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId, NameType nameType, Guid languageId, string value) { var commonProperties = GetCommonProperties(title, animeSeasonId, animeEpisodeId); if (!await languageGateway.AnyWhere(q => q.Id == languageId)) throw new Exception("Language is not found"); switch (nameType) { case NameType.Original: AddNameOriginal(commonProperties, languageId, value); break; case NameType.OriginalInAnotherLanguage: AddNameOriginalInAnotherLanguage(commonProperties, languageId, value); break; case NameType.Translation: AddNameTranslation(commonProperties, languageId, value); break; case NameType.Abbreviation: AddNameAbbreviation(commonProperties, languageId, value); break; default: throw new NotImplementedException(); } } private static void AddNameOriginal(CommonProperties commonProperties, Guid languageId, string value) { if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value)); if (commonProperties.Names.Any(q => q.Type == NameType.Original)) throw new Exception("Original name is already exist."); commonProperties.Names.Add(new NameItem(languageId, NameType.Original, value)); } private static void AddNameOriginalInAnotherLanguage(CommonProperties commonProperties, Guid languageId, string value) { if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value)); if (languageId == commonProperties.Names.SingleOrDefault(q => q.Type == NameType.Original)?.LanguageId) throw new Exception("Language must not match original name language"); if (commonProperties.Names.Any(q => q.Type == NameType.OriginalInAnotherLanguage && q.LanguageId == languageId)) throw new Exception("Name in following language is already exist."); commonProperties.Names.Add(new NameItem(languageId, NameType.OriginalInAnotherLanguage, value)); } private static void AddNameTranslation(CommonProperties commonProperties, Guid languageId, string value) { if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value)); commonProperties.Names.Add(new NameItem(languageId, NameType.Translation, value)); } private static void AddNameAbbreviation(CommonProperties commonProperties, Guid languageId, string value) { if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value)); commonProperties.Names.Add(new NameItem(languageId, NameType.Abbreviation, value)); } internal static void RemoveName(CommonProperties commonProperties, Guid nameId) { var name = GetName(commonProperties, nameId); if (name.Type == NameType.Original) throw new Exception($"Unable to remove original name"); commonProperties.Names.Remove(name); } internal static void SetNameValue(CommonProperties commonProperties, Guid nameId, string value) { if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value)); var name = GetName(commonProperties, nameId); if (name.Type != NameType.Original && commonProperties.Names.Any(q => q.LanguageId == name.LanguageId && q.Value == value)) throw new Exception("Name item with in same language with same value is already exists"); name.SetValue(value); } private static NameItem GetName(CommonProperties commonProperties, Guid nameId) => commonProperties.Names.FirstOrDefault(q => q.Id == nameId) ?? throw new Exception($"Name with id [{nameId}] is not found"); #endregion internal static void SetPreview(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId, string url, MediaInfoType type) { var commonProperties = GetCommonProperties(title, animeSeasonId, animeEpisodeId); if (string.IsNullOrWhiteSpace(url)) throw new ArgumentNullException(nameof(url)); commonProperties.Preview ??= new MediaInfo(url, type); commonProperties.Preview.Url = url; } internal static void DeletePreview(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId) { var commonProperties = GetCommonProperties(title, animeSeasonId, animeEpisodeId); commonProperties.Preview = null; } #region Description internal async Task AddDescription(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId, Guid languageId, bool isOriginal, string value) { var commonProperties = GetCommonProperties(title, animeSeasonId, animeEpisodeId); if (isOriginal) { if (!await languageGateway.AnyWhere(q => q.Id == languageId)) throw new Exception("Language is not found"); if (commonProperties.Descriptions.Any(q => q.IsOriginal)) throw new Exception("Original description is already exist."); } if (!await languageGateway.AnyWhere(q => q.Id == languageId)) throw new Exception("Language is not found"); var description = new DescriptionItem(languageId, isOriginal, value); commonProperties.Descriptions.Add(description); } internal static void RemoveDescription(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId, Guid descriptionId) { var commonProperties = GetCommonProperties(title, animeSeasonId, animeEpisodeId); var description = GetDescription(commonProperties, descriptionId); if (description.IsOriginal) throw new Exception($"Unable to remove original description"); commonProperties.Descriptions.Remove(description); } internal static void SetDescriptionValue(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId, Guid descriptionId, string value) { var commonProperties = GetCommonProperties(title, animeSeasonId, animeEpisodeId); var name = GetDescription(commonProperties, descriptionId); SetDescriptionValue(commonProperties, name, value); } private static void SetDescriptionValue(CommonProperties commonProperties, DescriptionItem descriptionItem, string value) { if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value)); if (!descriptionItem.IsOriginal && commonProperties.Descriptions.Any(q => q.LanguageId == descriptionItem.LanguageId && q.Value == value)) throw new Exception("Descriptoin item with with same value is already exists"); descriptionItem.Value = value; } private static DescriptionItem GetDescription(CommonProperties commonProperties, Guid descriptionId) { return commonProperties.Descriptions.FirstOrDefault(q => q.Id == descriptionId) ?? throw new Exception($"Description with id [{descriptionId}] is not found"); } #endregion #region RelatedContent internal static void AddRelatedContent(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId, string url, MediaInfoType type) { var commonProperties = GetCommonProperties(title, animeSeasonId, animeEpisodeId); if (string.IsNullOrWhiteSpace(url)) throw new ArgumentNullException(nameof(url)); CheckIfCanAddOrEdit(commonProperties, url, type); commonProperties.RelatedContent.Add(new MediaInfo(url, type)); } internal static void EditRelatedContent(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId, Guid relatedContentId, string url, MediaInfoType type) { var commonProperties = GetCommonProperties(title, animeSeasonId, animeEpisodeId); var relatedContent = GetRelatedContent(commonProperties, relatedContentId); CheckIfCanAddOrEdit(commonProperties, url, type); relatedContent.Url = url; relatedContent.Type = type; } internal static void RemoveRelatedContent(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId, Guid relatedContentId) { var commonProperties = GetCommonProperties(title, animeSeasonId, animeEpisodeId); var relatedContent = GetRelatedContent(commonProperties, relatedContentId); //_relatedContent.Remove(relatedContent); commonProperties.RelatedContent.Remove(relatedContent); } private static void CheckIfCanAddOrEdit(CommonProperties commonProperties, string url, MediaInfoType type) { if (commonProperties.RelatedContent.Any(q => q.Url == url && q.Type == type)) throw new Exception("Related content with same url and same type is already exists"); } private static MediaInfo GetRelatedContent(CommonProperties commonProperties, Guid relatedContentId) { return commonProperties.RelatedContent.FirstOrDefault(q => q.Id == relatedContentId) ?? throw new Exception($"Related content with id [{relatedContentId}] is not found"); } #endregion #region Genre internal async Task AddGenre(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId, Guid genreId, decimal? proportion) { var commonProperties = GetCommonProperties(title, animeSeasonId, animeEpisodeId); if (!await genreGateway.AnyWhere(q => q.Id == genreId)) throw new GenreIsNotFoundException(); commonProperties.Genres.Add(new GenreProportionItem(genreId, proportion)); } internal static void SetGenreProportion(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId, Guid genreProportionId, decimal? proportion) { var commonProperties = GetCommonProperties(title, animeSeasonId, animeEpisodeId); var genreProportion = commonProperties.Genres.First(q => q.GenreId == genreProportionId); genreProportion.Proportion = proportion; } internal static void RemoveGenre(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId, Guid genreProportionId, decimal? proportion) { var commonProperties = GetCommonProperties(title, animeSeasonId, animeEpisodeId); var genreProportion = commonProperties.Genres.First(q => q.GenreId == genreProportionId); commonProperties.Genres.Remove(genreProportion); } #endregion internal static void SetAnnouncementDate(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId, DateTimeOffset value) { var commonProperties = GetCommonProperties(title, animeSeasonId, animeEpisodeId); if (value == default) throw new ArgumentNullException(nameof(value)); commonProperties.AnnouncementDate = value; } internal static void SetEstimatedReleaseDate(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId, DateTimeOffset value) { var commonProperties = GetCommonProperties(title, animeSeasonId, animeEpisodeId); if (value == default) throw new ArgumentNullException(nameof(value)); if (commonProperties.AnnouncementDate.HasValue && value <= commonProperties.AnnouncementDate.Value) throw new Exception("Estimated release date can not be less or equal to announcement date"); commonProperties.EstimatedReleaseDate = value; } internal static void SetReleaseDate(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId, DateTimeOffset value) { var commonProperties = GetCommonProperties(title, animeSeasonId, animeEpisodeId); if (value == default) throw new ArgumentNullException(nameof(value)); if (commonProperties.AnnouncementDate.HasValue && value <= commonProperties.AnnouncementDate.Value) throw new Exception("Release date can not be less or equal to announcement date"); commonProperties.ReleaseDate = value; } private static CommonProperties GetCommonProperties(AnimeTitle title, Guid? animeSeasonId, Guid? animeEpisodeId) { if (!animeSeasonId.HasValue && !animeEpisodeId.HasValue) return title.CommonProperties; else if (animeSeasonId.HasValue && !animeEpisodeId.HasValue) { var season = title.Items.OfType().First(q => q.Id == animeSeasonId); return season.CommonProperties; } else if (!animeSeasonId.HasValue && animeEpisodeId.HasValue) { //set to unseason episode var episode = title.Items.OfType().First(q => q.Id == animeEpisodeId); return episode.CommonProperties; } else { var season = title.Items.OfType().First(q => q.Id == animeSeasonId); var episode = season.Episodes.First(q => q.Id == animeEpisodeId); return episode.CommonProperties; } } }