#include "street.h"
#include "common.h"
#include <maps/libs/common/include/exception.h>
#include <set>

namespace maps::wiki::schedule_feedback {

namespace rf = revision::filters;

namespace {

std::vector<ObjId> roadElementIds(
    const geolib3::BoundingBox& bboxMerc,
    const revision::Snapshot& snapshot)
{
    rf::ProxyFilterExpr roadElementFilter =
        rf::ObjRevAttr::isNotDeleted() &&
        rf::ObjRevAttr::isNotRelation() &&
        rf::Attr("cat:rd_el").defined() &&
        rf::Geom::defined();

    auto areaFilter = rf::GeomFilterExpr(
        rf::GeomFilterExpr::Operation::IntersectsLinestrings,
        bboxMerc.minX(), bboxMerc.minY(),
        bboxMerc.maxX(), bboxMerc.maxY());

    auto filter = roadElementFilter && areaFilter;
    auto rdelRevIds = snapshot.revisionIdsByFilter(filter);

    std::vector<ObjId> ids;
    for (const auto& revId : rdelRevIds) {
        ids.push_back(revId.objectId());
    }
    return ids;
}

std::set<ObjId> loadMasterRoads(
    const std::vector<ObjId>& slaveObjIds,
    const revision::Snapshot& snapshot)
{
    if (slaveObjIds.empty()) {
        return {};
    }

    std::set<ObjId> masterIds;
    for (const auto& relation : snapshot.loadMasterRelations(slaveObjIds)) {
        const auto& relData = relation.data().relationData;
        ASSERT(relData);
        masterIds.insert(relData->masterObjectId());
    }

    auto masterRevs = snapshot.objectRevisions(masterIds);

    std::set<ObjId> roads;
    for (const auto& [masterId, masterRev] : masterRevs) {
        bool deleted = masterRev.data().deleted;
        const auto& attrs = masterRev.data().attributes;

        if (attrs && attrs->count("cat:rd") && !deleted) {
            roads.insert(masterId);
        }
    }
    return roads;
}

std::string revisionStreetNameFromRoad(
    ObjId roadId,
    const revision::Snapshot& snapshot)
{
    return extractOfficialLocalName(roadId, "rd_nm", snapshot);
}

} // anonymous


std::string revisionStreetName(
    ObjId revisionAddressId,
    const revision::Snapshot& snapshot)
{
    auto roads = loadMasterRoads({revisionAddressId}, snapshot);
    ASSERT(roads.size() == 1);
    return revisionStreetNameFromRoad(*roads.begin(), snapshot);
}

std::vector<std::string> revisionStreetNames(
    const geolib3::BoundingBox& bboxMerc,
    const revision::Snapshot& snapshot)
{
    auto rdEls = roadElementIds(bboxMerc, snapshot);
    auto roads = loadMasterRoads(rdEls, snapshot);
    std::vector<std::string> streets;
    for (const auto& road : roads) {
        streets.push_back(revisionStreetNameFromRoad(road, snapshot));
    }
    return streets;
}

} // namespace maps::wiki::schedule_feedback
