#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/objects/include/road.h>

#include <maps/libs/common/include/exception.h>

#include <maps/libs/geolib/include/polyline.h>
#include <maps/libs/geolib/include/test_tools/comparison.h>

#include <maps/wikimap/mapspro/services/autocart/libs/geometry/include/hex_wkb.h>

#include <mapreduce/yt/interface/client.h>

namespace maps::wiki::autocart::pipeline {

namespace {

static const TString SHAPE = "shape";
static const TString RD_EL_FC = "fc";
static const TString RD_EL_FOW = "fow";
static const TString ROAD_LIST = "roads";

} // namespace

Road Road::fromYTNode(const NYT::TNode& node) {
    Road road;
    road.geoGeom_ = hexWKBToPolyline(node[SHAPE].AsString());
    if (!node[RD_EL_FC].IsUndefined() && !node[RD_EL_FC].IsNull()) {
        road.setFc(node[RD_EL_FC].AsInt64());
    }
    if (!node[RD_EL_FOW].IsUndefined() && !node[RD_EL_FOW].IsNull()) {
        road.setFow(node[RD_EL_FOW].AsInt64());
    }
    return road;
}

NYT::TNode Road::toYTNode() const {
    NYT::TNode node;
    toYTNode(node);
    return node;
}

void Road::toYTNode(NYT::TNode& node) const {
    node[SHAPE] = TString(polylineToHexWKB(geoGeom_));
    if (hasFc()) {
        node[RD_EL_FC] = getFc();
    }
    if (hasFow()) {
        node[RD_EL_FOW] = getFow();
    }
}


bool Road::operator==(const Road& that) const {
    return geolib3::test_tools::approximateEqual(geoGeom_, that.geoGeom_, geolib3::EPS)
        && fc_ == that.fc_
        && fow_ == that.fow_;
}

bool Road::hasFc() const {
    return fc_.has_value();
}

bool Road::hasFow() const {
    return fow_.has_value();
}

void Road::setFc(int64_t fc) {
    fc_ = fc;
}

void Road::setFow(int64_t fow) {
    fow_ = fow;
}

int64_t Road::getFc() const {
    REQUIRE(hasFc(), "fc is not defined");
    return fc_.value();
}

int64_t Road::getFow() const {
    REQUIRE(hasFow(), "fow is not defined");
    return fow_.value();
}


std::vector<Road> roadsFromYTNode(const NYT::TNode& node) {
    NYT::TNode roadList = node[ROAD_LIST];
    std::vector<Road> roads;
    roads.reserve(roadList.AsList().size());
    for (size_t i = 0; i < roadList.AsList().size(); i++) {
        roads.push_back(Road::fromYTNode(roadList[i]));
    }
    return roads;
}

void roadsToYTNode(const std::vector<Road>& roads, NYT::TNode& node) {
    NYT::TNode roadList = NYT::TNode::CreateList();
    for (const Road& road : roads) {
        roadList.Add(road.toYTNode());
    }
    node[ROAD_LIST] = roadList;
}

} // namespace maps::wiki::autocart::pipeline
