#include "geobase.h"
#include <maps/libs/log8/include/log8.h>

#include <util/generic/iterator_range.h>

namespace maps::mrc::geobase {

GeobaseRegion Geobase6Adapter::getRegionById(int32_t id) const
{
    auto region = geobaseLookup_.GetRegionById(id);
    return GeobaseRegion{
        .id = id, .type = region.GetType(), .name = region.GetName().c_str()};
}

std::vector<int32_t> Geobase6Adapter::getRegionParentIds(int32_t id) const
{
    return geobaseLookup_.GetParentsIds(id);
}

int32_t Geobase6Adapter::getRegionIdByLocation(
    const geolib3::Point2& geoPoint) const
{
    return geobaseLookup_.GetRegionIdByLocation(geoPoint.y(), geoPoint.x());
}

PatchedGeobaseAdapter::PatchedGeobaseAdapter(
    GeobasePtr geobasePtr,
    std::unordered_map<int32_t, GeobaseRegion> regions,
    std::unordered_multimap<int32_t, int32_t> regionToParentRegion)
    : geobasePtr_(std::move(geobasePtr))
    , regions_(std::move(regions))
    , regionToParentRegion_(std::move(regionToParentRegion))
{}

GeobaseRegion PatchedGeobaseAdapter::getRegionById(int32_t id) const
{
    auto it = regions_.find(id);
    if (it != regions_.end()) {
        return it->second;
    }
    return geobasePtr_->getRegionById(id);
}

std::vector<int32_t> PatchedGeobaseAdapter::getRegionParentIds(
    int32_t id) const
{
    std::vector<int32_t> result;

    if (!regions_.count(id)) {
        result = geobasePtr_->getRegionParentIds(id);
    }

    for (auto [_, regionId]: MakeIteratorRange(regionToParentRegion_.equal_range(id)))
    {
        result.push_back(regionId);
    }
    return result;
}

int32_t PatchedGeobaseAdapter::getRegionIdByLocation(
    const geolib3::Point2& geoPoint) const
{
    return geobasePtr_->getRegionIdByLocation(geoPoint);
}


GeobasePtr loadPatchedGeobasePtr(
    NYT::IClientPtr ytClient,
    const std::string& geodataPatchYtDir,
    GeobasePtr geobasePtr)
{
    INFO() << "Loading patched geobase from " << geodataPatchYtDir;
    std::unordered_map<int32_t, GeobaseRegion> regions;
    std::unordered_multimap<int32_t, int32_t> regionToParentRegion;
    const TString geodataPatchYtDirTStr(geodataPatchYtDir.c_str());

    auto newRegionsReader = ytClient->CreateTableReader<NYT::TNode>(geodataPatchYtDirTStr + "/facets_cfg");

    for (; newRegionsReader->IsValid(); newRegionsReader->Next()) {
        const NYT::TNode& row = newRegionsReader->GetRow();
        int32_t regionId = row["facet_id"].IntCast<int32_t>();
        regions.emplace(
            regionId,
            GeobaseRegion{
                .id = regionId,
                .type = 4 /* Federal district */,
                .name = row["name"].AsString()
            });
    }

    auto regionTableReader = ytClient->CreateTableReader<NYT::TNode>(geodataPatchYtDirTStr + "/major_regions_map");
    for (; regionTableReader->IsValid(); regionTableReader->Next()) {
        const NYT::TNode& row = regionTableReader->GetRow();
        int32_t parentRegionId = std::stoi(row["major_id"].AsString());
        if (regions.count(parentRegionId)) {
            regionToParentRegion.emplace(
                std::stoi(row["region_id"].AsString()),
                parentRegionId
            );
        }
    }
    return std::make_shared<PatchedGeobaseAdapter>(
        std::move(geobasePtr),
        std::move(regions),
        std::move(regionToParentRegion)
    );
}

} // namespace maps::mrc::geobase
