#pragma once

#include <yandex/maps/wiki/revision/common.h>
#include <yandex/maps/wiki/revision/revisionid.h>

#include <string>
#include <map>

namespace maps::wiki::revision {

class ObjectRevision;
class ObjectRevisionDiffImpl;

// first - old data, second - new data
template <typename T>
struct Diff
{
    template <typename U, typename V>
    Diff(U&& before, V&& after)
        : before(std::forward<U>(before))
        , after(std::forward<V>(after))
    {}
    T before;
    T after;
};

using StringDiff = Diff<std::string>;
using AttributesDiff = std::map<std::string, StringDiff>;
using DescriptionDiff = Diff<Description>;
using GeometryDiff = Diff<Wkb>;
using DeletedDiff = Diff<bool>;

class ObjectRevisionDiff {
public:
    struct Data {
        std::optional<AttributesDiff> attributes;
        std::optional<DescriptionDiff> description;
        std::optional<GeometryDiff> geometry;
        std::optional<DeletedDiff> deleted; // for update only

        std::optional<RelationData> newRelationData;
        std::optional<RelationData> oldRelationData;
    };
    explicit ObjectRevisionDiff(const Data& data, const std::optional<RevisionID>& oldId, const RevisionID& newId);
    explicit ObjectRevisionDiff(const RevisionID& unionRevId);
    explicit ObjectRevisionDiff(const ObjectRevision& newRev);
    ObjectRevisionDiff(const ObjectRevision& newRev, const ObjectRevision& oldRev);
    explicit  ObjectRevisionDiff(const ObjectRevisionDiff& other);
    explicit  ObjectRevisionDiff(ObjectRevisionDiff&& other) noexcept;
    ObjectRevisionDiff& operator =(const ObjectRevisionDiff& other);
    ObjectRevisionDiff& operator =(ObjectRevisionDiff&& other) noexcept;
    ~ObjectRevisionDiff();

    const Data& data() const;

    const RevisionID& newId() const;
    const RevisionID& oldId() const;

private:
    std::unique_ptr<ObjectRevisionDiffImpl> impl_;
};

// objectId -> Diff
using CommitDiff = std::map<DBID, ObjectRevisionDiff>;

CommitDiff
commitDiff(pqxx::transaction_base& txn, DBID commitId, DBID objectId = 0);

std::optional<AttributesDiff>
createAttributesDiff(const Attributes& oldAttrs, const Attributes& newAttrs);

} // namespace maps::wiki::revision
