#include <maps/wikimap/mapspro/services/mrc/long_tasks/async_takeout_uploader/lib/takeout.h>

#include <library/cpp/testing/common/env.h>
#include <library/cpp/testing/gtest/gtest.h>
#include <maps/libs/common/include/file_utils.h>
#include <yandex/maps/mrc/unittest/database_fixture.h>
#include <yandex/maps/mrc/unittest/local_server.h>
#include <yandex/maps/mrc/unittest/unittest_config.h>
#include <yandex/maps/wiki/unittest/config.h>
#include <yandex/maps/wiki/unittest/localdb.h>

namespace maps::mrc::testing {

namespace {
// clang-format off
const std::string SQL_DATA_PATH
    = "/maps/wikimap/mapspro/services/mrc/long_tasks/async_takeout_uploader/tests/data.sql";

const std::string UID1 = "101";
const std::string UID2 = "102";

const std::string USER_1_EXPECTED_RESULT
    = maps::common::readFileToString(SRC_("user-1.expected.json"));
// clang-format on


const auto TAKEOUT_STUB_SERVER_PROVIDER = [](uint16_t port) {
    return process::Command(
        {BinaryPath("maps/wikimap/mapspro/services/mrc/long_tasks/"
                    "async_takeout_uploader/tests/takeout_stub/takeout-stub"),
         unittest::LOCAL_HOST, std::to_string(port)});
};

class TakeoutStubFixture : private unittest::TestServer {
public:
    TakeoutStubFixture() : TestServer(TAKEOUT_STUB_SERVER_PROVIDER) {}

    explicit TakeoutStubFixture(xml3::Doc& configDoc)
        : TestServer(TAKEOUT_STUB_SERVER_PROVIDER)
    {
        INFO() << "Takeout stub is running on " << getHost() << ":"
               << getPort();

        static const char* const TAKEOUT_XPATH
            = "/config/external-services/takeout";
        {
            auto node = configDoc.node(TAKEOUT_XPATH);
            node.setAttr("url", "http://localhost:" + getPortStr());
        }
    }
};

class TestFixture
    : public ::testing::Test,
      public unittest::WithUnittestConfig<unittest::DatabaseFixture,
                                          TakeoutStubFixture>
{
public:
    TestFixture()
        : mapsproDbFixture_()
        , nmapsConfigFileHolder_(
            mapsproDbFixture_.host(),
            mapsproDbFixture_.port(),
            mapsproDbFixture_.dbname(),
            mapsproDbFixture_.user(),
            mapsproDbFixture_.password())
        , nmapsConfigXml_(nmapsConfigFileHolder_.filepath())
    {
        auto txn = pool().masterWriteableTransaction();
        txn->exec(maps::common::readFileToString(ArcadiaSourceRoot()
                                                 + SQL_DATA_PATH));
        txn->commit();
    }

    std::string getFilesContentFor(const std::string& jobId)
    {
        http::Client httpClient;

        auto takeoutUrl = config().externals().takeoutConfig().url();
        takeoutUrl.setPath("/get_data_for");

        http::Request request{httpClient, http::GET, takeoutUrl};
        request.addParam("job_id", jobId);

        auto response = request.perform();
        REQUIRE(response.status() == 200,
                "Failed to get files content for job ID = " + jobId);
        return response.readBody();
    }

    const maps::wiki::common::ExtendedXmlDoc& nmapsConfigXml() {
        return nmapsConfigXml_;
    }

private:
    maps::wiki::unittest::MapsproDbFixture mapsproDbFixture_;
    maps::wiki::unittest::ConfigFileHolder nmapsConfigFileHolder_;
    maps::wiki::common::ExtendedXmlDoc nmapsConfigXml_;

};

} // namespace

TEST_F(TestFixture, basic_upload_test)
{
    const auto jobId = "user-1-job-id";
    takeout::TakeoutUploader takeoutUploader{config(), nmapsConfigXml()};
    takeoutUploader.handleTakeoutUpload(UID1, jobId);
    EXPECT_EQ(getFilesContentFor(jobId), USER_1_EXPECTED_RESULT);
}

TEST_F(TestFixture, non_existing_uid_test)
{
    const auto jobId = "user-2-job-id";
    takeout::TakeoutUploader takeoutUploader{config(), nmapsConfigXml()};
    takeoutUploader.handleTakeoutUpload(UID2, jobId);
    EXPECT_EQ(getFilesContentFor(jobId), "{}");
}

} // namespace maps::mrc::testing
