#pragma once

#include "document.h"
#include "common.h"

#include <library/cpp/json/json_value.h>

#include <util/generic/string.h>

namespace NSaas {
    class TAction {
        friend class TProtobufDeserializer;
    public:
        typedef TAction TThis;
        typedef NRTYServer::TMessage TProtobuf;
        enum TActionType { atAdd, atModify, atDelete, atDeprecated__Update, atReopen, atSwitchPrefix };

    public:
        TAction();
        TAction(const TProtobuf& protobuf);

        TAction(const TAction& other) = delete;
        TAction& operator=(const TAction& other) = delete;

        TAction(TAction&& other) = default;
        TAction& operator=(TAction&& other) = default;

        TString BuildJsonMessage() const;
        TString GetActionDescription() const;
        bool Validate(TString& error) const;

        NJson::TJsonValue ToJson(TToJsonContext& context) const;
        const NRTYServer::TMessage& ToProtobuf() const;
        TAction& ParseFromJson(const NJson::TJsonValue& source);
        TAction& ParseFromProtobuf(const NRTYServer::TMessage& source);

        TDocument& GetDocument() const;
        TDocument& GetDocument();
        TDocument& AddDocument();
        bool HasDocument() const {
            return Document.Get();
        }

        /*(Get|Set|Has)Prefix*/ PROTOBUF_FIELD_ACCESSORS(ui64, Prefix, InternalDocument, KeyPrefix)
        /*(Get|Set|Has)Id*/     PROTOBUF_FIELD_ACCESSORS(ui64, Id, InternalMessage, MessageId)
        /*(Get|Set|Has)ProtobufActionType*/ PROTOBUF_FIELD_ACCESSORS(NRTYServer::TMessage::TMessageType, ProtobufActionType, InternalMessage, MessageType)
        /*(Get|Set|Has)ExternalShard*/ PROTOBUF_FIELD_ACCESSORS(ui64, ExternalShard, InternalMessage, ExternalShard)
        /*(Get|Set|Has)IndexedDoc*/ PROTOBUF_PBFIELD_ACCESSORS(NRealTime::TIndexedDoc, IndexedDoc, InternalDocument, IndexedDoc)

        TAction& SetActionType(TActionType action);
        TActionType GetActionType() const;
        bool HasActionType() const {
            return HasProtobufActionType();
        }

        TAction& SetRequest(const TString& request);
        TAction& ClearRequest();
        const TString& GetRequest() const {
            return Request;
        }
        bool HasRequest() const {
            return !Request.empty();
        }

        TAction& AddDistributorAttribute(const TString& value);
        TAction& ClearDistributorAttributes();
        TAction& SetReceiveTimestamp(TInstant value);
        TInstant GetReceiveTimestamp() const;
        bool HasReceiveTimestamp() const;
        void SetPosition(const TString& key, NRTYServer::TPositionValue value);
        TMaybe<NRTYServer::TPosition> GetPosition() const;
        void AddExtraPosition(const TString& key, NRTYServer::TPositionValue value);
        TVector<NRTYServer::TPosition> GetExtraPositions() const;

        TString GetDebugString() const {
            return BuildJsonMessage();
        }

        NRTYServer::TMessage& MutableProtobuf();

    protected:
        THolder<TProtobuf> OwnedProtobufPtr;
        TProtobuf& InternalMessage;
        TProtobuf::TDocument& InternalDocument;

        THolder<TDocument> Document;
        TString Request;
    };

    struct TActionDebugView {
        ui64 MessageId;
        i64 KeyPrefix;
        TString Key;
        TString Name;
    };

    TActionDebugView GetActionDetails(const NRTYServer::TMessage& message);

    TString GetActionDescription(const NRTYServer::TMessage& message);

    ui64 GetActionVersion(const NRTYServer::TMessage& message);
}
