package io.delphiplatform.api.integration;

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

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import io.delphiplatform.api.integration.annotation.BigTable;
import io.delphiplatform.api.integration.util.MockCurrentDateService;
import io.delphiplatform.api.v3.constant.DspConstants;

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

public class TrackStreamsInsightsIT extends BaseIT {

    /**
     * Series of streams insights tests on the common dataset.
     * <p>
     * 5 weeks streams dataset collected for DSP of 'apple', 'amazon', 'spotify' for single ISRC. Amazon dataset for CA market, all others for US
     * market. Testing full dataset for specific ISRC and for filters combination for 'country code' and 'dsp'
     * <p>
     * case1 - DSP not specified, aggregation per-dsp plus for "all" dsps
     * <p>
     * case2 - DSPs specified, aggregation per-dsp
     */

    @Test
    @BigTable(table = "spotify", json = "/scripts/TrackStreamsInsightsIT/BT_spotify_common_test_data.json")
    @BigTable(table = "apple_music", json = "/scripts/TrackStreamsInsightsIT/BT_apple_music_common_test_data.json")
    @BigTable(table = "amazon_music", json = "/scripts/TrackStreamsInsightsIT/BT_amazon_music_common_test_data.json")
    public void streamsInsights_full() {
        MockCurrentDateService.setMockDateTime(LocalDateTime.parse("2023-01-31T00:00:00"));
        String isrc = "USRC11301214";

        //case1

        ResponseEntity<Map> response1 = apiClient.sendAPIRequest(
            "/v3/streams/insights?"
                + "isrc=" + isrc,
            Map.class
        );

        assertEquals(HttpStatus.OK, response1.getStatusCode());

        Map<String, List<Map<String, Object>>> result1 = response1.getBody();
        assertEquals(4, result1.size());

        List<Map<String, Object>> all_dsps = result1.get("all_dsps");
        Optional<Map<String, Object>> allDspsSingleCountry = all_dsps.stream()
            .filter(p -> p.get("country_code").equals("us"))
            .findFirst();
        assertTrue(allDspsSingleCountry.isPresent());
        assertTrue(isInsightsEqual("2023-01-03", 100, 2, "us", allDspsSingleCountry.get()));
        allDspsSingleCountry = all_dsps.stream()
            .filter(p -> p.get("country_code").equals("ca"))
            .findFirst();
        assertTrue(allDspsSingleCountry.isPresent());
        assertTrue(isInsightsEqual("2023-01-27", 99, 2, "ca", allDspsSingleCountry.get()));

        List<Map<String, Object>> amazon1 = result1.get("amazon");
        assertEquals(1, amazon1.size());
        assertTrue(isInsightsEqual("2023-01-27", 99, 2, "ca", amazon1.get(0)));

        List<Map<String, Object>> apple1 = result1.get("apple");
        assertEquals(1, apple1.size());
        assertTrue(isInsightsEqual("2023-01-03", 89, 2, "us", apple1.get(0)));

        List<Map<String, Object>> spotify1 = result1.get("spotify");
        assertEquals(1, spotify1.size());
        assertTrue(isInsightsEqual("2023-01-19", 34, 0, "us", spotify1.get(0)));

        //case2

        ResponseEntity<Map> response2 = apiClient.sendAPIRequest(
            "/v3/streams/insights?"
                + "isrc=" + isrc
                + "&dsp=" + String.format("%s,%s,%s", DspConstants.SPOTIFY, DspConstants.APPLE, DspConstants.AMAZON),
            Map.class
        );

        assertEquals(HttpStatus.OK, response2.getStatusCode());

        Map<String, List<Map<String, Object>>> result2 = response2.getBody();
        assertEquals(3, result2.size());

        List<Map<String, Object>> amazon2 = result2.get("amazon");
        assertEquals(1, amazon2.size());
        assertTrue(isInsightsEqual("2023-01-27", 99, 2, "ca", amazon2.get(0)));

        List<Map<String, Object>> apple2 = result2.get("apple");
        assertEquals(1, apple2.size());
        assertTrue(isInsightsEqual("2023-01-03", 89, 2, "us", apple2.get(0)));

        List<Map<String, Object>> spotify2 = result2.get("spotify");
        assertEquals(1, spotify2.size());
        assertTrue(isInsightsEqual("2023-01-19", 34, 0, "us", spotify2.get(0)));
    }

    @Test
    @BigTable(table = "spotify", json = "/scripts/TrackStreamsInsightsIT/BT_spotify_common_test_data.json")
    @BigTable(table = "apple_music", json = "/scripts/TrackStreamsInsightsIT/BT_apple_music_common_test_data.json")
    @BigTable(table = "amazon_music", json = "/scripts/TrackStreamsInsightsIT/BT_amazon_music_common_test_data.json")
    public void streamsInsights_filter_by_country() {
        MockCurrentDateService.setMockDateTime(LocalDateTime.parse("2023-01-31T00:00:00"));
        String isrc = "USRC11301214";
        String countryCode = "us";

        //case1

        ResponseEntity<Map> response1 = apiClient.sendAPIRequest(
            "/v3/streams/insights?"
                + "isrc=" + isrc
                + "&country_code=" + countryCode,
            Map.class
        );

        assertEquals(HttpStatus.OK, response1.getStatusCode());

        Map<String, List<Map<String, Object>>> result1 = response1.getBody();
        assertEquals(4, result1.size());

        List<Map<String, Object>> all_dsps = result1.get("all_dsps");
        assertEquals(1, all_dsps.size());
        assertTrue(isInsightsEqual("2023-01-03", 100, 2, "us", all_dsps.get(0)));

        assertEquals(0, result1.get("amazon").size());

        List<Map<String, Object>> apple1 = result1.get("apple");
        assertEquals(1, apple1.size());
        assertTrue(isInsightsEqual("2023-01-03", 89, 2, "us", apple1.get(0)));

        List<Map<String, Object>> spotify1 = result1.get("spotify");
        assertEquals(1, spotify1.size());
        assertTrue(isInsightsEqual("2023-01-19", 34, 0, "us", spotify1.get(0)));

        //case2

        ResponseEntity<Map> response2 = apiClient.sendAPIRequest(
            "/v3/streams/insights?"
                + "isrc=" + isrc
                + "&country_code=" + countryCode
                + "&dsp=" + String.format("%s,%s,%s", DspConstants.SPOTIFY, DspConstants.APPLE, DspConstants.AMAZON),
            Map.class
        );

        assertEquals(HttpStatus.OK, response2.getStatusCode());

        Map<String, List<Map<String, Object>>> result2 = response2.getBody();
        assertEquals(3, result2.size());

        assertEquals(0, result2.get("amazon").size());

        List<Map<String, Object>> apple2 = result2.get("apple");
        assertEquals(1, apple2.size());
        assertTrue(isInsightsEqual("2023-01-03", 89, 2, "us", apple2.get(0)));

        List<Map<String, Object>> spotify2 = result2.get("spotify");
        assertEquals(1, spotify2.size());
        assertTrue(isInsightsEqual("2023-01-19", 34, 0, "us", spotify2.get(0)));
    }

    /**
     * `Strike Weeks` calculatiion on data with data gap.
     * <p>
     * Apple data for last 6 weeks. Amazon, Spotify missing data for the latest week. For ALL_DSP `Strike Weeks` should calculate on all present
     * DSP, so last week Apple data not taked into account.
     * <p>
     * case1 - DSP not specified, aggregation per-dsp plus for "all" dsps
     * <p>
     * case2 - DSPs specified, aggregation per-dsp
     */
    @Test
    @BigTable(table = "spotify", json = "/scripts/TrackStreamsInsightsIT/BT_spotify_common_test_data.json")
    @BigTable(table = "apple_music", json = {"/scripts/TrackStreamsInsightsIT/BT_apple_music_common_test_data.json",
        "/scripts/TrackStreamsInsightsIT/BT_apple_music_streamsInsights_full_with_data_gap.json"})
    @BigTable(table = "amazon_music", json = "/scripts/TrackStreamsInsightsIT/BT_amazon_music_common_test_data.json")
    public void streamsInsights_full_with_data_gap() {
        MockCurrentDateService.setMockDateTime(LocalDateTime.parse("2023-02-04T00:00:00"));
        String isrc = "USRC11301214";

        //case1

        ResponseEntity<Map> response1 = apiClient.sendAPIRequest(
            "/v3/streams/insights?"
                + "isrc=" + isrc,
            Map.class
        );

        assertEquals(HttpStatus.OK, response1.getStatusCode());

        Map<String, List<Map<String, Object>>> result1 = response1.getBody();
        assertEquals(4, result1.size());

        List<Map<String, Object>> all_dsps = result1.get("all_dsps");
        Optional<Map<String, Object>> allDspsSingleCountry = all_dsps.stream()
            .filter(p -> p.get("country_code").equals("us"))
            .findFirst();
        assertTrue(allDspsSingleCountry.isPresent());
        assertTrue(isInsightsEqual("2023-01-03", 100, 2, "us", allDspsSingleCountry.get()));
        allDspsSingleCountry = all_dsps.stream()
            .filter(p -> p.get("country_code").equals("ca"))
            .findFirst();
        assertTrue(allDspsSingleCountry.isPresent());
        assertTrue(isInsightsEqual("2023-01-27", 99, 2, "ca", allDspsSingleCountry.get()));

        List<Map<String, Object>> amazon1 = result1.get("amazon");
        assertEquals(1, amazon1.size());
        assertTrue(isInsightsEqual("2023-01-27", 99, 2, "ca", amazon1.get(0)));

        List<Map<String, Object>> apple1 = result1.get("apple");
        assertEquals(1, apple1.size());
        assertTrue(isInsightsEqual("2023-01-03", 89, 3, "us", apple1.get(0)));

        List<Map<String, Object>> spotify1 = result1.get("spotify");
        assertEquals(1, spotify1.size());
        assertTrue(isInsightsEqual("2023-01-19", 34, 0, "us", spotify1.get(0)));

        //case2

        ResponseEntity<Map> response2 = apiClient.sendAPIRequest(
            "/v3/streams/insights?"
                + "isrc=" + isrc
                + "&dsp=" + String.format("%s,%s,%s", DspConstants.SPOTIFY, DspConstants.APPLE, DspConstants.AMAZON),
            Map.class
        );

        assertEquals(HttpStatus.OK, response2.getStatusCode());

        Map<String, List<Map<String, Object>>> result2 = response2.getBody();
        assertEquals(3, result2.size());

        List<Map<String, Object>> amazon2 = result2.get("amazon");
        assertEquals(1, amazon2.size());
        assertTrue(isInsightsEqual("2023-01-27", 99, 2, "ca", amazon2.get(0)));

        List<Map<String, Object>> apple2 = result2.get("apple");
        assertEquals(1, apple2.size());
        assertTrue(isInsightsEqual("2023-01-03", 89, 3, "us", apple2.get(0)));

        List<Map<String, Object>> spotify2 = result2.get("spotify");
        assertEquals(1, spotify2.size());
        assertTrue(isInsightsEqual("2023-01-19", 34, 0, "us", spotify2.get(0)));
    }

    private static boolean isInsightsEqual(String streamDate, Integer streams, Integer strikeWeeks, String country, Map<String, ?> ref) {
        return ref.get("peak_streams_date").equals(streamDate) &&
            ref.get("peak_streams_number").equals(streams) &&
            ref.get("strike_weeks").equals(strikeWeeks) &&
            ref.get("country_code").equals(country);
    }
}
