#include "fixture.h"

#include <library/cpp/testing/common/env.h>
#include <mapreduce/yt/tests/yt_unittest_lib/yt_unittest_lib.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/libs/yt/include/serialization.h>
#include <maps/wikimap/mapspro/services/mrc/long_tasks/regular_upload/lib/include/config.h>
#include <maps/wikimap/mapspro/services/mrc/long_tasks/regular_upload/lib/include/uploader.h>
#include <maps/libs/geolib/include/contains.h>
#include <yandex/maps/mrc/unittest/database_fixture.h>

#include <array>
#include <tuple>

using namespace testing;

namespace maps::mrc::regular_upload::tests {

namespace {

constexpr blackbox_client::Uid USER_ID_1 = 84277110;
const std::string LOGIN = "login_1";

std::unique_ptr<testing::NiceMock<MockBlackboxClient>> makeMockBlackbox()
{
    auto blackbox = std::make_unique<testing::NiceMock<MockBlackboxClient>>();
    const blackbox_client::UidToLoginMap uidToLoginMap = {{USER_ID_1, LOGIN}};
    const blackbox_client::Uids uids = {USER_ID_1};

    ON_CALL(*blackbox, uidToLoginMap(testing::Eq(uids)))
        .WillByDefault(testing::Return(uidToLoginMap));
    return blackbox;
}

} // anonymous qnamespace

TEST_F(Fixture, test_regular_upload)
{
    const std::string rideSource = "a3af5bb077f68f94fb523adbeecab05b";
    const std::string walkSource = "b374938a5cb8388b9b33dd2668b7c0e2";


    const std::array FEATURE_PROPS = {
        std::make_tuple(1, rideSource, db::Dataset::Rides, "2018-12-05 13:41:06.613+03", db::GraphType::Road),
        std::make_tuple(2, rideSource, db::Dataset::Rides, "2018-12-05 13:41:10.088+03", db::GraphType::Road),
        std::make_tuple(4, rideSource, db::Dataset::Rides, "2018-12-05 13:41:16.997+03", db::GraphType::Road),
        std::make_tuple(5, walkSource, db::Dataset::Walks, "2018-12-12 11:23:21.504+03", db::GraphType::Pedestrian)
    };

    db::Features referenceFeatures;
    for (const auto& prop : FEATURE_PROPS) {
        referenceFeatures.push_back(db::Feature{std::get<0>(prop)}
                                        .setSourceId(std::get<1>(prop))
                                        .setDataset(std::get<2>(prop))
                                        .setTimestamp(std::get<3>(prop))
                                        .setGraph(std::get<4>(prop)));
    }

    auto client = NYT::NTesting::CreateTestClient();
    auto blackboxClient = makeMockBlackbox();
    auto mds = config().makeMdsClient();
    const std::string featuresTablePath = "//tmp/test_features";

    auto testCfg = RegularUploadConfig()
        .setMds(&mds)
        .setPool(&pool())
        .setYtClient(client.Get())
        .setBlackboxClient(blackboxClient.get())
        .setFeaturesTablePath(featuresTablePath);

    RegularUploader uploader(testCfg);
    uint64_t loadedFeatures = uploader.loadAndSaveFromDatabase();

    EXPECT_EQ(loadedFeatures, 4U);

    auto reader = client->CreateTableReader<NYT::TNode>("//tmp/test_features");

    std::vector<NYT::TNode> result;
    for (; reader->IsValid(); reader->Next()) {
        result.push_back(reader->GetRow());
    }

    EXPECT_EQ(result.size(), 4U);

    EXPECT_EQ(referenceFeatures[0].id()       , yt::deserialize<db::TId>(result[0]["id"]));
    EXPECT_EQ(referenceFeatures[0].timestamp(), yt::deserialize<chrono::TimePoint>(result[0]["timestamp"]));
    EXPECT_EQ(referenceFeatures[0].sourceId() , yt::deserialize<std::string>(result[0]["source_id"]));
    EXPECT_EQ(referenceFeatures[0].dataset()  , enum_io::fromString<db::Dataset>(yt::deserialize<std::string>(result[0]["dataset"])));
    EXPECT_EQ(referenceFeatures[0].graph()    , yt::deserialize<db::GraphType>(result[0]["graph"]));
    EXPECT_EQ(LOGIN                           , yt::deserialize<std::string>(result[0]["login"]));

    EXPECT_EQ(referenceFeatures[1].id()       , yt::deserialize<db::TId>(result[1]["id"]));
    EXPECT_EQ(referenceFeatures[1].timestamp(), yt::deserialize<chrono::TimePoint>(result[1]["timestamp"]));
    EXPECT_EQ(referenceFeatures[1].sourceId() , yt::deserialize<std::string>(result[1]["source_id"]));
    EXPECT_EQ(referenceFeatures[1].dataset()  , enum_io::fromString<db::Dataset>(yt::deserialize<std::string>(result[1]["dataset"])));
    EXPECT_EQ(referenceFeatures[1].graph()    , yt::deserialize<db::GraphType>(result[1]["graph"]));
    EXPECT_EQ(LOGIN                           , yt::deserialize<std::string>(result[1]["login"]));

    // In this case no such id in blackbox
    EXPECT_EQ(referenceFeatures[2].id()       , yt::deserialize<db::TId>(result[2]["id"]));
    EXPECT_EQ(referenceFeatures[2].timestamp(), yt::deserialize<chrono::TimePoint>(result[2]["timestamp"]));
    EXPECT_EQ(referenceFeatures[2].sourceId() , yt::deserialize<std::string>(result[2]["source_id"]));
    EXPECT_EQ(referenceFeatures[2].dataset()  , enum_io::fromString<db::Dataset>(yt::deserialize<std::string>(result[2]["dataset"])));
    EXPECT_EQ(referenceFeatures[2].graph()    , yt::deserialize<db::GraphType>(result[2]["graph"]));
    EXPECT_TRUE(yt::deserialize<std::string>(result[2]["login"]).empty());

    EXPECT_EQ(referenceFeatures[3].id()       , yt::deserialize<db::TId>(result[3]["id"]));
    EXPECT_EQ(referenceFeatures[3].timestamp(), yt::deserialize<chrono::TimePoint>(result[3]["timestamp"]));
    EXPECT_EQ(referenceFeatures[3].sourceId() , yt::deserialize<std::string>(result[3]["source_id"]));
    EXPECT_EQ(referenceFeatures[3].dataset()  , enum_io::fromString<db::Dataset>(yt::deserialize<std::string>(result[3]["dataset"])));
    EXPECT_EQ(referenceFeatures[3].graph()    , yt::deserialize<db::GraphType>(result[3]["graph"]));
    EXPECT_TRUE(yt::deserialize<std::string>(result[3]["login"]).empty());
}

} // maps::mrc::regular_upload::tests
