#include <maps/wikimap/mapspro/services/mrc/libs/db/include/eye/url_context.h>

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

#include <string>
#include <utility>

namespace maps::mrc::db::eye {

namespace {

const std::string SOURCE = "source";
const std::string FEATURE_ID = "feature_id";
const std::string MDS_GROUP_ID = "mds_group_id";
const std::string MDS_PATH = "mds_path";
const std::string OID = "oid";
const std::string HEADING = "heading";
const std::string TILT = "tilt";
const std::string HORIZONTAL_FOV = "horizontal_fov";
const std::string WIDTH = "width";
const std::string HEIGHT = "height";

template<typename Tagged>
Tagged jsonValueAs(const json::Value& value)
{
    return Tagged{value.as<typename Tagged::ValueType>()};
}

} // namespace

UrlContext::UrlContext(const json::Value& value)
    : value_(value)
{}

UrlContext::UrlContext(const MrcUrlContext& context)
    : value_{
        {SOURCE, json::Value(std::string(toString(FrameSource::Mrc)))},
        {FEATURE_ID, json::Value(context.featureId)},
        {MDS_GROUP_ID, json::Value(context.mdsGroupId)},
        {MDS_PATH, json::Value(context.mdsPath)},
    }
{}

UrlContext::UrlContext(const PanoramaUrlContext& context)
    : value_{
        {SOURCE, json::Value(std::string(toString(FrameSource::Panorama)))},
        {OID, json::Value(context.oid)},
        {HEADING, json::Value(context.heading.value())},
        {TILT, json::Value(context.tilt.value())},
        {HORIZONTAL_FOV, json::Value(context.horizontalFOV.value())},
        {WIDTH, json::Value(context.size.width)},
        {HEIGHT, json::Value(context.size.height)},
    }
{}

const json::Value& UrlContext::json() const {
    return value_;
}

FrameSource UrlContext::source() const {
    FrameSource source;
    fromString(value_[SOURCE].as<std::string>(), source);
    return source;
}

MrcUrlContext UrlContext::mrc() const {
    REQUIRE(source() == FrameSource::Mrc, "Incorrect context type");

    return MrcUrlContext{
        value_[FEATURE_ID].as<TId>(),
        value_[MDS_GROUP_ID].as<std::string>(),
        value_[MDS_PATH].as<std::string>()
    };
}

PanoramaUrlContext UrlContext::panorama() const {
    REQUIRE(source() == FrameSource::Panorama, "Incorrect context type");

    return PanoramaUrlContext{
        .oid = value_[OID].as<PanoramaOID>(),
        .heading = jsonValueAs<geolib3::Heading>(value_[HEADING]),
        .tilt = jsonValueAs<geolib3::Degrees>(value_[TILT]),
        .horizontalFOV = jsonValueAs<geolib3::Degrees>(value_[HORIZONTAL_FOV]),
        .size = common::Size{
            value_[WIDTH].as<std::size_t>(),
            value_[HEIGHT].as<std::size_t>()}};
}

} // namespace maps::mrc::eye
