#include "recent_patches.h"
#include "config.h"
#include <maps/libs/log8/include/log8.h>

namespace maps::wiki::merge_poi {

namespace {
const auto MERGE_POI_RECENT_PATCHES_TABLE = "sprav.merge_poi_recent_patches"s;
const auto OBJECT_ID_FIELD = "object_id"s;
const auto DATA_JSON_FIELD = "data_json"s;
} // namespace

RecentPatches::RecentPatches(const Config& cfg)
    : cfg_(cfg)
{
    auto socialTxn = cfg_.socialPool().slaveTransaction();
    auto rows = socialTxn->exec("SELECT * FROM " + MERGE_POI_RECENT_PATCHES_TABLE);
    for (const auto& row : rows) {
        objectIdToRecentPatch_.emplace(
            row[OBJECT_ID_FIELD].as<poi_feed::ObjectId>(),
            row[DATA_JSON_FIELD].as<std::string>());
    }
    INFO() << "Recent patches table size is: " << objectIdToRecentPatch_.size();
}

const poi_feed::FeedObjectData*
RecentPatches::find(poi_feed::ObjectId oid) const
{
    auto it = objectIdToRecentPatch_.find(oid);
    return
        it == objectIdToRecentPatch_.end()
        ? nullptr
        : &it->second;
}

PatchModifiedDataFlags
RecentPatches::compareToStored(const poi_feed::FeedObjectData& newPatch) const
{
    const auto* current = find(newPatch.nmapsId());
    if (!current) {
        return {
            PatchModifiedData::Names,
            PatchModifiedData::Permalink,
            PatchModifiedData::Coordinate};
    }
    PatchModifiedDataFlags compareResult;
    if (newPatch.names() != current->names() ||
        newPatch.shortNames() != current->shortNames())
    {
        compareResult.set(PatchModifiedData::Names);
    }
    if (newPatch.position() && current->position() &&
        *newPatch.position() != *current->position()) {
        compareResult.set(PatchModifiedData::Coordinate);
    }
    if (newPatch.permalink() != current->permalink()) {
        compareResult.set(PatchModifiedData::Permalink);
    }
    return compareResult;
}

void
RecentPatches::updateStorage(
    const poi_feed::FeedObjectDataVector& patches,
    const std::unordered_set<poi_feed::ObjectId>& modifiedObjectIds) const
{
    auto socialTxn = cfg_.socialPool().masterWriteableTransaction();
    for (const auto& newPatch : patches) {
        const auto objectId = newPatch.nmapsId();
        if (!modifiedObjectIds.count(objectId)) {
            continue;
        }
        const auto patchJson = newPatch.toJson();
        if (objectIdToRecentPatch_.count(objectId)) {
            socialTxn->exec(
                "UPDATE " + MERGE_POI_RECENT_PATCHES_TABLE +
                " SET applied_at = NOW(), " +
                DATA_JSON_FIELD + " = " + socialTxn->quote(patchJson) +
                " WHERE " + OBJECT_ID_FIELD + " = " + std::to_string(objectId));
        } else {
            socialTxn->exec(
                "INSERT INTO " + MERGE_POI_RECENT_PATCHES_TABLE +
                " (" + OBJECT_ID_FIELD + ", " + DATA_JSON_FIELD + ") VALUES"
                " (" + std::to_string(objectId) + ", " + socialTxn->quote(patchJson) + ");");
        }
    }
    socialTxn->commit();
}

} // namespace maps::wiki::merge_poi
