#include <maps/wikimap/mapspro/libs/query_builder/include/insert_query.h>

#include <maps/wikimap/mapspro/libs/common/include/yandex/maps/wiki/common/string_utils.h>

namespace maps::wiki::query_builder {

namespace internal {

std::string buildInsertQuery(
    const pqxx::transaction_base& txn,
    const std::string& table,
    const std::map<std::string, QuotedValue>& keyValues,
    const std::optional<OnConflict>& onConflict,
    const std::vector<std::string>& returning)
{
    REQUIRE(
        !keyValues.empty(),
        "Cannot build empty insert query for table " << table);

    std::string keys = maps::wiki::common::join(
        keyValues,
        [](const std::pair<std::string, QuotedValue>& entry) {
            return entry.first;
        },
        ", "
    );
    std::string values = maps::wiki::common::join(
        keyValues,
        [&txn](const std::pair<std::string, QuotedValue>& entry) {
            return entry.second.quote(txn);
        },
        ", "
    );

    std::ostringstream query;
    query << "INSERT INTO " << table
        << " (" << keys << ") VALUES (" << values << ")";
    if (onConflict) {
        query << " " << onConflict->asString(txn);
    }

    if (returning.size()) {
        query << " RETURNING " << maps::wiki::common::join(returning, ", ");
    }
    return query.str();
}

} // namespace internal

InsertQuery::InsertQuery(std::string table)
    : ModifyQuery(std::move(table))
{}

InsertQuery& InsertQuery::updateOnConflict(OnConflict onConflict)
{
    onConflict_ = std::move(onConflict);
    return *this;
}

std::string InsertQuery::asString(const pqxx::transaction_base& txn) const
{
    return internal::buildInsertQuery(txn, table_, data_.keyValues(), onConflict_, data_.returning());
}

} // namespace maps::wiki::query_builder
