#include <maps/wikimap/mapspro/services/tasks_sprav/src/export_poi_worker/config.h>
#include "poi_fixture.h"
#include <maps/wikimap/mapspro/services/tasks_sprav/src/export_poi_worker/dump.h>
#include <maps/wikimap/mapspro/services/tasks_sprav/src/export_poi_worker/export_poi.h>
#include <maps/wikimap/mapspro/services/tasks_sprav/src/export_poi_worker/json2ymapsdf.h>
#include <maps/wikimap/mapspro/services/tasks_sprav/src/export_poi_worker/loader.h>
#include <maps/wikimap/mapspro/services/tasks_sprav/src/export_poi_worker/revision2json.h>
#include <maps/wikimap/mapspro/services/tasks_sprav/src/export_poi_worker/search_path_guard.h>
#include <maps/wikimap/mapspro/services/tasks_sprav/src/export_poi_worker/utils.h>

#include <maps/libs/ymapsdf/include/ft.h>
#include <yandex/maps/wiki/revision/branch_manager.h>
#include <yandex/maps/wiki/revision/revisionsgateway.h>
#include <yandex/maps/wiki/revisionapi/revisionapi.h>

#include <boost/test/unit_test.hpp>

#include <algorithm>
#include <filesystem>
#include <fstream>

namespace fs = std::filesystem;

using namespace maps;
using namespace maps::wiki;
using namespace maps::wiki::poi;

namespace {

const DBID POI_ID_A = 101;
const DBID VEGETATION_ID1 = 998;
const DBID VEGETATION_ID2 = 1001;
const DBID METRO_STATION_ID = 13787134421;

const DBIDSet EXPECTED_IDS = {POI_ID_A, VEGETATION_ID1, VEGETATION_ID2, METRO_STATION_ID};

const PoiDef POI_DEF_A = {POI_ID_A,
                          0,
                          boost::none,
                          {},
                          184105716,
                          {40995601955, 137236877779},
                          {{"ru", "100 лет локомотивному депо Кандалакши"}},
                          {{"ru", "100 лет Кандалакши"}},
                          {{"ru", "Век Кандалакши"}},
                          {},
                          {},
                          {},
                          "666",
                          {},
                          {},
                          32.413904292,
                          67.160285438,
                          {102, 103, 104, 105},
                          5,
                          {},
                          "org",
                          1,
                          maps::wiki::common::WIKIMAPS_SPRAV_UID,
                          std::vector<EntranceDef>{{201, 32.41390429, 67.16028543}, {202, 32.41390428, 67.16028544}},
                          "",
                          {},
                          {},
                          PermalinkFromReserves::False,
                          HasVerifiedCoords::False,
                          {}};

} // anonymous namespace

BOOST_FIXTURE_TEST_SUITE(db_tests, tests::PoiFixture)

BOOST_AUTO_TEST_CASE(test_search_path_guard)
{
    auto txn = pool().masterReadOnlyTransaction();
    auto currentSearchPath =
        [&txn] { return txn->get_variable("search_path"); };
    const std::string initSearchPath = currentSearchPath();
    {
        SearchPathGuard searchPathGuard(*txn, "revision");
        BOOST_CHECK(initSearchPath != currentSearchPath());
    }
    BOOST_CHECK_EQUAL(initSearchPath, currentSearchPath());
}

BOOST_AUTO_TEST_CASE(test_loader)
{
    fillYMapsDF();

    auto txn = pool().masterReadOnlyTransaction();
    revision::BranchManager branchManager(txn.get());
    auto branch = branchManager.loadTrunk();
    revision::RevisionsGateway gateway(txn.get(), branch);
    auto snapshotId = gateway.maxSnapshotId();
    auto snapshot = gateway.snapshot(snapshotId);

    SearchPathGuard searchPathGuard(*txn, config().resourceName());
    Loader loader(*txn, pool(), snapshot, config().mapping(), config().parentMapping(), {});


    auto poiIds = loader.loadPoiIds();
    BOOST_CHECK_EQUAL_COLLECTIONS(poiIds.begin(), poiIds.end(),
                                  EXPECTED_IDS.begin(), EXPECTED_IDS.end());

    auto def = loader.loadPoiDefs({POI_ID_A}).front();
    BOOST_CHECK(def == POI_DEF_A);
    BOOST_CHECK_GE(
        chrono::convertToUnixTime(def.actualizationDate.get()),
        startTime());

    auto stationDef = loader.loadPoiDefs({METRO_STATION_ID}).front();
    BOOST_CHECK_EQUAL(stationDef.rubricIds.size(), 1);
    auto stationWithMetroLineMapping =
        config().parentMapping().find(
            static_cast<FtTypeId>(maps::ymapsdf::ft::Type::TransportMetroLine));
    BOOST_REQUIRE(stationWithMetroLineMapping != config().parentMapping().end());
    BOOST_CHECK(stationDef.rubricIds.count(stationWithMetroLineMapping->second));
    BOOST_CHECK_EQUAL(stationDef.mtrId, "mtr_2323423");

    auto poiService = loader.loadPoiDefs({13787134435}).front();
    BOOST_REQUIRE(poiService.indoorPlanId);
    BOOST_CHECK_EQUAL(*poiService.indoorPlanId, 13787134433);
}

BOOST_AUTO_TEST_CASE(test_actualization_on_delete)
{
    fillYMapsDF();
    chrono::TimePoint createdAt;
    {// get current actualizationDate and delete all names
        auto txn = pool().masterWriteableTransaction();
        revision::BranchManager branchManager(txn.get());
        auto branch = branchManager.loadTrunk();
        revision::RevisionsGateway gateway(txn.get(), branch);
        auto snapshotId = gateway.maxSnapshotId();
        auto snapshot = gateway.snapshot(snapshotId);
        SearchPathGuard searchPathGuard(*txn, config().resourceName());
        Loader loader(*txn, pool(), snapshot, config().mapping(), config().parentMapping(), {});
        auto poiIds = loader.loadPoiIds();
        auto def = loader.loadPoiDefs({POI_ID_A}).front();
        BOOST_REQUIRE(def.id == POI_ID_A && def.actualizationDate);
        createdAt = *def.actualizationDate;
        auto namesRels = snapshot.loadSlaveRelations(POI_ID_A);
        std::list<revision::RevisionsGateway::NewRevisionData> deleteAllNames;
        for (const auto& rel : namesRels) {
            deleteAllNames.emplace_back(
                rel.id(),
                revision::ObjectRevision::Data {std::nullopt, std::nullopt, std::nullopt, std::nullopt, true});
        }
        revision::Attributes commitAttrs;
        commitAttrs.emplace("primary_object:" + std::to_string(POI_ID_A), "1");
        gateway.createCommit(deleteAllNames, tests::USER_ID, commitAttrs);
        txn->commit();
    }
    {// check new actualizationDate is more recent
        auto txn = pool().masterReadOnlyTransaction();
        revision::BranchManager branchManager(txn.get());
        auto branch = branchManager.loadTrunk();
        revision::RevisionsGateway gateway(txn.get(), branch);
        auto snapshotId = gateway.maxSnapshotId();
        auto snapshot = gateway.snapshot(snapshotId);
        SearchPathGuard searchPathGuard(*txn, config().resourceName());
        Loader loader(*txn, pool(), snapshot, config().mapping(), config().parentMapping(), {});
        auto poiIds = loader.loadPoiIds();
        auto def = loader.loadPoiDefs({POI_ID_A}).front();
        BOOST_REQUIRE(def.id == POI_ID_A && def.actualizationDate);
        BOOST_CHECK(*def.actualizationDate > createdAt);
    }
}

BOOST_AUTO_TEST_CASE(test_dump)
{
    fillYMapsDF();
    tests::BuildDirGuard bdg;
    dump(config());
    std::ifstream file(config().resultFilePathCalculatedRubricId(),
                       std::ifstream::ate | std::ifstream::binary);
    REQUIRE(file.good(), "Failed to open: "
        << config().resultFilePathCalculatedRubricId());
    BOOST_CHECK(file.tellg() > 0);
    auto txn = config().socialPool().masterReadOnlyTransaction();
    auto rows = txn->exec("SELECT object_id FROM sprav.export_poi_worker_feed_data");
    BOOST_CHECK_EQUAL(rows.size(), EXPECTED_IDS.size());
    for (const auto& row : rows) {
        BOOST_CHECK(EXPECTED_IDS.count(row[0].as<DBID>()));
    }
}

BOOST_AUTO_TEST_CASE(cleanup_old_schemas_test)
{
    auto oldSchemaName = makeResourceName(
        0, chrono::parseIsoDateTime("2015-06-16T15:25:21"));
    auto newSchemaName = makeResourceName(1, chrono::TimePoint::clock::now());
    {
        auto txn = pool().masterWriteableTransaction();
        txn->exec("CREATE SCHEMA " + oldSchemaName);
        txn->exec("CREATE SCHEMA " + newSchemaName);
        txn->commit();
    }
    cleanupOldSchemas(pool());
    BOOST_CHECK(!doesSchemaExist(oldSchemaName));
    BOOST_CHECK(doesSchemaExist(newSchemaName));
}

BOOST_AUTO_TEST_CASE(test_export_poi)
{
    tests::BuildDirGuard bdg;
    exportPoi(config(), [](const poi::Config&) {}, json2ymapsdfPath());
    BOOST_CHECK(!fs::exists(config().taskDirPath()));
    BOOST_CHECK(!doesSchemaExist(config().resourceName()));
}

BOOST_AUTO_TEST_SUITE_END()
