#include "diff_details.h"
#include "maps/wikimap/mapspro/services/editor/src/configs/config.h"
#include "maps/wikimap/mapspro/services/editor/src/objects/object.h"

#include <yandex/maps/wiki/configs/editor/categories.h>
#include <yandex/maps/wiki/common/string_utils.h>

namespace maps {
namespace wiki {
namespace {
const std::string ATTRIBUTE_FT_TYPE_ID_SUFFIX = "ft_type_id";
void
removeNotConfiguredAttributes(
    revision::AttributesDiff& attrDiffs,
    const std::string& categoryId)
{
    const auto& categories = cfg()->editor()->categories();
    REQUIRE(categories.defined(categoryId),
        "Category " << categoryId << " not defined while building diff.");
    StringSet definedAttributes;
    for (const auto& attrDef : categories[categoryId].attrDefs()) {
        definedAttributes.insert(attrDef->id());
    }
    auto it = attrDiffs.begin();
    while (it != attrDiffs.end()) {
        const auto& attrName = it->first;
        if (definedAttributes.count(attrName)) {
           ++it;
        } else {
            it = attrDiffs.erase(it);
        }
    }
}

revision::AttributesDiff
updateCategoryPrefix(const revision::AttributesDiff& attrDiffs, const revision::StringDiff& categoryDiff)
{
    revision::AttributesDiff fixedDiff;
    for (const auto& attrDiff : attrDiffs) {
        const auto& attrName = attrDiff.first;
        if (!boost::algorithm::starts_with(attrName, categoryDiff.before)) {
            fixedDiff.insert(attrDiff);
        }
    }
    for (const auto& attrDiff : attrDiffs) {
        const auto& attrName = attrDiff.first;
        const auto& valueDiff = attrDiff.second;
        if (!boost::algorithm::starts_with(attrName, categoryDiff.before)) {
            continue;
        }
        if (boost::algorithm::ends_with(attrName, ATTRIBUTE_FT_TYPE_ID_SUFFIX)) {
            continue;
        }
        auto updatedName = boost::replace_all_copy(
            attrName, categoryDiff.before, categoryDiff.after);
        auto it = fixedDiff.find(updatedName);
        if (it == fixedDiff.end()) {
            fixedDiff.insert({updatedName, valueDiff});
            continue;
        }
        if (it->second.after != valueDiff.before) {
            it->second.before = valueDiff.before;
        } else {
            fixedDiff.erase(it);
        }
    }
    removeNotConfiguredAttributes(fixedDiff, categoryDiff.after);
    return fixedDiff;
}
}// namespace

bool DiffDetails::hasErrors() const
{
    return threadStopDiff && threadStopDiff->brokenSequence;
}

void
fillCategoryDiff(DiffDetails& details) {
    if (details.fromObjectVersion && details.toObjectVersion &&
        details.fromObjectVersion->categoryId() != details.toObjectVersion->categoryId()) {
        details.categoryDiff = revision::StringDiff {
            details.fromObjectVersion->categoryId(),
            details.toObjectVersion->categoryId()
        };
        details.attributesDiff = updateCategoryPrefix(
            details.attributesDiff,
            *details.categoryDiff);
    }
}

} // namespace wiki
} // namespace maps
