#include "saas_document.h"

#include <saas/api/action.h>

#include <kernel/querydata/saas_yt/idl/qd_saas_snapshot_record.pb.h>
#include <kernel/querydata/saas_yt/qd_saas_yt.h>

#include <library/cpp/string_utils/base64/base64.h>

#include <util/generic/list.h>

namespace NSaas {
    namespace {
        void BuildDeleteMessage(TAction& action, const TString& key, TInstant timestamp) {
            action.SetActionType(TAction::atDelete);
            auto& document = action.AddDocument();
            document.SetUrl(key);
            document.SetTimestamp(timestamp.Seconds());
        }
    }

    struct TQueryDataMessageBuilder::TPrivate {
        TList<NQueryDataSaaS::TQDSaaSSnapshotRecord> SnapshotRecords;
        TString Key;
        TInstant Timestamp;

        void Clear() {
            SnapshotRecords.clear();
            Key.clear();
            Timestamp = TInstant::Zero();
        }

        void Add(const TString& ns, TInstant timestamp, const NQueryDataSaaS::TQDSaaSInputRecord& record, const TKeyOrder* customKeyOrder) {
            if (record.GetDelete()) {
                Y_ENSURE(SnapshotRecords.empty());
            }

            NQueryDataSaaS::TQDSaaSSnapshotRecord snapshotRecord;
            NQueryDataSaaS::TSnapshotRecordFillParams params;
            params.Namespace = ns;
            params.TimestampMicro = timestamp.MicroSeconds();
            params.AddDebugInfo = true;
            params.SaasType = NQueryDataSaaS::EQDSaaSType::Trie;
            params.CustomKeyOrder = customKeyOrder;
            NQueryDataSaaS::FillSnapshotRecordFromInputRecord(snapshotRecord, record, params);

            Timestamp = std::max(Timestamp, timestamp);
            auto key = snapshotRecord.GetSaasKey();
            if (Key.empty()) {
                Key = std::move(key);
            } else {
                Y_ENSURE(key == Key);
            }

            SnapshotRecords.emplace_back().Swap(&snapshotRecord);
        }

        void BuildAction(TAction& action) {
            Y_ENSURE(!Key.empty());
            Y_ENSURE(!SnapshotRecords.empty());
            if (SnapshotRecords.front().GetDelete()) {
                Y_ENSURE(SnapshotRecords.size() == 1);
                BuildDeleteMessage(action, Key, Timestamp);
                return;
            }
            action.SetActionType(TAction::atModify);
            auto& document = action.AddDocument();
            document.SetUrl(Key);
            document.SetTimestamp(Timestamp.Seconds());
            for (auto& rec : SnapshotRecords) {
                auto sf = rec.GetSourceFactors().SerializeAsString();
                document.AddBinaryProperty(rec.GetSaasPropName(), Base64Encode(sf));
            }
        }
    };

    TQueryDataMessageBuilder::TQueryDataMessageBuilder() {
        P = MakeHolder<TPrivate>();
    }

    TQueryDataMessageBuilder::~TQueryDataMessageBuilder() {
    }

    void TQueryDataMessageBuilder::Clear() {
        P->Clear();
    }

    void TQueryDataMessageBuilder::Add(const TString& ns, TInstant timestamp, const NQueryDataSaaS::TQDSaaSInputRecord& record, const TKeyOrder* customKeyOrder) {
        P->Add(ns, timestamp, record, customKeyOrder);
    }

    TAction TQueryDataMessageBuilder::BuildAction() const {
        TAction action;
        P->BuildAction(action);
        return action;
    }

    NRTYServer::TMessage TQueryDataMessageBuilder::BuildProto() const {
        return BuildAction().ToProtobuf();
    }

    NJson::TJsonValue TQueryDataMessageBuilder::BuildJson() const {
        return BuildAction().BuildJsonMessage();
    }
}

