package io.delphiplatform.api.integration;

import com.google.common.collect.Sets;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import java.time.LocalDate;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import groovy.lang.Tuple;
import io.delphiplatform.api.integration.annotation.BigTable;
import io.delphiplatform.api.integration.annotation.SqlScript;
import io.delphiplatform.api.integration.model.TikTokTopIsrcSoundModel;
import io.delphiplatform.api.integration.model.TikTokTopIsrcSoundModelItems;
import io.delphiplatform.api.v3.model.tiktok.TikTokContentType;
import io.delphiplatform.api.v3.model.tiktok.top.sound.TikTokTopIsrcSound;
import io.delphiplatform.api.v3.model.tiktok.top.sound.TikTokTopIsrcSoundsItems;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class TikTokTopIsrcSoundsIT extends BaseIT {

    @Test
    @BigTable(table = "fact_tiktok_top_isrc_sounds_weekly", json = "/scripts/TikTokTopIsrcSoundsIT/BT_tiktokTopSounds_singleIsrc.json")
    public void tiktokTopSounds_singleIsrc() {
        String isrc1 = "USQX91901111";
        LocalDate startDate = LocalDate.of(2023, 2, 1);
        LocalDate endDate = LocalDate.of(2023, 3, 1);
        ResponseEntity<TikTokTopIsrcSoundModelItems> response = apiClient.sendAPIRequest(
            String.format("/v3/tiktok/isrc/top/sounds?isrc=%s&start_date=%s&end_date=%s", isrc1, startDate, endDate),
            TikTokTopIsrcSoundModelItems.class);

        assertEquals(HttpStatus.OK, response.getStatusCode());
        TikTokTopIsrcSoundModelItems topIsrcSounds = response.getBody();
        Assertions.assertNotNull(topIsrcSounds);
        assertEquals(20, topIsrcSounds.getCount());

        TikTokTopIsrcSoundModel isrcSounds0 = topIsrcSounds.getItems().get(0);

        assertEquals(LocalDate.of(2023, 2, 5), isrcSounds0.getReportDate());
        assertEquals("sound100", isrcSounds0.getSoundId());
        assertEquals(Set.of("USQX91901111"), isrcSounds0.getIsrcs());
        assertEquals("https://www.tiktok.com/music/-sound100", isrcSounds0.getSoundUrl());
        assertEquals(TikTokContentType.PGC, isrcSounds0.getContentType());
        assertEquals(100, isrcSounds0.getCreations());
        assertEquals(10, isrcSounds0.getCreationsShare().longValue());
        assertEquals(0, isrcSounds0.getPosition());

        assertEquals(List.of(
                "sound100",
                "sound95",
                "sound90",
                "sound85",
                "sound80",
                "sound75",
                "sound70",
                "sound65",
                "sound60",
                "sound55",
                "sound50",
                "sound45",
                "sound40",
                "sound35",
                "sound30",
                "sound25",
                "sound20",
                "sound15",
                "sound10",
                "sound5"
            ),
            topIsrcSounds.getItems().stream().map(TikTokTopIsrcSoundModel::getSoundId).collect(Collectors.toList()));

        long currentValue = Long.MAX_VALUE;
        int currentPosition = Integer.MIN_VALUE;

        for (TikTokTopIsrcSoundModel item : topIsrcSounds.getItems()) {
            assertTrue(item.getCreations() < currentValue);
            assertTrue(item.getPosition() > currentPosition);
            currentValue = item.getCreations();
            currentPosition = item.getPosition();
        }
    }


    @Test
    @SqlScript(scripts = "/scripts/TikTokTopIsrcSoundsIT/tiktokTopSounds_relatedIsrcs.sql")
    @BigTable(table = "fact_tiktok_top_isrc_sounds_weekly", json = "/scripts/TikTokTopIsrcSoundsIT/BT_tiktokTopSounds_relatedIsrcs.json")
    public void tiktokTopSounds_relatedIsrcs() {
        String isrc1 = "USQX91901111";
        LocalDate startDate = LocalDate.of(2023, 2, 1);
        LocalDate endDate = LocalDate.of(2023, 3, 1);
        ResponseEntity<TikTokTopIsrcSoundsItems> response = apiClient.sendAPIRequest(
            String.format("/v3/tiktok/isrc/top/sounds?expand_to=related_isrcs&isrc=%s&start_date=%s&end_date=%s", isrc1, startDate, endDate),
            TikTokTopIsrcSoundsItems.class);

        assertEquals(HttpStatus.OK, response.getStatusCode());
        TikTokTopIsrcSoundsItems topIsrcSounds = response.getBody();
        Assertions.assertNotNull(topIsrcSounds);
        assertEquals(20, topIsrcSounds.getCount());

        assertEquals(List.of(
                "sound100",
                "sound95",
                "sound90",
                "sound85",
                "sound80",
                "sound75",
                "sound70",
                "sound65",
                "sound60",
                "sound55",
                "sound50",
                "sound45",
                "sound40",
                "sound35",
                "sound30",
                "sound25",
                "sound20",
                "sound15",
                "sound10",
                "sound5"
            ),
            topIsrcSounds.getItems().stream().map(TikTokTopIsrcSound::getSoundId).collect(Collectors.toList()));

        assertEquals(List.of(
                Set.of("USQX91901111"),
                Set.of("USQX91901112"),
                Set.of("USQX91901111"),
                Set.of("USQX91901112"),
                Set.of("USQX91901111"),
                Set.of("USQX91901112"),
                Set.of("USQX91901111"),
                Set.of("USQX91901112"),
                Set.of("USQX91901111"),
                Set.of("USQX91901112"),
                Set.of("USQX91901111"),
                Set.of("USQX91901112"),
                Set.of("USQX91901111"),
                Set.of("USQX91901112"),
                Set.of("USQX91901111"),
                Set.of("USQX91901112"),
                Set.of("USQX91901111"),
                Set.of("USQX91901112"),
                Set.of("USQX91901111"),
                Set.of("USQX91901112")
            ),
            topIsrcSounds.getItems().stream().map(TikTokTopIsrcSound::getIsrcs).collect(Collectors.toList()));

        long currentValue = Long.MAX_VALUE;
        int currentPosition = Integer.MIN_VALUE;

        for (TikTokTopIsrcSound item : topIsrcSounds.getItems()) {
            assertTrue(item.getCreations() < currentValue);
            assertTrue(item.getPosition() > currentPosition);
            currentValue = item.getCreations();
            currentPosition = item.getPosition();
        }
    }

    /**
     * This test verifies that API correctly merges sounds if they are duplicated in the response.
     * <p>
     * This is expected that API will sum up the creations. Also, it will recalculate the creations_share based on total number of creations
     * in the response.
     */
    @Test
    @SqlScript(scripts = "/scripts/TikTokTopIsrcSoundsIT/tiktokTopSounds_relatedIsrcsDuplicatedSounds.sql")
    @BigTable(table = "fact_tiktok_top_isrc_sounds_weekly", json = "/scripts/TikTokTopIsrcSoundsIT/BT_tiktokTopSounds_relatedIsrcsDuplicatedSounds.json")
    public void tiktokTopSounds_relatedIsrcsDuplicatedSounds() {
        String isrc1 = "USQX91901111";
        LocalDate startDate = LocalDate.of(2023, 2, 1);
        LocalDate endDate = LocalDate.of(2023, 3, 1);
        ResponseEntity<TikTokTopIsrcSoundModelItems> response = apiClient.sendAPIRequest(
            String.format("/v3/tiktok/isrc/top/sounds?expand_to=related_isrcs&isrc=%s&start_date=%s&end_date=%s", isrc1, startDate,
                endDate), TikTokTopIsrcSoundModelItems.class);

        assertEquals(HttpStatus.OK, response.getStatusCode());
        TikTokTopIsrcSoundModelItems topIsrcSounds = response.getBody();
        Assertions.assertNotNull(topIsrcSounds);
        assertEquals(16, topIsrcSounds.getCount(),
            "Count mismatch. Possible reasons: duplicates were not merged, items with different content type were merged."
                + " Actual items are:" + topIsrcSounds);

        int position = 0;
        LocalDate reportDate = LocalDate.of(2023, 2, 5);
        float totalCreations = 1010;
        Set<String> isrcs1 = Set.of("USQX91901111");
        Set<String> isrcs2 = Set.of("USQX91901112");
        Set<String> isrcsMerged = Sets.newHashSet("USQX91901111", "USQX91901112");

        assertEquals(List.of(
                Tuple.tuple("sound0_0", isrcs1, TikTokContentType.PGC, position++),
                Tuple.tuple("sound_common_0", isrcsMerged, TikTokContentType.PGC, position++),
                Tuple.tuple("sound1_0", isrcs2, TikTokContentType.PGC, position++),
                Tuple.tuple("sound0_1", isrcs1, TikTokContentType.PGC, position++),
                Tuple.tuple("sound1_1", isrcs2, TikTokContentType.PGC, position++),
                Tuple.tuple("sound0_2", isrcs1, TikTokContentType.PGC, position++),
                Tuple.tuple("sound1_2", isrcs2, TikTokContentType.PGC, position++),
                Tuple.tuple("sound0_3", isrcs1, TikTokContentType.PGC, position++),
                Tuple.tuple("sound1_3", isrcs2, TikTokContentType.PGC, position++),
                Tuple.tuple("sound0_4", isrcs1, TikTokContentType.PGC, position++),
                Tuple.tuple("sound_common_2", isrcsMerged, TikTokContentType.PGC, position++),
                Tuple.tuple("sound1_4", isrcs2, TikTokContentType.PGC, position++),
                Tuple.tuple("sound_pgc_ugc_common_1", isrcs1, TikTokContentType.PGC, position++),
                Tuple.tuple("sound_pgc_ugc_common_1", isrcs2, TikTokContentType.UGC, position++),
                Tuple.tuple("sound_common_3", isrcsMerged, TikTokContentType.PGC, position++),
                Tuple.tuple("sound_common_4", isrcsMerged, TikTokContentType.PGC, position++)
            ),
            topIsrcSounds.getItems().stream().map(i -> Tuple.tuple(i.getSoundId(), i.getIsrcs(), i.getContentType(), i.getPosition()))
                .collect(Collectors.toList()),
            "Maybe these are sorting problems, makes sense to verify the order. Also, maybe some problems with share calculation." +
                " Actual items are:" + topIsrcSounds);

        assertEquals(List.of(
                Tuple.tuple(100L, Math.round((100L / totalCreations) * 100)),
                Tuple.tuple(90L, Math.round((90L / totalCreations) * 100)),
                Tuple.tuple(90L, Math.round((90L / totalCreations) * 100)),
                Tuple.tuple(90L, Math.round((90L / totalCreations) * 100)),
                Tuple.tuple(80L, Math.round((80L / totalCreations) * 100)),
                Tuple.tuple(80L, Math.round((80L / totalCreations) * 100)),
                Tuple.tuple(70L, Math.round((70L / totalCreations) * 100)),
                Tuple.tuple(70L, Math.round((70L / totalCreations) * 100)),
                Tuple.tuple(60L, Math.round((60L / totalCreations) * 100)),
                Tuple.tuple(60L, Math.round((60L / totalCreations) * 100)),
                Tuple.tuple(50L, Math.round((50L / totalCreations) * 100)),
                Tuple.tuple(50L, Math.round((50L / totalCreations) * 100)),
                Tuple.tuple(40L, Math.round((40L / totalCreations) * 100)),
                Tuple.tuple(30L, Math.round((30L / totalCreations) * 100)),
                Tuple.tuple(30L, Math.round((30L / totalCreations) * 100)),
                Tuple.tuple(20L, Math.round((20L / totalCreations) * 100))
            ),
            topIsrcSounds.getItems().stream().map(i -> Tuple.tuple(i.getCreations(), Math.round(i.getCreationsShare().floatValue())))
                .collect(Collectors.toList()),
            "Maybe these are sorting problems, makes sense to verify the order. Also, maybe some problems with share calculation." +
                " Actual items are:" + topIsrcSounds);

        long currentValue = Long.MAX_VALUE;
        int currentPosition = Integer.MIN_VALUE;

        for (TikTokTopIsrcSoundModel item : topIsrcSounds.getItems()) {
            assertTrue(item.getCreations() <= currentValue);
            assertTrue(item.getPosition() > currentPosition);
            currentValue = item.getCreations();
            currentPosition = item.getPosition();
        }
    }

}
