#pragma once

#include <yandex/maps/wiki/common/minmax.h>
#include <yandex/maps/wiki/common/string_utils.h>
#include <maps/libs/common/include/exception.h>

#include <boost/lexical_cast.hpp>

#include <pqxx/pqxx>

#include <map>
#include <string>
#include <vector>

namespace maps::wiki::common {

using Hstore = std::string;
using Attributes = std::map<std::string, std::string>;

Hstore attributesToHstore(
    const pqxx::transaction_base& txn,
    const Attributes& attrs);

std::vector<std::string> parseSqlArray(const std::string& data);

template<typename T>
std::vector<T> parseSqlArray(const std::string& data)
{
    auto strings = parseSqlArray(data);
    std::vector<T> result;
    result.reserve(strings.size());
    for (const auto& str : strings) {
        result.push_back(boost::lexical_cast<T>(str));
    }
    return result;
}

template<typename Container>
std::string whereClause(const std::string& key, const Container& ids)
{
    ASSERT(!ids.empty());

    if (ids.size() == 1) {
        return key + " = " + std::to_string(*ids.begin());
    }

    auto minMaxValues = MinMaxValue<Container, typename Container::value_type>()(ids);
    return "(" + key + " IN (" + common::join(ids, ',') + ")"
         + " AND " + key + " BETWEEN " + std::to_string(minMaxValues.min)
         + " AND " + std::to_string(minMaxValues.max) + ")";
}

template<typename Collection>
std::string sqlInCondition(const Collection& vals)
{
    if (vals.empty()) {
        return "IS NULL";
    }

    std::vector<std::string> valsStr;
    for (const auto& val : vals) {
        valsStr.push_back("'" + boost::lexical_cast<std::string>(val) + "'");
    }
    return "IN (" + common::join(valsStr, ",") + ")";
}

} // namespace maps::wiki::common
