/*
 * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *     http://aws.amazon.com/apache2.0/
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
#ifndef ALEXA_CLIENT_SDK_PLAYLISTPARSER_TEST_PLAYLISTPARSER_MOCKCONTENTFETCHER_H_
#define ALEXA_CLIENT_SDK_PLAYLISTPARSER_TEST_PLAYLISTPARSER_MOCKCONTENTFETCHER_H_

#include <string>
#include <vector>
#include <unordered_map>

#include <AVSCommon/SDKInterfaces/HTTPContentFetcherInterface.h>

namespace alexaClientSDK {
namespace playlistParser {
namespace test {

/// HTTP OK response code which indicates that the request has succeeded.
static const int HTTP_REQUEST_OK = 200;

/// Short time out for when callbacks are expected not to occur.
static const auto SHORT_TIMEOUT = std::chrono::milliseconds(50);

/// Test M3U url.
static const std::string TEST_M3U_PLAYLIST_URL{"http://sanjayisthecoolest.com/sample.m3u"};

static const std::string TEST_M3U_PLAYLIST_CONTENT =
    "http://stream.radiotime.com/sample.mp3\n"
    "http://live-mp3-128.kexp.org\n";

static const size_t TEST_M3U_PLAYLIST_URL_EXPECTED_PARSES = 2;

static const std::vector<std::chrono::milliseconds> TEST_M3U_DURATIONS = {std::chrono::milliseconds{-1},
                                                                          std::chrono::milliseconds{-1}};

static const std::vector<std::string> TEST_M3U_PLAYLIST_URLS = {"http://stream.radiotime.com/sample.mp3",
                                                                "http://live-mp3-128.kexp.org"};

/// Test M3U url with relative urls within.
static const std::string TEST_M3U_RELATIVE_PLAYLIST_URL{"http://sanjayisthecoolest.com/sampleRelativeUrls.m3u"};

static const std::string TEST_M3U_RELATIVE_PLAYLIST_CONTENT =
    "../test.mp3\n"
    "test2.aac\n";

static const size_t TEST_M3U_RELATIVE_PLAYLIST_URL_EXPECTED_PARSES = 2;

static const std::vector<std::string> TEST_M3U_RELATIVE_PLAYLIST_URLS = {"http://sanjayisthecoolest.com/../test.mp3",
                                                                         "http://sanjayisthecoolest.com/test2.aac"};

/// A test playlist in HLS format.
static const std::string TEST_HLS_PLAYLIST_URL{"http://sanjayisthecoolest.com/sample.m3u8"};

static const std::string TEST_HLS_PLAYLIST_CONTENT =
    "#EXTM3U\n"
    "#EXT-X-TARGETDURATION:10\n"
    "#EXT-X-MEDIA-SEQUENCE:9684358\n"
    "#EXTINF:10,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684358.aac\n"
    "#EXTINF:10.0,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684359.aac\n"
    "#EXTINF:10,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac\n"
    "#EXTINF:10.34,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac\n"
    "#EXTINF:10.344,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac\n"
    "#EXTINF:10.3444,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac\n"
    "#EXTINF:10.002,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac\n"
    "#EXTINF:10.0022,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac\n"
    "#EXTINF : 10.0022,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac\n"
    "#EXTINF : -1,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac\n"
    "#EXTINF:123ms,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac\n"
    "#EXTINF:123 ms,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac\n"
    "#EXTINF:123.0ms,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac\n"
    "#EXTINF:123ms,\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac\n"
    "#EXTINF:123 ,hi\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac\n"
    "#EXT-X-ENDLIST\n";

static const size_t TEST_HLS_PLAYLIST_URL_EXPECTED_PARSES = 15;

static const std::vector<std::string> TEST_HLS_PLAYLIST_URLS = {
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684358.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684359.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac"};

static const std::vector<std::chrono::milliseconds> TEST_HLS_DURATIONS = {std::chrono::milliseconds{10000},
                                                                          std::chrono::milliseconds{10000},
                                                                          std::chrono::milliseconds{10000},
                                                                          std::chrono::milliseconds{10340},
                                                                          std::chrono::milliseconds{10344},
                                                                          std::chrono::milliseconds{10344},
                                                                          std::chrono::milliseconds{10002},
                                                                          std::chrono::milliseconds{10002},
                                                                          std::chrono::milliseconds{10002},
                                                                          std::chrono::milliseconds{-1},
                                                                          std::chrono::milliseconds{-1},
                                                                          std::chrono::milliseconds{-1},
                                                                          std::chrono::milliseconds{-1},
                                                                          std::chrono::milliseconds{-1},
                                                                          std::chrono::milliseconds{123000}};

/// A test playlist in PLS format.
static const std::string TEST_PLS_PLAYLIST_URL{"http://sanjayisthecoolest.com/sample3.pls"};

static const std::string TEST_PLS_CONTENT =
    "[playlist]\n"
    "NumberOfEntries=2\n"
    "File1=http://stream.radiotime.com/sample.mp3\n"
    "Length1=-1\n"
    "File2=http://live-mp3-128.kexp.org\n"
    "Length2=-1\n";

static const size_t TEST_PLS_PLAYLIST_URL_EXPECTED_PARSES = 2;

static const std::vector<std::string> TEST_PLS_PLAYLIST_URLS = {"http://stream.radiotime.com/sample.mp3",
                                                                "http://live-mp3-128.kexp.org"};

/// A test playlist in HLS format.
static const std::string TEST_HLS_LIVE_STREAM_PLAYLIST_URL{"http://sanjayisthecoolest.com/liveStream.m3u8"};

static const std::string TEST_HLS_LIVE_STREAM_PLAYLIST_CONTENT_1 =
    "#EXTM3U\n"
    "#EXT-X-TARGETDURATION:10\n"
    "#EXT-X-MEDIA-SEQUENCE:9684358\n"
    "#EXTINF:10,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684358.aac\n"
    "#EXTINF:10.0,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684359.aac\n"
    "#EXTINF:10,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac\n";

static const std::string TEST_HLS_LIVE_STREAM_PLAYLIST_CONTENT_2 =
    "#EXTM3U\n"
    "#EXT-X-TARGETDURATION:10\n"
    "#EXT-X-MEDIA-SEQUENCE:9684360\n"
    "#EXTINF:10,RADIO\n"
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac\n"
    "#EXTINF:10,RADIO\n"
    "http://sanjay.com/chunk.mp3\n"
    "#EXTINF:10,RADIO\n"
    "http://sanjay.com/anotherChunk.mp3\n"
    "#EXT-X-ENDLIST\n";

static const size_t TEST_HLS_LIVE_STREAM_PLAYLIST_EXPECTED_PARSES = 5;

static const std::vector<std::string> TEST_HLS_LIVE_STREAM_PLAYLIST_URLS = {
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684358.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684359.aac",
    "http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac",
    "http://sanjay.com/chunk.mp3",
    "http://sanjay.com/anotherChunk.mp3"};

static const std::vector<std::chrono::milliseconds> TEST_HLS_LIVE_STREAM_DURATIONS = {std::chrono::milliseconds{10000},
                                                                                      std::chrono::milliseconds{10000},
                                                                                      std::chrono::milliseconds{10000},
                                                                                      std::chrono::milliseconds{10000},
                                                                                      std::chrono::milliseconds{10000}};

static const size_t NUM_PARSES_EXPECTED_WHEN_NO_PARSING = 1;

static const std::string TEST_MEDIA_URL = "https://sanjay.com/some_media.mp3";

static const std::unordered_map<std::string, std::string> urlsToContentTypes{
    // Valid playlist content types
    {TEST_M3U_PLAYLIST_URL, "audio/mpegurl"},
    {TEST_M3U_RELATIVE_PLAYLIST_URL, "audio/mpegurl"},
    {TEST_HLS_PLAYLIST_URL, "application/vnd.apple.mpegurl"},
    {TEST_PLS_PLAYLIST_URL, "audio/x-scpls"},
    {TEST_HLS_LIVE_STREAM_PLAYLIST_URL, "audio/mpegurl"},
    // Not playlist content types
    {TEST_MEDIA_URL, "audio/mpeg"},
    {"http://stream.radiotime.com/sample.mp3", "audio/mpeg"},
    {"http://live-mp3-128.kexp.org", "audio/mpeg"},
    {"http://76.74.255.139/bismarck/live/bismarck.mov_9684358.aac", "audio/mpeg"},
    {"http://76.74.255.139/bismarck/live/bismarck.mov_9684359.aac", "audio/mpeg"},
    {"http://76.74.255.139/bismarck/live/bismarck.mov_9684360.aac", "audio/mpeg"},
    {"http://stream.radiotime.com/sample.mp3", "audio/mpeg"},
    {"http://live-mp3-128.kexp.org", "audio/mpeg"},
    {"http://sanjayisthecoolest.com/../test.mp3", "audio/mpeg"},
    {"http://sanjayisthecoolest.com/test2.aac", "audio/mpeg"},
    {"http://sanjay.com/chunk.mp3", "audio/mpeg"},
    {"http://sanjay.com/anotherChunk.mp3", "audio/mpeg"}};

static std::unordered_map<std::string, std::string> urlsToContent{
    {TEST_M3U_PLAYLIST_URL, TEST_M3U_PLAYLIST_CONTENT},
    {TEST_M3U_RELATIVE_PLAYLIST_URL, TEST_M3U_RELATIVE_PLAYLIST_CONTENT},
    {TEST_HLS_PLAYLIST_URL, TEST_HLS_PLAYLIST_CONTENT},
    {TEST_PLS_PLAYLIST_URL, TEST_PLS_CONTENT},
    {TEST_HLS_LIVE_STREAM_PLAYLIST_URL, TEST_HLS_LIVE_STREAM_PLAYLIST_CONTENT_1}};

/// A mock content fetcher
class MockContentFetcher : public avsCommon::sdkInterfaces::HTTPContentFetcherInterface {
public:
    MockContentFetcher(const std::string& url) : m_url{url} {
    }

    std::unique_ptr<avsCommon::utils::HTTPContent> getContent(
        FetchOptions fetchOption,
        std::shared_ptr<avsCommon::avs::attachment::AttachmentWriter> writer,
        const std::vector<std::string>& customHeaders = std::vector<std::string>()) {
        if (fetchOption == FetchOptions::CONTENT_TYPE) {
            auto it1 = urlsToContentTypes.find(m_url);
            if (it1 == urlsToContentTypes.end()) {
                return nullptr;
            } else {
                std::promise<long> statusPromise;
                auto statusFuture = statusPromise.get_future();
                statusPromise.set_value(HTTP_REQUEST_OK);
                std::promise<std::string> contentTypePromise;
                auto contentTypeFuture = contentTypePromise.get_future();
                contentTypePromise.set_value(it1->second);
                return avsCommon::utils::memory::make_unique<avsCommon::utils::HTTPContent>(
                    std::move(statusFuture), std::move(contentTypeFuture), nullptr);
            }
        } else if (fetchOption == FetchOptions::ENTIRE_BODY) {
            auto it2 = urlsToContent.find(m_url);
            if (it2 == urlsToContent.end()) {
                return nullptr;
            } else {
                static bool liveStreamPlaylistRequested = false;
                if (m_url == TEST_HLS_LIVE_STREAM_PLAYLIST_URL) {
                    if (!liveStreamPlaylistRequested) {
                        it2->second = TEST_HLS_LIVE_STREAM_PLAYLIST_CONTENT_1;
                        liveStreamPlaylistRequested = true;
                    } else {
                        it2->second = TEST_HLS_LIVE_STREAM_PLAYLIST_CONTENT_2;
                    }
                }
                std::promise<long> statusPromise;
                auto statusFuture = statusPromise.get_future();
                statusPromise.set_value(HTTP_REQUEST_OK);
                std::promise<std::string> contentTypePromise;
                auto contentTypeFuture = contentTypePromise.get_future();
                contentTypePromise.set_value("");
                return avsCommon::utils::memory::make_unique<avsCommon::utils::HTTPContent>(
                    std::move(statusFuture), std::move(contentTypeFuture), writeStringIntoAttachment(it2->second));
            }
        } else {
            return nullptr;
        }
    }

private:
    std::shared_ptr<avsCommon::avs::attachment::InProcessAttachment> writeStringIntoAttachment(
        const std::string& string) {
        static int id = 0;
        std::shared_ptr<avsCommon::avs::attachment::InProcessAttachment> stream =
            std::make_shared<avsCommon::avs::attachment::InProcessAttachment>(std::to_string(id++));
        if (!stream) {
            return nullptr;
        }
        auto writer = stream->createWriter();
        if (!writer) {
            return nullptr;
        }
        avsCommon::avs::attachment::AttachmentWriter::WriteStatus writeStatus;
        writer->write(string.data(), string.size(), &writeStatus);
        return stream;
    };

    std::string m_url;
};

}  // namespace test
}  // namespace playlistParser
}  // namespace alexaClientSDK

#endif  // ALEXA_CLIENT_SDK_PLAYLISTPARSER_TEST_PLAYLISTPARSER_MOCKCONTENTFETCHER_H_
