#include <maps/wikimap/mapspro/services/mrc/libs/toloka_manager/include/detection_missing_on_frame.h>
#include <maps/libs/json/include/builder.h>
#include <maps/libs/json/include/std.h>
#include <maps/libs/json/include/value.h>

namespace maps::mrc::toloka {

constexpr maps::enum_io::Representations<DetectionMissingOnFrameIsVisible>
DETECTION_MISSING_ON_FRAME_IS_VISIBLE_TO_STRING{
    {DetectionMissingOnFrameIsVisible::Yes, "yes"},
    {DetectionMissingOnFrameIsVisible::No, "no"},
    {DetectionMissingOnFrameIsVisible::NotLoaded, "not_loaded"},
    {DetectionMissingOnFrameIsVisible::Unknown, "unknown"},
};

DEFINE_ENUM_IO(DetectionMissingOnFrameIsVisible, DETECTION_MISSING_ON_FRAME_IS_VISIBLE_TO_STRING);

constexpr maps::enum_io::Representations<DetectionMissingOnFrameMissingReason>
DETECTION_MISSING_ON_FRAME_MISSING_REASON_TO_STRING{
    {DetectionMissingOnFrameMissingReason::Missing, "missing"},
    {DetectionMissingOnFrameMissingReason::Hidden, "hidden"},
    {DetectionMissingOnFrameMissingReason::PlaceIsNotVisible, "place_is_not_visible"},
    {DetectionMissingOnFrameMissingReason::Unknown, "unknown"},
};

DEFINE_ENUM_IO(DetectionMissingOnFrameMissingReason, DETECTION_MISSING_ON_FRAME_MISSING_REASON_TO_STRING);


void DetectionMissingOnFrameInput::json(json::ObjectBuilder builder) const
{
    builder[FIELD_IMAGE_1] = imageUrl1_;
    builder[FIELD_BBOX_1] = bbox1_;

    builder[FIELD_IMAGE_2] = imageUrl2_;
}

template <>
DetectionMissingOnFrameInput parseJson(const json::Value& jsonInput)
{
    return DetectionMissingOnFrameInput(
        jsonInput[FIELD_IMAGE_1].as<std::string>(),
        common::ImageBox::fromJson(jsonInput[FIELD_BBOX_1]),
        jsonInput[FIELD_IMAGE_2].as<std::string>()
    );
}

void DetectionMissingOnFrameOutput::json(json::ObjectBuilder builder) const
{
    builder[FIELD_IS_VISIBLE] = toString(isVisible_);

    if (missingReason_.has_value()) {
        builder[FIELD_MISSING_REASON] = toString(missingReason_.value());
    }
}

template <>
DetectionMissingOnFrameOutput parseJson(const json::Value& jsonOutput)
{
    DetectionMissingOnFrameIsVisible isVisible;
    fromString(jsonOutput[FIELD_IS_VISIBLE].as<std::string>(), isVisible);

    if (jsonOutput.hasField(FIELD_MISSING_REASON)) {
        DetectionMissingOnFrameMissingReason missingReason;
        fromString(jsonOutput[FIELD_MISSING_REASON].as<std::string>(), missingReason);

        return DetectionMissingOnFrameOutput(isVisible, missingReason);
    } else {
        return DetectionMissingOnFrameOutput(isVisible, std::nullopt);
    }
}

DetectionMissingOnFrameInput::DetectionMissingOnFrameInput(
    std::string imageUrl1, common::ImageBox bbox1,
    std::string imageUrl2)
    : imageUrl1_(std::move(imageUrl1)), bbox1_(std::move(bbox1))
    , imageUrl2_(std::move(imageUrl2))
{
}

const std::string& DetectionMissingOnFrameInput::imageUrl1() const { return imageUrl1_; }

const common::ImageBox& DetectionMissingOnFrameInput::bbox1() const { return bbox1_; }

const std::string& DetectionMissingOnFrameInput::imageUrl2() const { return imageUrl2_; }


DetectionMissingOnFrameOutput::DetectionMissingOnFrameOutput(
    DetectionMissingOnFrameIsVisible isVisible,
    std::optional<DetectionMissingOnFrameMissingReason> missingReason)
    : isVisible_(isVisible)
    , missingReason_(missingReason)
{
}

const DetectionMissingOnFrameIsVisible& DetectionMissingOnFrameOutput::isVisible() const {
    return isVisible_;
}

const std::optional<DetectionMissingOnFrameMissingReason>& DetectionMissingOnFrameOutput::missingReason() const {
    return missingReason_;
}

bool operator==(const DetectionMissingOnFrameOutput& lhs, const DetectionMissingOnFrameOutput& rhs)
{
    return lhs.introspect() == rhs.introspect();
}

bool operator<(const DetectionMissingOnFrameOutput& lhs, const DetectionMissingOnFrameOutput& rhs)
{
    return lhs.introspect() < rhs.introspect();
}

std::ostream& operator<<(std::ostream& out, const DetectionMissingOnFrameOutput& taskOutput)
{
    json::Builder builder(out);
    builder << [&](json::ObjectBuilder b) {
        taskOutput.json(b);
    };
    return out;
}

} // namespace maps::mrc::toloka
