#pragma once

#include "revision_data.h"
#include <yandex/maps/wiki/revision/revisionid.h>
#include <yandex/maps/wiki/revision/objectrevision.h>
#include <yandex/maps/wiki/revision/common.h>
#include <yandex/maps/wiki/revision/range.h>
#include <maps/libs/common/include/exception.h>
#include <boost/utility.hpp>

#include <list>
#include <string>
#include <sstream>
#include <unordered_set>

namespace maps::wiki::revision::helpers {

class TransactionGuard : public boost::noncopyable {
public:
    explicit TransactionGuard(pqxx::transaction_base& work)
        : workPtr_(&work)
    {}

    ~TransactionGuard()
    {
        if (workPtr_) {
            workPtr_->abort();
        }
    }

    void ok() { workPtr_ = nullptr; }

private:
    pqxx::transaction_base* workPtr_;
};


CommitState stringToCommitState(const std::string& str);
const std::string& commitStateToString(CommitState state);

Hstore attributesToHstore(
    const pqxx::transaction_base& work, const Attributes& attrs,
    Attributes* newAttrs = nullptr);

Attributes stringToAttributes(const std::string& attrs);

std::vector<DBID> loadIds(
    const pqxx::result& r, const std::string& fieldName);

pqxx::result lockData(
    pqxx::transaction_base& work, const std::string& query,
    const char* context);


template <typename Iter>
std::string
valuesToString(Iter begin, Iter end)
{
    ASSERT(begin != end);

    std::ostringstream os;
    os << *begin;
    while (++begin != end) {
        os << ',' << *begin;
    }
    return os.str();
}

DBID stringToDBID(const std::string& s);
DBIDSet stringToDBIDSet (const std::string& s);

template <typename Cont>
std::string
valuesToString(const Cont& cont)
{
    return valuesToString(cont.begin(), cont.end());
}

void checkObjectId(DBID objectId);
void checkCommitId(DBID commitId);
void checkRevisionId(const RevisionID& revisionId);
void checkUserId(UserID uid);
void checkLimit(size_t limit);

void checkRevisionIds(const ConstRange<RevisionID>& ids);
RevisionIds revisionIdsToVector(const ConstRange<RevisionID>& revisionIds);

template <typename RevisionsMap>
Revisions
toList(const RevisionsMap& data)
{
    Revisions result;
    for (const auto& p : data) {
        result.push_back(p.second);
    }
    return result;
}

template<typename Set>
Set join(Set first, const Set& second)
{
    first.insert(second.begin(), second.end());
    return first;
}

template<typename Set>
void append(Set& first, const Set& second)
{
    first.insert(second.begin(), second.end());
}

template <typename Set, typename Container>
Set erase(Set&& from, const Container& values) {
    for (const auto& value: values) {
        from.erase(value);
    }
    return from;
}

} // namespace maps::wiki::revision::helpers
