#include "bbox_writer.h"
#include <maps/wikimap/mapspro/services/editor/src/sync/db_helpers.h>

#include <yandex/maps/wiki/common/batch.h>
#include <yandex/maps/wiki/common/pg_utils.h>

namespace maps {
namespace wiki {

namespace {

const std::string TABLE_GEOMETRY = "revision_meta.geometry";

const size_t OBJECTS_BATCH_COUNT = 500;

} //namespace

BBoxWriter::BBoxWriter(BBoxContext context)
    : context_(std::move(context))
{
}

void BBoxWriter::setInitialGeom(TOid objectId, const Geom& geom)
{
    currentCachedGeometries_[objectId] = geom;
}

void BBoxWriter::writeToDb(TCommitId commitId, TOid objectId, const std::string& categoryId, const Geom& geom)
{
    auto it = currentCachedGeometries_.find(objectId);
    if (it != currentCachedGeometries_.end()) {
        const auto& cachedGeom = it->second;
        if (geom.equal(cachedGeom, CALCULATION_TOLERANCE)) {
            return;
        }
    }

    records_.push_back(Record{commitId, objectId, categoryId, geom});

    currentCachedGeometries_[objectId] = geom;
}

void BBoxWriter::flush(pqxx::transaction_base& txn)
{
    if (records_.empty()) {
        return;
    }

    for (const auto& record : records_) {
        //for historical reasons write object category to the table as hstore
        //key: category, value: 1
        StringMap categories{{plainCategoryIdToCanonical(record.categoryId), "1"}};

        auto geomString = geomToSqlString(txn, record.geom);

        std::ostringstream query;
        query << "INSERT INTO " << TABLE_GEOMETRY << " (object_id, commit_id, branch_id, the_geom, categories) VALUES "
            << "(" << record.objectId << ","
            << record.commitId << ","
            << context_.branchId() << ","
            << geomString << ", "
            << common::attributesToHstore(txn, categories) << ") "
            << "ON CONFLICT (object_id, commit_id, branch_id) DO UPDATE "
            << "SET the_geom=" << geomString;

        txn.exec(query.str());
    }

    records_.clear();
}

void BBoxWriter::clearOldBBoxes(pqxx::transaction_base& txn, const TOIds& objectIdsToClear)
{
    common::applyBatchOp<TOIds>(
        objectIdsToClear,
        OBJECTS_BATCH_COUNT,
        [&, this](const TOIds& batch) {
            std::ostringstream query;
            query << "DELETE FROM " << TABLE_GEOMETRY
                << "  WHERE branch_id=" << context_.branchId()
                << "    AND commit_id BETWEEN " << context_.startCommitId() << " AND " << context_.endCommitId()
                << "    AND " << common::whereClause("object_id", batch);

            txn.exec(query.str());
        });
}

} // namespace wiki
} // namespace maps
