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

namespace maps {
namespace mrc {
namespace toloka {

static constexpr enum_io::Representations<ApprovementAnswer> APPROVEMENT_ANSWER_REPRESENTATION{
    {ApprovementAnswer::Yes, "yes"},
    {ApprovementAnswer::No, "no"},
    {ApprovementAnswer::NotLoaded, "not-loaded"}};
DEFINE_ENUM_IO(ApprovementAnswer, APPROVEMENT_ANSWER_REPRESENTATION);

void ApprovementInput::json(json::ObjectBuilder builder) const
{
    builder[FIELD_IMAGE] = imageUrl();
    if (!bboxes_.empty()) {
        builder[FIELD_BBOXES] = [this](json::ArrayBuilder b) {
            b.putRange(bboxes_.begin(), bboxes_.end());
        };
    }
}

template <>
ApprovementInput parseJson(const json::Value& jsonInput)
{
    auto imageUrl = jsonInput[FIELD_IMAGE].as<std::string>();
    std::vector<common::ImageBox> bboxes;
    if (jsonInput.hasField(FIELD_BBOXES)
        && !jsonInput[FIELD_BBOXES].isNull()) {
        auto jsonBboxes = jsonInput[FIELD_BBOXES];
        REQUIRE(jsonBboxes.isArray(), "Invalid approvement input format");
        for (const auto& jsonBbox : jsonBboxes) {
            bboxes.push_back(common::ImageBox::fromJson(jsonBbox));
        }
    }
    return {imageUrl, bboxes};
}

void ApprovementOutput::json(json::ObjectBuilder builder) const
{
    builder[FIELD_STATE] = std::string(toString(answer_));
}

template <>
ApprovementOutput parseJson(const json::Value& jsonOutput)
{
    ApprovementAnswer answer;
    fromString(jsonOutput[FIELD_STATE].as<std::string>(), answer);
    return {answer};
}

/**
 * ApprovementInput implementation
 */

ApprovementInput::ApprovementInput(std::string imageUrl,
                                   std::vector<common::ImageBox> bboxes)
    : imageUrl_(std::move(imageUrl)), bboxes_(std::move(bboxes))
{
}

const std::string& ApprovementInput::imageUrl() const { return imageUrl_; }

const std::vector<common::ImageBox>& ApprovementInput::bboxes() const
{
    return bboxes_;
}

ApprovementInput& ApprovementInput::setImageUrl(std::string url)
{
    imageUrl_ = url;
    return *this;
}

ApprovementOutput::ApprovementOutput(ApprovementAnswer answer)
    : answer_(answer)
{
}

ApprovementAnswer ApprovementOutput::answer() const { return answer_; }

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

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

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


} // namespace toloka
} // namespace mrc
} // namespace maps
