package tv.pluto;

import static tv.pluto.utils.GenreUtils.deletedGenresSet;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.grpc.StatusRuntimeException;
import io.qameta.allure.Description;
import io.qameta.allure.TmsLink;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import lombok.SneakyThrows;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import tv.pluto.core.BaseTest;
import tv.pluto.grpc.genres.Genre;
import tv.pluto.grpc.genres.Genres;
import tv.pluto.grpc.genres.GenresClient;
import tv.pluto.grpc.utils.ProtoJsonUtil;
import tv.pluto.ip.dal.genre.Genres.GetTranslationsResponse;
import tv.pluto.utils.GenreUtils;
import tv.pluto.utils.JsonUtils;

public class GenresTestSuite extends BaseTest {
  private String genres;
  private Genres genre;
  private JsonUtils jsonUtils;
  private final String NON_EXISTING_GENRE = "NON_EXISTING_GENRE";

  private GenreUtils genreUtils;
  private GenresClient genresClient;

  @SneakyThrows
  @BeforeClass(alwaysRun = true)
  private void setUp() {
    genres = Files.readString(Paths.get("src/main/resources/genres.json"));
    jsonUtils = new JsonUtils();
    genre = jsonUtils.fromJson(genres, Genres.class);
    genreUtils = new GenreUtils();
    genresClient = new GenresClient(grpcChannelManager.getChannel());
  }

  @Test
  @Description("GetGenres - verify genres")
  @TmsLink("28501007")
  public void C28501007_TEST() {
    List<String> grpcGenres = genresClient.getGenres().getGenresList();
    Assert.assertEquals(genreUtils.getGenresFromAnnotations(), grpcGenres);
  }

  @Test
  @Description("GetSubGenresByGenre - existing genre")
  @TmsLink("28501008")
  public void C28501008_TEST() throws InvocationTargetException, IllegalAccessException {
    String randomGenre = genreUtils.getRandomItem(genreUtils.getGenresFromAnnotations());

    List<String> grpcSubGenres = genresClient.getSubGenresByGenre(randomGenre).getSubGenresList();
    List<String> subGenres = genreUtils.getSubGenresByAnnotation(randomGenre, genre);

    Assert.assertEquals(subGenres, grpcSubGenres);
  }

  @Test(
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp =
          "NOT_FOUND: No sub genres found associated with genre: " + NON_EXISTING_GENRE)
  @Description("GetSubGenresByGenre - existing non existing genre")
  @TmsLink("28501009")
  public void C28501009_TEST() {
    genresClient.getSubGenresByGenre(NON_EXISTING_GENRE);
  }

  @Test
  @Description("GetGenreSubGenreMap - verify GenreSubGenreMap")
  @TmsLink("28501010")
  public void C28501010_TEST() throws InvocationTargetException, IllegalAccessException {
    List<Genre> grpcGenreList = genreUtils.toGenreSubGenreMap(genresClient.getGenreSubGenreMap());
    List<Genre> genreList = genreUtils.toSubGenreMap(genre);

    for (Genre genre1 : genreList) {
      if (!grpcGenreList.contains(genre1)) {
        System.out.println("Genre: " + genre1.getGenre() + " is present in both lists.");
      }
    }

    for (Genre genre1 : grpcGenreList) {
      if (!genreList.contains(genre1)) {
        System.out.println("Genregrpc: " + genre1.getGenre() + " is present in both lists.");
      }
    }
    Assert.assertEquals(genreList, grpcGenreList);
  }

  @Test
  @Description("GetGenreSubGenreMap - verify GenreSubGenreMap")
  @TmsLink("28501011")
  public void C28501011_TEST() throws IOException {
    String path =
        System.getProperty("user.dir")
            .replace("integration-test", "lib/static/genres-translations.json");
    String genresTranslations = Files.readString(Paths.get(path));
    GetTranslationsResponse getTranslationsResponse = genresClient.getTranslationsResponse();
    Map<String, String> expectedResponse =
        new ObjectMapper().readValue(genresTranslations, Map.class);

    LinkedHashMap expectedGenres =
        new ObjectMapper().convertValue(expectedResponse.get("languages"), LinkedHashMap.class);

    String getTranslationJson = ProtoJsonUtil.toJson(getTranslationsResponse);
    Map<String, String> grpcResponse = new ObjectMapper().readValue(getTranslationJson, Map.class);
    LinkedHashMap grpcGenres =
        new ObjectMapper().convertValue(grpcResponse.get("languageCodes"), LinkedHashMap.class);

    Assert.assertNotNull(grpcGenres);
    Assert.assertEquals(grpcGenres, expectedGenres);
  }

  @Test
  @Description("GetGenreSubGenreMap - has a Thriller object for es")
  @TmsLink("28501013")
  public void C28501013_TEST() {
    String thriller = "Thriller";
    GetTranslationsResponse getTranslationsResponse = genresClient.getTranslationsResponse();
    String responseThriller =
        getTranslationsResponse.getLanguageCodesMap().get("es").getTranslationsMap().get(thriller);

    Assert.assertNotNull(responseThriller);
    Assert.assertEquals(responseThriller, thriller);
  }

  @Test
  @Description("GetGenres - Verify deleted genres are not present.")
  @TmsLink("28501014")
  public void C28501014_TEST() {
    List<String> grpcGenres = genresClient.getGenres().getGenresList();

    boolean containsDeletedGenres = grpcGenres.stream().anyMatch(deletedGenresSet::contains);

    Assert.assertFalse(
        containsDeletedGenres,
        "Found deleted genres in grpcGenres: "
            + grpcGenres.stream().filter(deletedGenresSet::contains).toList());
  }

  @DataProvider(name = "deletedGenres")
  public Object[][] provideDeletedGenres() {
    return new Object[][] {
      {"Classics"},
      {"Dance"},
      {"Gay & Lesbian"},
      {"Independent"},
      {"Musicals"},
      {"Other"},
      {"Private Channel"},
      {"Documentaries"},
      {"Faith and Spirituality"},
      {"News and Information"},
      {"Thrillers"}
    };
  }

  @Test(
      dataProvider = "deletedGenres",
      expectedExceptions = StatusRuntimeException.class,
      expectedExceptionsMessageRegExp = "NOT_FOUND: No sub genres found associated with genre: .*")
  @TmsLink("28501015")
  @Description("GetSubGenresByGenre - Verify deleted genres are not present.")
  public void C28501015_TEST(String deletedGenre) {
    genresClient.getSubGenresByGenre(deletedGenre);
  }

  @Test
  @Description("GenreSubGenreMap - Verify deleted genres are not present.")
  @TmsLink("28501016")
  public void C28501016_TEST() {
    List<Genre> grpcGenreList = genreUtils.toGenreSubGenreMap(genresClient.getGenreSubGenreMap());

    boolean containsDeletedGenre =
        grpcGenreList.stream().map(Genre::getGenre).anyMatch(deletedGenresSet::contains);

    Assert.assertFalse(
        containsDeletedGenre,
        "Found deleted genres in grpcGenreList: "
            + grpcGenreList.stream()
                .map(Genre::getGenre)
                .filter(deletedGenresSet::contains)
                .toList());
  }

  @Test
  @Description("GetTranslations - Verify deleted genres are not present.")
  @TmsLink("28501017")
  public void C28501017_TEST() {
    GetTranslationsResponse response = genresClient.getTranslationsResponse();

    Map<String, tv.pluto.ip.dal.genre.Genres.TranslationSet> languageCodes =
        response.getLanguageCodesMap();

    boolean containsDeletedGenres =
        languageCodes.values().stream()
            .flatMap(translationSet -> translationSet.getTranslationsMap().values().stream())
            .anyMatch(deletedGenresSet::contains);

    Assert.assertFalse(
        containsDeletedGenres,
        "Found deleted genres in translations: "
            + languageCodes.values().stream()
                .flatMap(translationSet -> translationSet.getTranslationsMap().values().stream())
                .filter(deletedGenresSet::contains)
                .toList());
  }
}
