package io.delphiplatform.api.v3.bigtable.processing;

import com.google.common.collect.Lists;
import com.sonymusic.delphi.etl.apps.proto.Tiktok.TikTokStats;

import org.junit.jupiter.api.Test;

import java.time.LocalDate;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

import io.delphiplatform.api.v3.bigtable.entity.TikTokTrackCountryMetrics;
import io.delphiplatform.api.v3.model.tiktok.IsrcContentTypeCountryTikTokTrackAnalyticsBreakdowns;
import io.delphiplatform.api.v3.model.tiktok.TikTokContentType;
import io.delphiplatform.api.v3.model.tiktok.TikTokTrackAnalyticsMetrics;
import io.delphiplatform.api.v3.model.tiktok.TikTokTrackAnalyticsRangeMetrics;
import io.delphiplatform.api.v3.rdb.service.BreakdownHelper;

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

class TikTokTrackMetricsAggregatorTest {

    private static final String TEST_ISRC = "ISRC1";
    private static final String TEST_ISRC2 = "ISRC2";
    private static final LocalDate DATE1 = LocalDate.of(2020, 1, 1);
    private static final LocalDate DATE2 = LocalDate.of(2020, 1, 2);
    private static final LocalDate DATE3 = LocalDate.of(2020, 1, 3);
    private static final String US = "us";
    private static final String GB = "gb";
    private static final String UA = "ua";
    private static final String MX = "mx";
    private final TikTokTrackMetricsAggregator aggregator = new TikTokTrackMetricsAggregator(new BreakdownHelper());
    private final Map<String, Set<String>> ISRC_MAPPING_WITHOUT_RELATED = Map.of(TEST_ISRC, Set.of(TEST_ISRC));
    private final Map<String, Set<String>> ISRC_MAPPING_WITH_RELATED = Map.of(TEST_ISRC, Set.of(TEST_ISRC, TEST_ISRC2));

    @Test
    void contentTypeCountryBreakdown_emptyMetrics() {
        IsrcContentTypeCountryTikTokTrackAnalyticsBreakdowns breakdowns = aggregator
            .contentTypeCountryBreakdown(Stream.empty(), null, ISRC_MAPPING_WITHOUT_RELATED);

        assertTrue(breakdowns.getPgc().isEmpty());
        assertTrue(breakdowns.getUgc().isEmpty());
    }

    @Test
    void contentTypeCountryBreakdown_singleMetric() {
        IsrcContentTypeCountryTikTokTrackAnalyticsBreakdowns breakdowns = aggregator
            .contentTypeCountryBreakdown(Stream.of(
                    createCountryMetrics(TikTokContentType.UGC, US, 100L, DATE1, TEST_ISRC)),
                Collections.emptySet(), ISRC_MAPPING_WITHOUT_RELATED);

        assertTrue(breakdowns.getPgc().isEmpty());
        assertEquals(1, breakdowns.getUgc().get(US).getComments().size());
        assertEquals(100L, breakdowns.getUgc().get(US).getComments().get(0));
        assertEquals(100L, breakdowns.getUgc().get(US).getCommentsByDate().get(DATE1));
    }

    @Test
    void contentTypeCountryBreakdown_multipleMetrics() {
        IsrcContentTypeCountryTikTokTrackAnalyticsBreakdowns breakdowns = aggregator
            .contentTypeCountryBreakdown(Stream.of(
                createCountryMetrics(TikTokContentType.PGC, US, 100L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, US, 100L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, US, 100L, DATE2, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, US, 100L, DATE2, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, GB, 100L, DATE1, TEST_ISRC)
            ), Collections.emptySet(), ISRC_MAPPING_WITHOUT_RELATED);

        assertEquals(2, breakdowns.getPgc().get(US).getComments().size());
        assertEquals(100L, breakdowns.getPgc().get(US).getComments().get(0));

        assertEquals(2, breakdowns.getUgc().get(US).getComments().size());
        assertEquals(2, breakdowns.getUgc().get(GB).getComments().size());
        assertEquals(100L, breakdowns.getUgc().get(US).getComments().get(0));
        assertEquals(200L, breakdowns.getUgc().get(US).getComments().get(1));
        assertEquals(100L, breakdowns.getUgc().get(GB).getComments().get(0));
    }


    @Test
    void contentTypeCountryBreakdown_multipleMetricsWithGap() {
        IsrcContentTypeCountryTikTokTrackAnalyticsBreakdowns breakdowns = aggregator
            .contentTypeCountryBreakdown(Stream.of(
                createCountryMetrics(TikTokContentType.UGC, US, 100L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, US, 100L, DATE3, TEST_ISRC)
            ), Collections.emptySet(), ISRC_MAPPING_WITHOUT_RELATED);

        assertEquals(3, breakdowns.getUgc().get(US).getComments().size());
        assertEquals(100L, breakdowns.getUgc().get(US).getComments().get(0));
        assertNull(breakdowns.getUgc().get(US).getComments().get(1));
        assertEquals(100, breakdowns.getUgc().get(US).getComments().get(2));
    }


    @Test
    void contentTypeCountryBreakdown_minMaxDate() {
        IsrcContentTypeCountryTikTokTrackAnalyticsBreakdowns breakdowns = aggregator
            .contentTypeCountryBreakdown(Stream.of(
                createCountryMetrics(TikTokContentType.PGC, US, 100L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, US, 100L, DATE2, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, US, 100L, DATE2, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, US, 100L, DATE2, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, US, 100L, DATE3, TEST_ISRC)
            ), Collections.emptySet(), ISRC_MAPPING_WITHOUT_RELATED);

        assertEquals(DATE1, breakdowns.getMinDate());
        assertEquals(DATE3, breakdowns.getMaxDate());
    }

    @Test
    void dailyBreakdown_ugcAndPgc() {
        IsrcContentTypeCountryTikTokTrackAnalyticsBreakdowns contentTypeCountryBreakdown = aggregator
            .contentTypeCountryBreakdown(Stream.of(
                createCountryMetrics(TikTokContentType.PGC, US, 100L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.PGC, US, 200L, DATE2, TEST_ISRC),

                createCountryMetrics(TikTokContentType.PGC, GB, 100L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.PGC, GB, 200L, DATE2, TEST_ISRC),

                createCountryMetrics(TikTokContentType.UGC, UA, 100L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, UA, 200L, DATE2, TEST_ISRC),

                createCountryMetrics(TikTokContentType.UGC, GB, 100L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, GB, 200L, DATE2, TEST_ISRC)
            ), null, ISRC_MAPPING_WITHOUT_RELATED);

        TikTokTrackAnalyticsRangeMetrics daily = aggregator.dailyBreakdown(contentTypeCountryBreakdown, null);
        assertEquals(2, daily.getComments().size());
        assertEquals(400L, daily.getComments().get(0));
        assertEquals(800L, daily.getComments().get(1));
    }

    @Test
    void dailyBreakdown_ugcAndPgcWighGaps() {
        IsrcContentTypeCountryTikTokTrackAnalyticsBreakdowns contentTypeCountryBreakdown = aggregator
            .contentTypeCountryBreakdown(Stream.of(
                createCountryMetrics(TikTokContentType.PGC, US, 100L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.PGC, US, 200L, DATE3, TEST_ISRC),

                createCountryMetrics(TikTokContentType.PGC, GB, 100L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.PGC, GB, 200L, DATE3, TEST_ISRC),

                createCountryMetrics(TikTokContentType.UGC, UA, 100L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, UA, 200L, DATE3, TEST_ISRC),

                createCountryMetrics(TikTokContentType.UGC, GB, 100L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, GB, 200L, DATE3, TEST_ISRC)
            ), null, ISRC_MAPPING_WITHOUT_RELATED);
        TikTokTrackAnalyticsRangeMetrics daily = aggregator
            .dailyBreakdown(contentTypeCountryBreakdown, null);
        assertEquals(3, daily.getComments().size());
        assertEquals(400L, daily.getComments().get(0));
        assertEquals(800L, daily.getComments().get(2));
    }

    @Test
    void countryTotalsBreakdown() {
        IsrcContentTypeCountryTikTokTrackAnalyticsBreakdowns contentTypeCountryBreakdown = aggregator
            .contentTypeCountryBreakdown(Stream.of(
                createCountryMetrics(TikTokContentType.PGC, US, 101L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.PGC, US, 205L, DATE3, TEST_ISRC),

                createCountryMetrics(TikTokContentType.PGC, MX, 103L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.PGC, MX, 204L, DATE3, TEST_ISRC),

                createCountryMetrics(TikTokContentType.UGC, UA, 100L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, UA, 200L, DATE3, TEST_ISRC),

                createCountryMetrics(TikTokContentType.PGC, UA, 19L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.PGC, UA, 21L, DATE2, TEST_ISRC),

                createCountryMetrics(TikTokContentType.UGC, MX, 102L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, MX, 208L, DATE3, TEST_ISRC)
            ), null, ISRC_MAPPING_WITHOUT_RELATED);

        Map<String, TikTokTrackAnalyticsMetrics> result = aggregator
            .countryTotalsBreakdown(contentTypeCountryBreakdown);

        assertEquals(617L, result.get(MX).getLikes());
        assertEquals(306L, result.get(US).getLikes());
        assertEquals(340L, result.get(UA).getLikes());
    }


    @Test
    void countryDailyBreakdown() {
        IsrcContentTypeCountryTikTokTrackAnalyticsBreakdowns contentTypeCountryBreakdown = aggregator
            .contentTypeCountryBreakdown(Stream.of(
                createCountryMetrics(TikTokContentType.PGC, US, 101L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.PGC, US, 205L, DATE3, TEST_ISRC),

                createCountryMetrics(TikTokContentType.PGC, MX, 103L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.PGC, MX, 204L, DATE3, TEST_ISRC),

                createCountryMetrics(TikTokContentType.UGC, UA, 100L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, UA, 200L, DATE3, TEST_ISRC),

                createCountryMetrics(TikTokContentType.PGC, UA, 19L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.PGC, UA, 21L, DATE2, TEST_ISRC),

                createCountryMetrics(TikTokContentType.UGC, MX, 102L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, MX, 208L, DATE3, TEST_ISRC)
            ), null, ISRC_MAPPING_WITHOUT_RELATED);

        Map<String, TikTokTrackAnalyticsRangeMetrics> result = aggregator
            .countryDailyBreakdown(contentTypeCountryBreakdown);

        assertEquals(Lists.newArrayList(103 + 102L, null, 204 + 208L), result.get(MX).getLikes());
        assertEquals(Lists.newArrayList(101L, null, 205L), result.get(US).getLikes());
        assertEquals(Lists.newArrayList(100L + 19L, 21L, 200L), result.get(UA).getLikes());
    }

    @Test
    void countryDailyBreakdown_multipleIsrcIntoSingleItem() {
        IsrcContentTypeCountryTikTokTrackAnalyticsBreakdowns contentTypeCountryBreakdown = aggregator
            .contentTypeCountryBreakdown(Stream.of(
                createCountryMetrics(TikTokContentType.PGC, US, 1L, DATE1, TEST_ISRC),
                createCountryMetrics(TikTokContentType.UGC, US, 1L, DATE1, TEST_ISRC),

                // new isrc should be summed with existing
                createCountryMetrics(TikTokContentType.PGC, US, 1L, DATE1, TEST_ISRC2),
                createCountryMetrics(TikTokContentType.UGC, US, 1L, DATE1, TEST_ISRC2),

                // new isrc introduces new date
                createCountryMetrics(TikTokContentType.PGC, US, 1L, DATE3, TEST_ISRC2),
                createCountryMetrics(TikTokContentType.UGC, US, 1L, DATE3, TEST_ISRC2),

                // new isrc introduces new country code
                createCountryMetrics(TikTokContentType.PGC, UA, 1L, DATE3, TEST_ISRC2),
                createCountryMetrics(TikTokContentType.UGC, UA, 1L, DATE3, TEST_ISRC2)

            ), null, ISRC_MAPPING_WITH_RELATED);

        Map<String, TikTokTrackAnalyticsRangeMetrics> result = aggregator
            .countryDailyBreakdown(contentTypeCountryBreakdown);

        assertEquals(Lists.newArrayList(4L, null, 2L), result.get(US).getLikes());
        assertEquals(Lists.newArrayList(null, null, 2L), result.get(UA).getLikes());
    }

    private TikTokTrackCountryMetrics createCountryMetrics(TikTokContentType contentType, String countryCode,
        long metricValue, LocalDate date, String isrc) {
        return new TikTokTrackCountryMetrics(date, isrc,
            contentType,
            countryCode,
            TikTokStats.newBuilder()
                .setComments(metricValue)
                .setCreations(metricValue)
                .setShares(metricValue)
                .setLikes(metricValue)
                .setVideoViews(metricValue)
                .build());
    }
}
