#include <maps/libs/cmdline/include/cmdline.h>
#include <maps/libs/common/include/exception.h>
#include <maps/libs/log8/include/log8.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/mds_dataset/dataset_gateway.h>
#include <yandex/maps/wiki/mds_dataset/export_metadata.h>

#include <chrono>
#include <memory>
#include <vector>

namespace cr = std::chrono;
namespace common = maps::wiki::common;
namespace ds = maps::wiki::mds_dataset;

using Datasets = std::vector<ds::Dataset<ds::ExportMetadata>>;

namespace {

std::ostream& operator<<(std::ostream& stream, cr::minutes minutes)
{
    auto hours = cr::duration_cast<cr::hours>(minutes);
    minutes -= hours;
    if (hours.count()) {
        stream << hours.count() << "h";
    }
    if (hours.count() && minutes.count()) {
        stream << " ";
    }
    if (minutes.count()) {
        stream << minutes.count() << "m";
    }
    return stream;
}

Datasets getDeletingDatasets(maps::pgpool3::Pool& pool)
{
    using Reader = ds::DatasetReader<ds::ExportMetadata>;

    auto txn = pool.slaveTransaction();
    Reader::FilterType filter(*txn);
    filter.byStatus(ds::DatasetStatus::Deleting);
    return Reader::datasets(*txn, filter);
}

void outputMonitoring(
    const Datasets& deletingDatasets,
    cr::minutes warnTimeout,
    cr::minutes errTimeout)
{
    constexpr size_t DATASETS_COUNT_WARN_THRESHOLD = 10;
    constexpr size_t DATASETS_COUNT_ERR_THRESHOLD = 20;

    const auto now = ds::Timestamp::clock::now();

    auto oldestDatasetAge = deletingDatasets.empty()
                ? ds::Timestamp::duration::zero()
                : now - deletingDatasets.back().metadata().basic().created();
    if (oldestDatasetAge > errTimeout &&
        deletingDatasets.size() > DATASETS_COUNT_ERR_THRESHOLD) {
        std::cout << "2;Error";
    } else if (oldestDatasetAge > warnTimeout &&
        deletingDatasets.size() > DATASETS_COUNT_WARN_THRESHOLD) {
        std::cout << "1;Warning";
    } else {
        std::cout << "0;Ok";
    }
    std::cout << ": datasets in status 'deleting' - " << deletingDatasets.size();
    if (!deletingDatasets.empty()) {
        std::cout << ", the oldest dataset age is "
                  << cr::duration_cast<cr::minutes>(oldestDatasetAge);
    }
    std::cout << "\n";
}

} // anonymous namespace

int main(int argc, char* argv[]) try
{
    maps::log8::setLevel(maps::log8::Level::FATAL);
    maps::cmdline::Parser parser{"Check datasets in status 'deleting'"};
    auto warnTimeout = parser.option<cr::minutes>("warn-timeout")
        .help("warning timeout");
    auto errTimeout = parser.option<cr::minutes>("err-timeout")
        .help("error timeout");
    parser.parse(argc, argv);

    REQUIRE(warnTimeout < errTimeout, "warn-timeout is less than err-timeout");

    std::unique_ptr<common::ExtendedXmlDoc> cfgPtr{common::loadDefaultConfig()};
    common::PoolHolder dbHolder(*cfgPtr, "core", "grinder");
    auto deletingDatasets = getDeletingDatasets(dbHolder.pool());

    outputMonitoring(deletingDatasets, warnTimeout, errTimeout);

    return EXIT_SUCCESS;
} catch (const std::exception& e) {
    std::cout << "2;Error: " << e.what() << "\n";
    return EXIT_SUCCESS;
} catch (...) {
    std::cout << "2;Unknown error\n";
    return EXIT_SUCCESS;
}
