#include <maps/wikimap/mapspro/services/tasks-ng/lib/task_type.h>

#include <maps/wikimap/mapspro/services/tasks-ng/lib/exception.h>
#include <maps/wikimap/mapspro/services/tasks-ng/lib/magic_strings.h>
#include <maps/wikimap/mapspro/services/tasks-ng/lib/query.h>

#include <maps/libs/json/include/value.h>
#include <maps/libs/xml/include/xml.h>

namespace maps {
namespace wiki {
namespace tasks_ng {

const std::string PARAMETER = "parameter";
const std::string PARAMETERS = "parameters";
const std::string KEY = "key";
const std::string VALUE = "value";

wiki::common::Attributes getTaskTypeParameters(
    DbContext& ctx,
    const std::string& taskType)
{
    auto& txnCore = ctx.txnCore();

    auto query = Query()
        .select("hstore_to_json(attributes)")
        .from(sql::table::TASK_TYPE)
        .where() << "name=" << txnCore.quote(taskType);
    auto res = txnCore.exec(query.str());
    if (res.empty()) {
        return {};
    }

    wiki::common::Attributes attrs;
    auto json = json::Value::fromString(res[0][0].as<std::string>());
    for (const auto& key : json.fields()) {
        attrs.emplace(key, json[key].toString());
    }
    return attrs;
}

void setTaskTypeParameters(
    DbContext& ctx,
    const std::string& taskType,
    const wiki::common::Attributes& attrs)
{
    auto& txnCore = ctx.txnCore();

    auto hstore = common::attributesToHstore(txnCore, attrs);
    auto query = Query()
        .update(sql::table::TASK_TYPE)
        .set("attributes = attributes || " + hstore)
        .where() << "name = " << txnCore.quote(taskType);

    auto res = txnCore.exec(query.str());
    if (res.affected_rows() == 0) {
        auto query = Query()
            .insertInto(sql::table::TASK_TYPE)
            .columns("name, attributes")
            .values(Query() << txnCore.quote(taskType) << ',' << hstore);
        txnCore.exec(query.str());
    }
}

wiki::common::Attributes parseTaskTypeParameters(const std::string& xml)
{
    auto checkNodeName = [](auto node, auto name) {
        TASKS_REQUIRE(
            node.name() == name,
            ERR_BAD_REQUEST,
            "Not parameters node : " << node.name());
    };

    auto doc = xml3::Doc::fromString(xml);
    auto root = doc.root();
    checkNodeName(root, PARAMETERS);

    wiki::common::Attributes attrs;
    for (auto node = root.firstElementChild(); !node.isNull(); node = node.nextElementSibling()) {
        checkNodeName(node, PARAMETER);
        attrs.emplace(node.attr<std::string>(KEY), node.attr<std::string>(VALUE));
    }
    return attrs;
}

void makeResponseTaskTypeParameters(
    XmlResponseWrapper& wrapper,
    const wiki::common::Attributes& attrs)
{
    auto& writer = wrapper.writer();

    wrapper.write(PARAMETERS, [&] {
        for (const auto& pair : attrs) {
            writer.addTag(PARAMETER, [&] {
                writer
                    .addAttribute(KEY, pair.first)
                    .addAttribute(VALUE, pair.second);
            });
        }
    });
}

} // namespace tasks_ng
} // namespace wiki
} // namespace maps
