#include "entrances_with_flats_counter.h"
#include "utils.h"

#include <yandex/maps/wiki/revision/filters.h>
#include <yandex/maps/wiki/revision/revisionsgateway.h>

#include <algorithm>

namespace rev = maps::wiki::revision;
namespace filter = maps::wiki::revision::filters;

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

namespace {

rev::DBIDSet
entranceIdsWithFlatsChanges(
    const rev::RevisionsGateway& gateway,
    const std::vector<revision::DBID> commitIds)
{
    auto snapshot = gateway.snapshot(gateway.maxSnapshotId());

    auto flatRangesRelationsFilter = (
        filter::Attr(REL_ROLE) == FLAT_RANGE_ASSIGNED &&
        filter::ObjRevAttr::prevCommitId().isZero() &&
        filter::ObjRevAttr::isNotDeleted() &&
        filter::ObjRevAttr::isRelation() &&
        filter::CommitAttr::id().in(commitIds)
    );

    auto flatRangesRelations = snapshot.objectRevisionsByFilter(flatRangesRelationsFilter);
    if (flatRangesRelations.empty()) {
        return {};
    }

    rev::DBIDSet result;
    for (const auto& relation : flatRangesRelations) {
        ASSERT(relation.data().relationData);
        result.insert(relation.data().relationData->masterObjectId());
    }

    return result;
}

rev::DBIDSet
entranceIdsWithoutPrevFlats(
    const rev::RevisionsGateway& gateway,
    const rev::DBIDSet& entranceIds,
    rev::DBID commitId)
{
    auto snapshot = gateway.snapshot(commitId);

    auto flatRangesRelations = snapshot.loadSlaveRelations(entranceIds,
        filter::Attr(REL_ROLE) == FLAT_RANGE_ASSIGNED &&
        filter::ObjRevAttr::masterObjectId().in(entranceIds) &&
        filter::ObjRevAttr::isNotDeleted());

    auto result = entranceIds;
    for (const auto& flatRangeRelation : flatRangesRelations) {
        result.erase(
            flatRangeRelation.data().relationData->masterObjectId());
    }

    return result;
}

} // namespace

EntrancesWithFlatsCounter::EntrancesWithFlatsCounter(
    std::vector<revision::DBID> commitIds,
    pqxx::transaction_base& coreTxn)
    : ICommitsCounter(std::move(commitIds))
    , coreTxn_(coreTxn)
{}

int64_t EntrancesWithFlatsCounter::countImpl(
    const geolib3::MultiPolygon2& polygons)
{
    rev::RevisionsGateway gateway(coreTxn_);
    auto entranceIds = entranceIdsWithFlatsChanges(gateway, commitIds_);

    if (entranceIds.empty()) {
        return 0;
    }

    auto prevCommitId = *std::min_element(commitIds_.begin(), commitIds_.end()) - 1;
    auto entranceIdsToCount = entranceIdsWithoutPrevFlats(
        gateway, entranceIds, prevCommitId);

    if (entranceIdsToCount.empty()) {
        return 0;
    }

    auto snapshot = gateway.snapshot(gateway.maxSnapshotId());
    auto entranceRevisions = snapshot.objectRevisionsByFilter(
        filter::ObjRevAttr::objectId().in(entranceIdsToCount) &&
        filter::ObjRevAttr::isNotRelation() &&
        filter::ObjRevAttr::isNotDeleted() &&
        filter::Attr(CAT_POI_ENTRANCE).defined() &&
        filter::Geom::defined()
    );

    auto geosMultiPolygon = polygons.polygonsNumber()
        ? geolib3::internal::geolib2geosGeometry(polygons)
        : nullptr;

    return countObjectsInsideRegion(
        entranceRevisions,
        geosMultiPolygon
    );
}

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