// package com.magamochi.service; // // import static java.util.Objects.isNull; // import static java.util.Objects.nonNull; // // import com.google.common.util.concurrent.RateLimiter; // import com.magamochi.catalog.model.entity.Genre; // import com.magamochi.catalog.model.repository.GenreRepository; // import com.magamochi.catalog.client.AniListClient; // import com.magamochi.catalog.client.JikanClient; // import com.magamochi.common.exception.NotFoundException; // import com.magamochi.ingestion.model.entity.ContentProvider; // import com.magamochi.model.dto.ContentProviderMangaChapterResponseDTO; // import com.magamochi.model.entity.*; // import com.magamochi.model.repository.*; // import com.magamochi.catalog.util.DoubleUtil; // import java.io.*; // import java.net.URI; // import java.net.URISyntaxException; // import java.net.URL; // import java.time.OffsetDateTime; // import java.time.ZoneOffset; // import java.util.ArrayList; // import java.util.Comparator; // import java.util.List; // import java.util.stream.IntStream; // import java.util.zip.ZipEntry; // import java.util.zip.ZipInputStream; // import lombok.RequiredArgsConstructor; // import lombok.extern.log4j.Log4j2; // import org.apache.commons.lang3.StringUtils; // import org.springframework.stereotype.Service; // import org.springframework.web.multipart.MultipartFile; // // @Log4j2 //// @Service // @RequiredArgsConstructor // public class MangaImportService { // private final ProviderService providerService; // private final MangaCreationService mangaCreationService; // private final ImageService imageService; // private final LanguageService languageService; // // private final GenreRepository genreRepository; // private final MangaGenreRepository mangaGenreRepository; // private final MangaContentProviderRepository mangaContentProviderRepository; // private final AuthorRepository authorRepository; // private final MangaAuthorRepository mangaAuthorRepository; // private final MangaChapterRepository mangaChapterRepository; // private final MangaRepository mangaRepository; // // private final JikanClient jikanClient; // private final AniListClient aniListClient; // private final MangaChapterImageRepository mangaChapterImageRepository; // private final MangaAlternativeTitlesRepository mangaAlternativeTitlesRepository; // // private final RateLimiter jikanRateLimiter; // // public void importMangaFiles(String malId, List files) { // log.info("Importing manga files for MAL ID {}", malId); // var provider = providerService.getOrCreateProvider("Manual Import", false); // // jikanRateLimiter.acquire(); // var mangaData = jikanClient.getMangaById(Long.parseLong(malId)); // // var mangaProvider = getOrCreateMangaProvider(mangaData.data().title(), provider); // // var sortedFiles = // files.stream().sorted(Comparator.comparing(MultipartFile::getName)).toList(); // // IntStream.rangeClosed(1, sortedFiles.size()) // .forEach( // fileIndex -> { // var file = sortedFiles.get(fileIndex - 1); // log.info( // "Importing file {}/{}: {}, for Mangá {}", // fileIndex, // sortedFiles.size(), // file.getOriginalFilename(), // mangaProvider.getManga().getTitle()); // // var chapter = // persistMangaChapter( // mangaProvider, // new ContentProviderMangaChapterResponseDTO( // removeFileExtension(file.getOriginalFilename()), // "manual_" + file.getOriginalFilename(), // file.getOriginalFilename(), // "en-US")); // // List allChapterImages = new ArrayList<>(); // try (InputStream is = file.getInputStream(); // ZipInputStream zis = new ZipInputStream(is)) { // ZipEntry entry; // var position = 0; // // while ((entry = zis.getNextEntry()) != null) { // if (entry.isDirectory()) { // continue; // } // // var os = new ByteArrayOutputStream(); // zis.transferTo(os); // var bytes = os.toByteArray(); // // var image = // imageService.uploadImage(bytes, "image/jpeg", "chapter/" + chapter.getId()); // // var chapterImage = // MangaChapterImage.builder() // .position(position++) // .image(image) // .mangaChapter(chapter) // .build(); // // allChapterImages.add(chapterImage); // zis.closeEntry(); // } // // log.info("Chapter images added for chapter {}", chapter.getTitle()); // } catch (IOException e) { // throw new RuntimeException(e); // } // // mangaChapterImageRepository.saveAll(allChapterImages); // chapter.setDownloaded(true); // mangaChapterRepository.save(chapter); // }); // // log.info("Import manga files for MAL ID {} completed.", malId); // } // // public void updateMangaData(Long mangaId) { // var manga = // mangaRepository // .findById(mangaId) // .orElseThrow(() -> new NotFoundException("Manga not found for ID: " + mangaId)); // // updateMangaData(manga); // } // // public void updateMangaData(Manga manga) { // log.info("Updating manga {}", manga.getTitle()); // // if (nonNull(manga.getMalId())) { // try { // updateFromJikan(manga); // return; // } catch (Exception e) { // log.warn( // "Error updating manga data from Jikan for manga {}. Trying AniList... Error: {}", // manga.getTitle(), // e.getMessage()); // } // } // // if (nonNull(manga.getAniListId())) { // try { // updateFromAniList(manga); // return; // } catch (Exception e) { // log.warn( // "Error updating manga data from AniList for manga {}. Error: {}", // manga.getTitle(), // e.getMessage()); // } // } // // log.warn( // "Could not update manga data for {}. No provider data available/found.", // manga.getTitle()); // } // // private void updateFromJikan(Manga manga) throws IOException, URISyntaxException { // jikanRateLimiter.acquire(); // var mangaData = jikanClient.getMangaById(manga.getMalId()); // // manga.setSynopsis(mangaData.data().synopsis()); // manga.setStatus(mangaData.data().status()); // manga.setScore(DoubleUtil.round((double) mangaData.data().score(), 2)); // manga.setPublishedFrom(mangaData.data().published().from()); // manga.setPublishedTo(mangaData.data().published().to()); // manga.setChapterCount(mangaData.data().chapters()); // // var authors = // mangaData.data().authors().stream() // .map( // authorData -> // authorRepository // .findByMalId(authorData.mal_id()) // .orElseGet( // () -> // authorRepository.save( // Author.builder() // .malId(authorData.mal_id()) // .name(authorData.name()) // .build()))) // .toList(); // // updateMangaAuthors(manga, authors); // // var genres = // mangaData.data().genres().stream() // .map( // genreData -> // genreRepository // .findByMalId(genreData.mal_id()) // .orElseGet( // () -> // genreRepository.save( // Genre.builder() // .malId(genreData.mal_id()) // .name(genreData.name()) // .build()))) // .toList(); // // updateMangaGenres(manga, genres); // // if (isNull(manga.getCoverImage())) { // downloadCoverImage(manga, mangaData.data().images().jpg().large_image_url()); // } // // var mangaEntity = mangaRepository.save(manga); // var alternativeTitles = // mangaData.data().title_synonyms().stream() // .map(at -> MangaAlternativeTitle.builder().manga(mangaEntity).title(at).build()) // .toList(); // mangaAlternativeTitlesRepository.saveAll(alternativeTitles); // } // // private void updateFromAniList(Manga manga) throws IOException, URISyntaxException { // var query = // """ // query ($id: Int) { // Media (id: $id, type: MANGA) { // startDate { year month day } // endDate { year month day } // description // status // averageScore // chapters // coverImage { large } // genres // staff { // edges { // role // node { // name { // full // } // } // } // } // } // } // """; // var request = // new AniListClient.GraphQLRequest( // query, new AniListClient.GraphQLRequest.Variables(manga.getAniListId())); // var media = aniListClient.getManga(request).data().Media(); // // manga.setSynopsis(media.description()); // manga.setStatus(mapAniListStatus(media.status())); // manga.setScore(DoubleUtil.round((double) media.averageScore() / 10, 2)); // 0-100 -> 0-10 // manga.setPublishedFrom(convertFuzzyDate(media.startDate())); // manga.setPublishedTo(convertFuzzyDate(media.endDate())); // manga.setChapterCount(media.chapters()); // // var authors = // media.staff().edges().stream() // .filter(edge -> isAuthorRole(edge.role())) // .map(edge -> edge.node().name().full()) // .distinct() // .map( // name -> // authorRepository // .findByName(name) // .orElseGet( // () -> authorRepository.save(Author.builder().name(name).build()))) // .toList(); // // updateMangaAuthors(manga, authors); // // var genres = // media.genres().stream() // .map( // name -> // genreRepository // .findByName(name) // .orElseGet(() -> // genreRepository.save(Genre.builder().name(name).build()))) // .toList(); // // updateMangaGenres(manga, genres); // // if (isNull(manga.getCoverImage())) { // downloadCoverImage(manga, media.coverImage().large()); // } // // mangaRepository.save(manga); // } // // private boolean isAuthorRole(String role) { // return role.equalsIgnoreCase("Story & Art") // || role.equalsIgnoreCase("Story") // || role.equalsIgnoreCase("Art"); // } // // private String mapAniListStatus(String status) { // return switch (status) { // case "RELEASING" -> "Publishing"; // case "FINISHED" -> "Finished"; // case "NOT_YET_RELEASED" -> "Not yet published"; // default -> "Unknown"; // }; // } // // private OffsetDateTime convertFuzzyDate(AniListClient.MangaResponse.Manga.FuzzyDate date) { // if (isNull(date) || isNull(date.year())) { // return null; // } // return OffsetDateTime.of( // date.year(), // isNull(date.month()) ? 1 : date.month(), // isNull(date.day()) ? 1 : date.day(), // 0, // 0, // 0, // 0, // ZoneOffset.UTC); // } // // private void updateMangaAuthors(Manga manga, List authors) { // var mangaAuthors = // authors.stream() // .map( // author -> // mangaAuthorRepository // .findByMangaAndAuthor(manga, author) // .orElseGet( // () -> // mangaAuthorRepository.save( // MangaAuthor.builder().manga(manga).author(author).build()))) // .toList(); // manga.setMangaAuthors(mangaAuthors); // } // // private void updateMangaGenres(Manga manga, List genres) { // var mangaGenres = // genres.stream() // .map( // genre -> // mangaGenreRepository // .findByMangaAndGenre(manga, genre) // .orElseGet( // () -> // mangaGenreRepository.save( // MangaGenre.builder().manga(manga).genre(genre).build()))) // .toList(); // manga.setMangaGenres(mangaGenres); // } // // private void downloadCoverImage(Manga manga, String imageUrl) // throws IOException, URISyntaxException { // var inputStream = // new BufferedInputStream(new URL(new URI(imageUrl).toASCIIString()).openStream()); // // var bytes = inputStream.readAllBytes(); // // inputStream.close(); // var image = imageService.uploadImage(bytes, "image/jpeg", "cover"); // // manga.setCoverImage(image); // } // // public MangaChapter persistMangaChapter( // MangaContentProvider mangaContentProvider, ContentProviderMangaChapterResponseDTO chapter) { // var mangaChapter = // mangaChapterRepository // .findByMangaContentProviderAndUrlIgnoreCase(mangaContentProvider, // chapter.url()) // .orElseGet(MangaChapter::new); // // mangaChapter.setMangaContentProvider(mangaContentProvider); // mangaChapter.setTitle(chapter.title()); // mangaChapter.setUrl(chapter.url()); // // var language = languageService.getOrThrow(chapter.languageCode()); // mangaChapter.setLanguage(language); // // if (nonNull(chapter.chapter())) { // try { // mangaChapter.setChapterNumber(Integer.parseInt(chapter.chapter())); // } catch (NumberFormatException e) { // log.warn( // "Could not parse chapter number {} from manga {}", // chapter.chapter(), // mangaContentProvider.getManga().getTitle()); // } // } // // return mangaChapterRepository.save(mangaChapter); // } // // private MangaContentProvider getOrCreateMangaProvider( // String title, ContentProvider contentProvider) { // return mangaContentProviderRepository // .findByMangaTitleIgnoreCaseAndContentProvider(title, contentProvider) // .orElseGet( // () -> { // jikanRateLimiter.acquire(); // var manga = mangaCreationService.getOrCreateManga(title, "manual", contentProvider); // // return mangaContentProviderRepository.save( // MangaContentProvider.builder() // .manga(manga) // .mangaTitle(manga.getTitle()) // .contentProvider(contentProvider) // .url("manual") // .build()); // }); // } // // private String removeFileExtension(String filename) { // if (StringUtils.isBlank(filename)) { // return filename; // } // // int lastDotIndex = filename.lastIndexOf('.'); // // // No dot, or dot is the first character (like .gitignore) // if (lastDotIndex <= 0) { // return filename; // } // // return filename.substring(0, lastDotIndex); // } // }