#include <maps/wikimap/mapspro/services/mrc/libs/config/include/config.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/track_point.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/track_point_gateway.h>

#include <maps/libs/cmdline/include/cmdline.h>
#include <maps/libs/common/include/exception.h>
#include <maps/libs/json/include/builder.h>
#include <maps/libs/log8/include/log8.h>
#include <maps/libs/pgpool/include/pgpool3.h>

#include <iostream>

using namespace maps::mrc;

namespace {

db::Features loadRidePhotos(maps::pgpool3::Pool& pool, const std::string& sourceId, const maps::chrono::TimePoint& start)
{
    auto txn = pool.slaveTransaction();
    return db::FeatureGateway{*txn}.load(
        db::table::Feature::sourceId.equals(sourceId) &&
        db::table::Feature::isPublished &&
        db::table::Feature::date.greaterOrEquals(start)
    );
}

void SavePassage(maps::json::ArrayBuilder &builder, const std::string& sourceId, const maps::chrono::TimePoint& start, const maps::chrono::TimePoint& end, int count) {
    builder << [&](maps::json::ObjectBuilder builder) {
        builder["source_id"] << sourceId;
        builder["time_start"] << maps::chrono::formatSqlDateTime(start);
        builder["time_end"] << maps::chrono::formatSqlDateTime(end);
        builder["photos_count"] << count;
    };
}

void savePassages(db::Features&& photos, const std::string& outputPath)
{
    constexpr auto TIME_GAP = std::chrono::seconds{300};

    if (photos.empty()) {
        return;
    }
    std::sort(photos.begin(), photos.end(),
        [](const db::Feature& a, const db::Feature& b) {
            return a.timestamp() < b.timestamp();
        }
    );

    std::ofstream ofs(outputPath);
    if (!ofs.is_open())
        ERROR() << "Unable to open output file: " << outputPath;
    maps::json::Builder builder(ofs);
    builder << [&](maps::json::ArrayBuilder builder) {
        maps::chrono::TimePoint start = photos[0].timestamp();
        maps::chrono::TimePoint last = start;
        const std::string sourceId = photos[0].sourceId();
        int count = 0;
        for (size_t i = 0; i < photos.size(); i++) {
            const db::Feature& feature = photos[i];
            if (std::chrono::abs(feature.timestamp() - last) > TIME_GAP) {
                SavePassage(builder, sourceId, start, last, count);
                start = feature.timestamp();
                last = start;
                count = 1;
            } else {
                last = feature.timestamp();
                count++;
            }
        };
        SavePassage(builder, sourceId, start, last, count);
    };
}

} //namespace

int main(int argc, const char** argv) try {
    maps::cmdline::Parser parser("Extract passages by source ID and start time");


    maps::cmdline::Option<std::string> sourceId = parser.string("source-id")
        .required()
        .help("Source ID");

    maps::cmdline::Option<std::string> timeStart = parser.string("time-start")
        .required()
        .help("Start time stamp (format: 2000-08-01 00:00:00.000000+03)");

    maps::cmdline::Option<std::string> mrcConfigPath = parser.string("mrc-config")
        .help("Path to mrc config");

    maps::cmdline::Option<std::string> secretVersion = parser.string("secret-version")
        .help("version for secrets from yav.yandex-team.ru");

    maps::cmdline::Option<std::string> outputPath = parser.string("output-path")
        .required()
        .help("Path to json for save passages");

    parser.parse(argc, const_cast<char**>(argv));

    maps::mrc::common::Config mrcConfig =
        secretVersion.defined()
        ? maps::mrc::common::Config(maps::vault_boy::loadContextWithYaVault(secretVersion), mrcConfigPath)
        : maps::mrc::common::templateConfigFromCmdPath(mrcConfigPath);
    maps::wiki::common::PoolHolder mrc(mrcConfig.makePoolHolder());

    db::Features photos = loadRidePhotos(mrc.pool(), sourceId, maps::chrono::parseSqlDateTime(timeStart));
    INFO() << "Loaded: " << photos.size() << " photos";
    savePassages(std::move(photos), outputPath);
    return EXIT_SUCCESS;
}
catch (const maps::Exception& e) {
    FATAL() << "Worker failed: " << e;
    return EXIT_FAILURE;
}
catch (const std::exception& e) {
    FATAL() << "Worker failed: " << e.what();
    return EXIT_FAILURE;
}
