#pragma once

#include <yandex/maps/wiki/social/event.h>
#include <yandex/maps/wiki/social/i_feed.h>
#include <yandex/maps/wiki/revision/revisionsgateway.h>

#include <geos/geom/MultiPolygon.h>

#include <map>
#include <memory>
#include <set>
#include <string>

namespace maps::wiki::tasks::involvement {

const std::string CATEGORY_PREFIX = "cat:";

const std::string CAT_COND_TRAFFIC_LIGHT = CATEGORY_PREFIX + "cond_traffic_light";
const std::string CAT_FLAT_RANGE = CATEGORY_PREFIX + "flat_range";
const std::string CAT_PARKING_LOTS_LINEAR = CATEGORY_PREFIX + "parking_lots_linear";
const std::string CAT_POI_ENTRANCE = CATEGORY_PREFIX + "poi_entrance";
const std::string CAT_POI_URBAN = CATEGORY_PREFIX + "poi_urban";
const std::string CAT_RD_EL = CATEGORY_PREFIX + "rd_el";
const std::string CAT_RD_JC = CATEGORY_PREFIX + "rd_jc";
const std::string CAT_URBAN_ROADNET_FENCE_EL = CATEGORY_PREFIX + "urban_roadnet_fence_el";

const std::string ATTR_SPEED_LIMIT = "rd_el:speed_limit";
const std::string ATTR_PAVED = "rd_el:paved";
const std::string ATTR_FUNCTIONAL_CLASS = "rd_el:fc";
const std::string ATTR_PREFIX_PRIMARY_OBJECT = "primary_object:";

const std::string REL_ROLE = "rel:role";
const std::string FLAT_RANGE_ASSIGNED = "flat_range_assigned";

const std::string ACTION = "action";
const std::string OBJECT_MODIFIED = "object-modified";
const std::string GROUP_MODIFY_ATTRIBUTES = "group-modified-attributes";

const std::string FT_TYPE_URBAN_BARBEQUE = "1906";
const std::string FT_TYPE_URBAN_BENCH = "1907";
const std::string FT_TYPE_URBAN_PAVILION = "1908";

const std::string FUNCTIONAL_CLASS_PEDESTRIAN = "10";

const std::set<std::string> COVID_CATEGORY_GROUPS = {
    "bld_group",
    "addr_group",
    "entrance_group",
    "poi_group",
    "fence_group",
    "parking_group",
    "cond_group",
    "rd_group",
    "ad_group"
};

enum class EAttributeChangeType
{
    UNCHANGED,
    SET,
    UNSET
};

using RevisionIdToRevision = std::map<
    maps::wiki::revision::RevisionID,
    maps::wiki::revision::ObjectRevision>;

using RdElRevisionCounter = std::function<int64_t(
    const maps::wiki::revision::ObjectRevision&,
    const EAttributeChangeType)>;

/**
 * Functions that create filters to get revisions for processing.
 * @param commitIDs - ids of commits to choose for processing.
 */
using RevisionFilterGetter = std::function<
    maps::wiki::revision::filters::BinaryFilterExpr(
    const std::vector<maps::wiki::revision::DBID>& commitIDs)>;

size_t countObjectsInsideRegion(
    const maps::wiki::revision::Revisions& revisions,
    std::shared_ptr<const geos::geom::MultiPolygon> geosMultiPolygon);

int64_t countLengthOnAnyChangeOfAttribute(
    const maps::wiki::revision::ObjectRevision& revision,
    const EAttributeChangeType pavedChangeType);

/**
 * Prevent cheating by series of attribute setting and unsetting revisions.
 */
int64_t countLengthAvoidingCheating(
    const maps::wiki::revision::ObjectRevision& revision,
    const EAttributeChangeType speedLimitChangeType);

// Commits that are made by Ya users are thrown away
social::Events
filterCommitEventsByNonYaUsers(
    pqxx::transaction_base& coreTxn,
    const social::Events& commitEvents);

std::optional<geolib3::BoundingBox>
extractBoundingBoxMerc(const social::Event& event);

// 'Multipolygon' with zero polygonsNumber means the whole world,
// so it contains any object even without bounding box
bool isMultipolygonIntersectsBoundingBox(
    const geolib3::MultiPolygon2& multipolygon,
    const std::optional<geolib3::BoundingBox>& boundingBox);

maps::wiki::revision::filters::BinaryFilterExpr
getFilterForAnyRdElRevisions(
    const std::vector<maps::wiki::revision::DBID>& commitIDs);

maps::wiki::revision::filters::BinaryFilterExpr
getFilterForRdElModifyingRevisions(
    const std::vector<maps::wiki::revision::DBID>& commitIDs);

/**
 * Count input for involvement counter (in meters)
 * for commits with rd_el attribute change.
 * @param lengthCounter - Function used to process a single commit.
 * @param attribute - attribute which change is tracked.
 */
int64_t countLengthOfChangedRdElements(
    const maps::wiki::revision::RevisionsGateway& gateway,
    const std::vector<maps::wiki::revision::DBID>& commitIds,
    const geolib3::MultiPolygon2& polygons,
    const RevisionFilterGetter& filterGetter,
    const RdElRevisionCounter& lengthCounter,
    const std::string& attribute);

} // namespace maps::wiki::tasks::involvement
