#include <maps/wikimap/mapspro/services/tasks_export/src/export_worker/client/garden_poster.h>
#include <maps/wikimap/mapspro/services/tasks_export/src/export_worker/lib/run.h>
#include <maps/wikimap/mapspro/services/tasks_export/src/export_worker/lib/db_log.h>

#include <maps/libs/cmdline/include/cmdline.h>
#include <maps/libs/log8/include/log8.h>
#include <maps/libs/common/include/exception.h>
#include <maps/libs/json/include/builder.h>
#include <maps/libs/http/include/request.h>
#include <yandex/maps/wiki/common/default_config.h>
#include <yandex/maps/wiki/common/extended_xml_doc.h>
#include <yandex/maps/wiki/common/pgpool3_helpers.h>
#include <yandex/maps/wiki/common/robot.h>
#include <yandex/maps/wiki/tasks/export.h>
#include <yandex/maps/wiki/tasks/ymapsdf.h>

#include <util/string/cast.h>
#include <util/system/env.h>

#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>

#include <contrib/libs/fmt/include/fmt/format.h>

#include <memory>

namespace maps::wiki::exporter {

size_t createDevExportTaskInDb(
    pgpool3::Pool& pool,
    const std::string& subset,
    size_t branchId,
    size_t commitId)
{
    auto txn = pool.masterWriteableTransaction();
    auto result = txn->exec(fmt::format(
        "INSERT INTO service.task (type, created_by, modified_by, status) "
        "VALUES ('export', {uid}, {uid}, 'SUCCESS') RETURNING id",
        fmt::arg("uid", common::ROBOT_UID))
    );
    REQUIRE(!result.empty(), "Could not register new task in DB");
    auto taskId = result[0][0].as<size_t>();

    txn->exec(fmt::format(
        "INSERT INTO service.export_task (id, commit_id, subset, branch_id, tested) "
        "VALUES ({}, {}, {}, {}, false)",
        taskId, commitId, txn->quote(subset), branchId)
    );

    txn->commit();
    INFO() << "Created new task " << taskId;
    return taskId;
}

} // namespace maps::wiki::exporter

int main(int argc, char* argv[]) try
{
    using namespace maps::wiki;
    using mds_dataset::IsTested;

    maps::cmdline::Parser parser;
    auto taskIdArg = parser.size_t("task-id").defaultValue(0)
        .help("fake task id for unique names generation. Ignored if Garden is involved");
    auto branch = parser.string("branch-id").defaultValue("trunk")
        .help("branch id. Default is 'trunk'");
    auto commitId = parser.size_t("commit-id").required()
        .help("commit id (required)");
    auto subset = parser.string("subset").defaultValue("domain")
        .help("subset of objects to export, 'domain', 'masstransit', or 'service'. Default is 'domain'");
    auto tested = parser.flag("tested").defaultValue(false)
        .help("Whether dataset is tested. Default is false");
    auto config = parser.string("config")
        .help("path to worker configuration");
    auto json2ymapsdfPath = parser.string("json2ymapsdf")
        .help("path to json2ymapsdf utility");
    auto json2ymapsdfTransformXml = parser.string("transform-cfg")
        .help("path to json2ymapsdf configuration");
    auto regionsStr = parser.string("regions")
        .help("regions to export. Use value from config by default");
    auto rootDirOption = parser.string("root-dir")
        .help("path to root dir");
    auto keepJson = parser.flag("keep-json").defaultValue(false)
        .help("Do not delete json files after processing");
    auto disableMdsUpload = parser.flag("disable-mds-upload")
        .defaultValue(false)
        .help("Do not upload export results to MDS");
    auto experiment = parser.string("experiment")
        .help("Run json2ymapsdf in experiment mode");
    auto ymapsdfDir = parser.string("ymapsdf-dir")
        .help("YMapsDF schemas directory");
    auto geodataPath = parser.string("geodata-path")
        .help("Yandex geodata path");
    auto gardenContour = parser.string("garden-contour")
        .help("Garden experimental contour to start release build in");

    parser.parse(argc, argv);

    std::string gardenOauth = GetEnv("GARDEN_OAUTH_TOKEN");

    auto configXmlPtr = config.defined()
        ? std::make_unique<common::ExtendedXmlDoc>(config)
        : common::loadDefaultConfig();

    size_t taskId = taskIdArg;
    if (gardenContour.defined()) {
        REQUIRE(!gardenOauth.empty(), "Missing Garden OAuth token");
        size_t branchId;
        REQUIRE(TryFromString(branch, branchId), "Only numeric branch id is supported");
        maps::wiki::common::PoolHolder poolHolder(*configXmlPtr, "mds", "mds");
        taskId = exporter::createDevExportTaskInDb(poolHolder.pool(), subset, branchId, commitId);
    }

    if (ymapsdfDir.defined()) {
        maps::wiki::tasks::ymapsdf::setDefaultSchemasDirectory(ymapsdfDir);
    }

    exporter::TaskParams taskParams(taskId, commitId, branch,
        maps::enum_io::fromString<maps::wiki::mds_dataset::Subset>(subset),
        experiment,
        tested ? IsTested::Yes : IsTested::No);

    exporter::ExportConfig exportCfg(taskParams, *configXmlPtr);
    exportCfg.detectAndLockNearestExportDatabase();

    if (json2ymapsdfPath.defined()) {
        exportCfg.setJson2ymapsdfPath(json2ymapsdfPath);
    }
    if (json2ymapsdfTransformXml.defined()) {
        exportCfg.setJson2ymapsdfTransformXml(json2ymapsdfTransformXml);
    }
    if (regionsStr.defined()) {
        exporter::Regions regions;
        boost::split(regions, regionsStr, boost::is_any_of(" ,"));
        exportCfg.setRegions(regions);
    }
    exportCfg.keepJson(keepJson);

    if (disableMdsUpload) {
        exportCfg.disableMdsUpload();
    }

    INFO() << exportCfg;

    std::string rootDir = rootDirOption.defined()
        ? rootDirOption
        : configXmlPtr->getAttr<std::string>(tasks::getConfigExportXpath(), "tmp-base-dir");

    exporter::setGeoData(geodataPath);
    auto datasets = exporter::runExport(exportCfg, rootDir);
    if (gardenContour.defined()) {
        // save dev export result to mds pool (unstable database)
        exporter::saveResult(exportCfg, exportCfg.mdsPool(), datasets);
        exporter::GardenPoster gardenPoster(gardenContour, gardenOauth);
        for (const auto& dataset : datasets) {
            gardenPoster.postToGarden(dataset, branch);
        }
    }

    return EXIT_SUCCESS;
} catch (const maps::Exception& e) {
    ERROR() << "Export failed: " << e;
    return EXIT_FAILURE;
} catch (const std::exception& e) {
    ERROR() << "Export failed: " << e.what();
    return EXIT_FAILURE;
}
