#include "generic.h"
#include "utils.h"

#include "../message_reporter.h"

#include <yandex/maps/wiki/diffalert/diff_context.h>

namespace maps::wiki::diffalert {
namespace {
const double REAL_DISTANCE_1KM = 1000.0;
const double REAL_DISTANCE_10KM = 10000.0;
const double REAL_DISTANCE_100KM = 100000.0;
const double REAL_DISTANCE_1000KM = 1000000.0;

const std::string LOCATION_CHANGE_MESSAGE = "object-location-suspicious-change";
const std::string CONTOUR_PART_PREFIX = "contour-part-";
}

void checkCategoryChange(const DiffContext& diff, MessageReporter& messages, RunMode runMode)
{
    if (isPoi(diff.categoryId())) {
       return;
    }
    if (!diff.oldObject() || !diff.newObject()) {
        return;
    }
    if (diff.categoryChanged()) {
       messages.report(prioByRespGroup({1, 1}, diff), makePoiMessage("object-category-changed", diff, runMode));
    }
}

void checkLocationChange(const DiffContext& diff, MessageReporter& messages, RunMode runMode)
{
    if (!diff.oldObject() || !diff.newObject()) {
        return;
    }
    if (!diff.geomChanged()) {
        return;
    }
    if (diff.oldObject()->geom().isNull() || diff.newObject()->geom().isNull()) {
        return;
    }
    const auto& geomOld = diff.oldObject()->geom();
    const auto& geomNew = diff.newObject()->geom();
    const auto isPoint = (geomOld->getGeometryTypeId() == geos::geom::GEOS_POINT);
    geos::geom::Coordinate oldPosition;
    geos::geom::Coordinate newPosition;
    ASSERT(geomOld->getCentroid(oldPosition));
    ASSERT(geomNew->getCentroid(newPosition));
    double ratio = mercatorDistanceRatio((oldPosition.y + newPosition.y) * 0.5);
    auto distance = ratio * geomOld.distance(geomNew);
    Priority priority(10, 10);
    if (diff.oldObject()->categoryId() == cat::BLD) {
        if (distance > REAL_DISTANCE_10KM) {
            priority = Priority(0, 0);
        } else if (!geomNew->getEnvelopeInternal()->intersects(
                    geomOld->getEnvelopeInternal())) {
            priority = Priority(3, 0);
        } else {
            return;
        }
    } else if (distance > REAL_DISTANCE_1000KM) {
        priority = Priority(0, 0);
    } else if (distance > REAL_DISTANCE_100KM) {
        priority = Priority(1, 0);
    } else if (distance > REAL_DISTANCE_10KM) {
        priority = Priority(3, 0);
    } else if (distance > REAL_DISTANCE_1KM && isPoint) {
        priority = Priority(3, 0);
    } else if (!isPoint &&
        !geomNew->getEnvelopeInternal()->intersects(geomOld->getEnvelopeInternal())) {
        priority = Priority(3, 0);
    } else {
        return;
    }
    if (diff.oldObject()->isFaceJunction() || diff.oldObject()->isFaceElement()) {
        messages.report(
            priority,
            CONTOUR_PART_PREFIX + LOCATION_CHANGE_MESSAGE);
    } else {
        messages.report(
            prioByRespGroup(priority, diff),
            makePoiMessage(LOCATION_CHANGE_MESSAGE, diff, runMode));
    }
}

void checkCategoryChangeLongTask(const DiffContext& diff, MessageReporter& messages)
{
    checkCategoryChange(diff, messages, RunMode::LongTask);
}

void checkCategoryChangeModeration(const DiffContext& diff, MessageReporter& messages)
{
    checkCategoryChange(diff, messages, RunMode::Moderation);
}

void checkLocationChangeLongTask(const DiffContext& diff, MessageReporter& messages)
{
    checkLocationChange(diff, messages, RunMode::LongTask);
}

void checkLocationChangeModeration(const DiffContext& diff, MessageReporter& messages)
{
    checkLocationChange(diff, messages, RunMode::Moderation);
}

} // namespace maps::wiki::diffalert
