#include "../common.h"
#include "../categories.h"
#include "../magic_strings.h"
#include "../attribute_dumper.h"

#include <yandex/maps/wiki/common/pg_utils.h>

#include <set>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>

namespace maps {

namespace {
const std::string META = "meta";
const std::string META_ID = "meta_id";
const std::string META_PARAM_KEYS = "meta_param_keys";
const std::string META_PARAM_VALS = "meta_param_vals";
const std::string TYPE = "type";
const std::string DELIM = "=";
} // namespace

struct MetaCategory: public Category {
    const Table& table() const override
    {
        return table::META;
    }

    std::string loadIdsSql() const override
    {
        return "SELECT meta_id FROM meta WHERE meta_id > 0";
    }

    std::string loadRowsSqlTemplate() const override
    {
        return "WITH paged_meta AS (SELECT * FROM meta WHERE meta_id IN %1%) "
            "SELECT"
            " paged_meta." + META_ID + " " + ID + "," +
            " paged_meta." + TYPE + "," +
            " st_asgeojson(paged_meta." + SHAPE + ")" + SHAPE + ", " +
            " array_agg(meta_param.key) " + META_PARAM_KEYS + ", " +
            " array_agg(meta_param.value) " + META_PARAM_VALS + " " +
            "FROM paged_meta "
            "LEFT JOIN meta_param ON (meta_param.meta_id = paged_meta.meta_id) "
            "GROUP BY"
            " paged_meta.meta_id,"
            " paged_meta.type,"
            " paged_meta.shape";
    }

    void tupleToJson(
        json::ObjectBuilder& builder,
        const pqxx::row& tuple) const override
    {
        const char* geojson = tuple.at(SHAPE).c_str();

        builder[jkey::ATTRIBUTES] = [&](json::ObjectBuilder builder) {
            AttributeDumper ad(tuple.at(TYPE).as<std::string>(), builder);
            ad.dumpCategory();

            const auto keys = wiki::common::parseSqlArray(tuple.at(META_PARAM_KEYS).as<std::string>());
            const auto vals = wiki::common::parseSqlArray(tuple.at(META_PARAM_VALS).as<std::string>());

            std::map<std::string, std::string> attributes;
            for (auto keyIt = keys.begin(), valIt = vals.begin(); keyIt != keys.end(); ++keyIt, ++valIt) {
                attributes.insert({*keyIt, *valIt});
            }
            for (const auto& attr : attributes) {
                ad.dump<std::string>(attr.first, attr.second);
            }
        };
        if (geojson && *geojson)
            builder[jkey::GEOMETRY] = json::Verbatim(geojson);
    }
};

DEFINE_CATEGORY_OBJECT(Meta, META);

} // namespace maps
