#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/libs/privacy_detector/include/privacy_detector_faster_rcnn.h>

#include <maps/libs/cmdline/include/cmdline.h>
#include <maps/libs/log8/include/log8.h>
#include <maps/libs/http/include/http.h>
#include <maps/libs/common/include/retry.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/types.h>
#include <maps/wikimap/mapspro/services/mrc/libs/config/include/config.h>

#include <maps/wikimap/mapspro/services/mrc/libs/common/include/pg_locks.h>
#include <yandex/maps/pgpool3utils/pg_advisory_mutex.h>

using namespace maps::mrc;

common::Bytes load(const std::string& url)
{
    INFO() << "Loading image by url " << url;
    static constexpr int HTTP_STATUS_OK = 200;

    maps::http::Client client;
    client.setTimeout(std::chrono::seconds(5));

    return retry(
        [&]() -> common::Bytes {
            maps::http::Request request(client, maps::http::GET, maps::http::URL(url));
            auto response = request.perform();

            if (response.status() != HTTP_STATUS_OK) {
                throw maps::RuntimeError() << "Http status " << response.status();
            }

            return response.readBodyToVector();
        },
        ::maps::common::RetryPolicy().setTryNumber(5)
    );
}


db::Features loadFeatures(maps::pgpool3::Pool& pool, db::TId minId, db::TId maxId) {
    auto txn = pool.slaveTransaction();
    db::FeatureGateway gateway(*txn);
    return gateway.load(db::table::Feature::id >= minId && db::table::Feature::id <= maxId);
}

int main(int argc, const char** argv) try
{
    maps::cmdline::Parser parser("Local detector of privacy objects on image");
    auto syslog = parser.string("syslog-tag")
        .help("redirect log output to syslog with given tag");

    auto configPath = parser.string("config").help("path to configuration");

    auto minId = parser.num("min-id").help("min feature id").required();
    auto maxId = parser.num("max-id").help("max feature id").required();

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

    if (syslog.defined()) {
        maps::log8::setBackend(maps::log8::toSyslog(syslog));
    }

    auto mrcConfig = maps::mrc::common::templateConfigFromCmdPath(configPath);
    auto mds = mrcConfig.makeMdsClient();

    maps::wiki::common::PoolHolder poolHolder(mrcConfig.makePoolHolder());


    maps::mrc::privacy_detector::FasterRCNNDetector privacyDetector;

    auto features = loadFeatures(poolHolder.pool(), minId, maxId);
    for (const auto& feature : features) {
        auto url = mds.makeReadUrl(feature.mdsKey());
        auto imageBytes = load(url);

        cv::Mat image = maps::mrc::common::decodeImage(imageBytes);
        cv::Mat normalizedImage = maps::mrc::common::transformByImageOrientation(image, feature.orientation());

        maps::mrc::privacy_detector::PrivacyImageBoxes privacyObjects = privacyDetector.detect(normalizedImage);
        INFO () << "Feature " << feature.id() << ". number of objects detected: " << privacyObjects.size();
    }
    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;
}
