package io.delphiplatform.api.integration;

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

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import io.delphiplatform.api.integration.annotation.BigTable;
import io.delphiplatform.api.integration.annotation.SqlScript;
import io.delphiplatform.api.integration.model.PublicPlaylistFollowersModel;
import io.delphiplatform.api.integration.model.PublicPlaylistsFollowersModel;
import io.delphiplatform.api.integration.util.MockCurrentDateService;

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

public class PublicPlaylistFollowersIT extends BaseIT {

    /**
     * <!-- @formatter:off -->
     * If just current date is requested - followers value is taken from PG, without requesting BT at all.
     * For playlist 1 (spotify_07TMuI4hFRURvHh5ZgG4YK) - we have data in PG
     * For playlist 2 (spotify_07TMuI4hFRURvHh5ZgG4YO) - we DONT have data in PG (followers value is null)
     * For playlist 3 (spotify_07TMuI4hFRURvHh5ZgG4YL) - we DONT have any data in PG (no record)
     * So only playlist 1 is returned
     * <!-- @formatter:on -->
     */
    @Test
    @SqlScript(scripts = "/scripts/PublicPlaylistFollowersIT/getPlaylistFollowers_spotify_currentDate.sql")
    public void getPlaylistFollowers_spotify_currentDate() {
        String currentDateStr = "2023-02-15";
        MockCurrentDateService.setMockDateTime(LocalDateTime.parse(currentDateStr + "T00:00:00"));

        String playlistId1 = "spotify_07TMuI4hFRURvHh5ZgG4YK";
        String playlistId2 = "spotify_07TMuI4hFRURvHh5ZgG4YO";
        String playlistId3 = "spotify_07TMuI4hFRURvHh5ZgG4YL";

        ResponseEntity<PublicPlaylistsFollowersModel> response = apiClient.sendAPIRequest(
            "/v3/public/playlists/followers?"
                + "playlist_id=" + String.join(",", playlistId1, playlistId2, playlistId3)
                + "&start_date=" + currentDateStr
                + "&end_date=" + currentDateStr,
            PublicPlaylistsFollowersModel.class
        );

        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertEquals(1, response.getBody().getCount());

        List<PublicPlaylistFollowersModel> followers = response.getBody().getItems();

        PublicPlaylistFollowersModel playlistFollowers = followers.stream()
            .filter(model -> playlistId1.equals(model.getPlaylistId()))
            .findAny().orElse(null);
        assertNotNull(playlistFollowers);

        assertEquals(LocalDate.parse(currentDateStr), playlistFollowers.getStartDate());
        assertEquals(LocalDate.parse(currentDateStr), playlistFollowers.getEndDate());
        assertEquals(List.of(19L), playlistFollowers.getFollowers());
    }

    /**
     * <!-- @formatter:off -->
     * 2 dates - current and previous are requested. We look for previous dates in BT.
     * But no data found in BT at all, nevertheless we want to look in PG for current date
     *
     * For playlist 1 (spotify_07TMuI4hFRURvHh5ZgG4YK) - we have data in PG
     * For playlist 2 (spotify_07TMuI4hFRURvHh5ZgG4YO) - we DONT have data in PG (followers value is null)
     * For playlist 3 (spotify_07TMuI4hFRURvHh5ZgG4YL) - we DONT have any data in PG (no record)
     * So only playlist 1 is returned
     * <!-- @formatter:on -->
     */
    @Test
    @BigTable(table = "spotify_fact_playlist_followers_daily", json = "/scripts/BT_empty.json")
    @SqlScript(scripts = "/scripts/PublicPlaylistFollowersIT/getPlaylistFollowers_spotify_currentAndPreviousDates_nothingInBT.sql")
    public void getPlaylistFollowers_spotify_currentAndPreviousDates_nothingInBT() {
        String prevDateStr = "2023-02-14";
        String currentDateStr = "2023-02-15";
        MockCurrentDateService.setMockDateTime(LocalDateTime.parse(currentDateStr + "T00:00:00"));

        String playlistId1 = "spotify_07TMuI4hFRURvHh5ZgG4YK";
        String playlistId2 = "spotify_07TMuI4hFRURvHh5ZgG4YO";
        String playlistId3 = "spotify_07TMuI4hFRURvHh5ZgG4YL";

        ResponseEntity<PublicPlaylistsFollowersModel> response = apiClient.sendAPIRequest(
            "/v3/public/playlists/followers?"
                + "playlist_id=" + String.join(",", playlistId1, playlistId2, playlistId3)
                + "&start_date=" + prevDateStr
                + "&end_date=" + currentDateStr,
            PublicPlaylistsFollowersModel.class
        );

        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertEquals(1, response.getBody().getCount());

        List<PublicPlaylistFollowersModel> followers = response.getBody().getItems();

        PublicPlaylistFollowersModel playlistFollowers = followers.stream()
            .filter(model -> playlistId1.equals(model.getPlaylistId()))
            .findAny().orElse(null);
        assertNotNull(playlistFollowers);

        assertEquals(LocalDate.parse(currentDateStr), playlistFollowers.getStartDate());
        assertEquals(LocalDate.parse(currentDateStr), playlistFollowers.getEndDate());
        assertEquals(List.of(19L), playlistFollowers.getFollowers());
    }

    /**
     * <!-- @formatter:off -->
     * 2 dates - current and previous are requested. We look for previous dates in BT.
     *
     * For playlist 1 (spotify_07TMuI4hFRURvHh5ZgG4YK) - we have some BT data (10 followers for previous date), and current date followers (19) are filled from PG
     * For playlist 2 (spotify_07TMuI4hFRURvHh5ZgG4YO) - we DONT have any BT data at requested dates. So we try to at least fill current date followers from PG (20)
     * For playlist 2 (spotify_07TMuI4hFRURvHh5ZgG4YL) - we DONT have either BT or PG data (PG playlist exists but has null followers) - skip playlist from response
     * <!-- @formatter:on -->
     */
    @Test
    @BigTable(table = "spotify_fact_playlist_followers_daily", json =
        "/scripts/PublicPlaylistFollowersIT/BT_spotify_fact_playlist_followers_daily_getPlaylistFollowers_spotify_currentAndPreviousDates.json")
    @SqlScript(scripts = "/scripts/PublicPlaylistFollowersIT/getPlaylistFollowers_spotify_currentAndPreviousDates.sql")
    public void getPlaylistFollowers_spotify_currentAndPreviousDates() {
        String prevDateStr = "2023-02-14";
        String currentDateStr = "2023-02-15";
        MockCurrentDateService.setMockDateTime(LocalDateTime.parse(currentDateStr + "T00:00:00"));

        String playlistId1 = "spotify_07TMuI4hFRURvHh5ZgG4YK";
        String playlistId2 = "spotify_07TMuI4hFRURvHh5ZgG4YO";
        String playlistId3 = "spotify_07TMuI4hFRURvHh5ZgG4YL";

        ResponseEntity<PublicPlaylistsFollowersModel> response = apiClient.sendAPIRequest(
            "/v3/public/playlists/followers?"
                + "playlist_id=" + String.join(",", playlistId1, playlistId2, playlistId3)
                + "&start_date=" + prevDateStr
                + "&end_date=" + currentDateStr,
            PublicPlaylistsFollowersModel.class
        );

        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertEquals(2, response.getBody().getCount());

        List<PublicPlaylistFollowersModel> followers = response.getBody().getItems();

        PublicPlaylistFollowersModel playlistFollowers1 = followers.stream()
            .filter(model -> playlistId1.equals(model.getPlaylistId()))
            .findAny().orElse(null);
        assertNotNull(playlistFollowers1);

        assertEquals(LocalDate.parse(prevDateStr), playlistFollowers1.getStartDate());
        assertEquals(LocalDate.parse(currentDateStr), playlistFollowers1.getEndDate());
        assertEquals(List.of(10L, 19L), playlistFollowers1.getFollowers());

        PublicPlaylistFollowersModel playlistFollowers2 = followers.stream()
            .filter(model -> playlistId2.equals(model.getPlaylistId()))
            .findAny().orElse(null);
        assertNotNull(playlistFollowers2);

        assertEquals(LocalDate.parse(currentDateStr), playlistFollowers2.getStartDate());
        assertEquals(LocalDate.parse(currentDateStr), playlistFollowers2.getEndDate());
        assertEquals(List.of(20L), playlistFollowers2.getFollowers());
    }

    /**
     *  <!-- @formatter:off -->
     * Check followers array truncation of null values / dates range narrowing
     * Use previous dates
     *
     * CASE1:
     * dates: 01-03
     * followers: [null, null, 10]
     * result: truncate, return dates: 03-03, followers: [10]
     *
     * CASE2:
     * dates: 03-05
     * followers: [10, null, null]
     * result: truncate, return dates: 03-03, followers: [10]
     *
     * CASE3:
     * dates: 05-09
     * followers: [null, 11, null, 12, null]
     * result: truncate, return dates: 06-08, followers: [11, null, 12]
     *  <!-- @formatter:on -->
     */
    @Test
    @BigTable(table = "spotify_fact_playlist_followers_daily", json =
        "/scripts/PublicPlaylistFollowersIT/BT_spotify_fact_playlist_followers_daily_getPlaylistFollowers_spotify_truncation_previousDates.json")
    public void getPlaylistFollowers_spotify_truncation_previousDates() {
        MockCurrentDateService.setMockDateTime(LocalDateTime.parse("2023-02-15T00:00:00"));

        String playlistId1 = "spotify_07TMuI4hFRURvHh5ZgG4YK";

        //CASE1:
        ResponseEntity<PublicPlaylistsFollowersModel> response1 = apiClient.sendAPIRequest(
            "/v3/public/playlists/followers?"
                + "playlist_id=" + playlistId1
                + "&start_date=" + "2023-02-01"
                + "&end_date=" + "2023-02-03",
            PublicPlaylistsFollowersModel.class
        );

        assertEquals(HttpStatus.OK, response1.getStatusCode());
        assertEquals(1, response1.getBody().getCount());

        List<PublicPlaylistFollowersModel> followers1 = response1.getBody().getItems();

        PublicPlaylistFollowersModel playlistFollowers1 = followers1.stream()
            .filter(model -> playlistId1.equals(model.getPlaylistId()))
            .findAny().orElse(null);
        assertNotNull(playlistFollowers1);

        assertEquals(LocalDate.parse("2023-02-03"), playlistFollowers1.getStartDate());
        assertEquals(LocalDate.parse("2023-02-03"), playlistFollowers1.getEndDate());
        assertEquals(List.of(10L), playlistFollowers1.getFollowers());

        //CASE2:
        ResponseEntity<PublicPlaylistsFollowersModel> response2 = apiClient.sendAPIRequest(
            "/v3/public/playlists/followers?"
                + "playlist_id=" + playlistId1
                + "&start_date=" + "2023-02-03"
                + "&end_date=" + "2023-02-05",
            PublicPlaylistsFollowersModel.class
        );

        assertEquals(HttpStatus.OK, response2.getStatusCode());
        assertEquals(1, response2.getBody().getCount());

        List<PublicPlaylistFollowersModel> followers2 = response2.getBody().getItems();

        PublicPlaylistFollowersModel playlistFollowers2 = followers2.stream()
            .filter(model -> playlistId1.equals(model.getPlaylistId()))
            .findAny().orElse(null);
        assertNotNull(playlistFollowers2);

        assertEquals(LocalDate.parse("2023-02-03"), playlistFollowers2.getStartDate());
        assertEquals(LocalDate.parse("2023-02-03"), playlistFollowers2.getEndDate());
        assertEquals(List.of(10L), playlistFollowers2.getFollowers());

        //CASE3:
        ResponseEntity<PublicPlaylistsFollowersModel> response3 = apiClient.sendAPIRequest(
            "/v3/public/playlists/followers?"
                + "playlist_id=" + playlistId1
                + "&start_date=" + "2023-02-05"
                + "&end_date=" + "2023-02-09",
            PublicPlaylistsFollowersModel.class
        );

        assertEquals(HttpStatus.OK, response3.getStatusCode());
        assertEquals(1, response3.getBody().getCount());

        List<PublicPlaylistFollowersModel> followers3 = response3.getBody().getItems();

        PublicPlaylistFollowersModel playlistFollowers3 = followers3.stream()
            .filter(model -> playlistId1.equals(model.getPlaylistId()))
            .findAny().orElse(null);
        assertNotNull(playlistFollowers3);

        assertEquals(LocalDate.parse("2023-02-06"), playlistFollowers3.getStartDate());
        assertEquals(LocalDate.parse("2023-02-08"), playlistFollowers3.getEndDate());
        List<Long> expectedFollowers = new ArrayList<>();
        expectedFollowers.add(11L);
        expectedFollowers.add(null);
        expectedFollowers.add(12L);
        assertEquals(expectedFollowers, playlistFollowers3.getFollowers());
    }

    /**
     *  <!-- @formatter:off -->
     * Check followers array truncation of null values / dates range narrowing
     * Use current date (03)
     *
     * CASE1:
     * dates: 01-03
     * followers: [null, null, 19] - nulls in BT, '19' in PG that is used for current date population
     * result: truncate, return dates: 03-03, followers: [19]
     *  <!-- @formatter:on -->
     */
    @Test
    @SqlScript(scripts = "/scripts/PublicPlaylistFollowersIT/getPlaylistFollowers_spotify_truncation_currentDate.sql")
    public void getPlaylistFollowers_spotify_truncation_currentDate() {
        MockCurrentDateService.setMockDateTime(LocalDateTime.parse("2023-02-03T00:00:00"));

        String playlistId1 = "spotify_07TMuI4hFRURvHh5ZgG4YK";

        //CASE1:
        ResponseEntity<PublicPlaylistsFollowersModel> response1 = apiClient.sendAPIRequest(
            "/v3/public/playlists/followers?"
                + "playlist_id=" + playlistId1
                + "&start_date=" + "2023-02-01"
                + "&end_date=" + "2023-02-03",
            PublicPlaylistsFollowersModel.class
        );

        assertEquals(HttpStatus.OK, response1.getStatusCode());
        assertEquals(1, response1.getBody().getCount());

        List<PublicPlaylistFollowersModel> followers1 = response1.getBody().getItems();

        PublicPlaylistFollowersModel playlistFollowers1 = followers1.stream()
            .filter(model -> playlistId1.equals(model.getPlaylistId()))
            .findAny().orElse(null);
        assertNotNull(playlistFollowers1);

        assertEquals(LocalDate.parse("2023-02-03"), playlistFollowers1.getStartDate());
        assertEquals(LocalDate.parse("2023-02-03"), playlistFollowers1.getEndDate());
        assertEquals(List.of(19L), playlistFollowers1.getFollowers());
    }

    /**
     *  <!-- @formatter:off -->
     * Check followers array truncation of null values / dates range narrowing
     * Use current date (03)
     *
     * CASE1:
     * dates: 01-03
     * followers: [null, 10, null] - null and '10' in BT, null in PG that is used for current date population
     * result: truncate, return dates: 02-02, followers: [10]
     *  <!-- @formatter:on -->
     */
    @Test
    @BigTable(table = "spotify_fact_playlist_followers_daily", json =
        "/scripts/PublicPlaylistFollowersIT/BT_spotify_fact_playlist_followers_daily_getPlaylistFollowers_spotify_truncation_currentDateNullValue.json")
    @SqlScript(scripts = "/scripts/PublicPlaylistFollowersIT/getPlaylistFollowers_spotify_truncation_currentDateNullValue.sql")
    public void getPlaylistFollowers_spotify_truncation_currentDateNullValue() {
        MockCurrentDateService.setMockDateTime(LocalDateTime.parse("2023-02-03T00:00:00"));

        String playlistId1 = "spotify_07TMuI4hFRURvHh5ZgG4YK";

        //CASE1:
        ResponseEntity<PublicPlaylistsFollowersModel> response1 = apiClient.sendAPIRequest(
            "/v3/public/playlists/followers?"
                + "playlist_id=" + playlistId1
                + "&start_date=" + "2023-02-01"
                + "&end_date=" + "2023-02-03",
            PublicPlaylistsFollowersModel.class
        );

        assertEquals(HttpStatus.OK, response1.getStatusCode());
        assertEquals(1, response1.getBody().getCount());

        List<PublicPlaylistFollowersModel> followers1 = response1.getBody().getItems();

        PublicPlaylistFollowersModel playlistFollowers1 = followers1.stream()
            .filter(model -> playlistId1.equals(model.getPlaylistId()))
            .findAny().orElse(null);
        assertNotNull(playlistFollowers1);

        assertEquals(LocalDate.parse("2023-02-02"), playlistFollowers1.getStartDate());
        assertEquals(LocalDate.parse("2023-02-02"), playlistFollowers1.getEndDate());
        assertEquals(List.of(10L), playlistFollowers1.getFollowers());
    }
}
