#include "common.h"
#include "processor.h"

#include <saas/api/clientapi.h>
#include <saas/library/behaviour/behaviour.h>
#include <saas/indexerproxy/adapters/proto_adapter/proto_adapter.h>
#include <saas/indexerproxy/export/config.pb.h>

#include <yweb/robot/kiwi/protos/export.pb.h>

#include <util/string/split.h>

namespace NSaas {
    class TOxyExportProcessor: public TCommonExportProcessor {
    private:
        TString KeyPrefixTuple;
        TString ExternalShardTuple;
        TString VersionTuple;
        TString ActionTypeTuple;
        TString RTYServerUrlTuple;
        TString DistributorAttrsTuple;
        TString ServiceTuple;
        TString TimestampTuple;
        TString RealtimeTuple;
        TString DeadlineTuple;
        TString UpdateTypeTuple;
        TString FilterRankTuple;
        TString TupleTimestampSuffix;
        TVector<TString> ExtraLogTupleList;

    public:
        TOxyExportProcessor(const NSaas::TExportTypeOptions& type)
            : TCommonExportProcessor(type)
            , KeyPrefixTuple(type.GetKeyprefixTuple())
            , ExternalShardTuple(type.GetExternalShardTuple())
            , VersionTuple(type.GetVersionTuple())
            , ActionTypeTuple(type.GetActionTypeTuple())
            , RTYServerUrlTuple(type.GetRTYServerUrlTuple())
            , DistributorAttrsTuple(type.GetDistributorAttrsTuple())
            , ServiceTuple(type.GetServiceTuple())
            , TimestampTuple(type.GetTimestampTuple())
            , RealtimeTuple(type.GetRealtimeTuple())
            , DeadlineTuple(type.GetDeadlineTuple())
            , UpdateTypeTuple(type.GetUpdateTypeTuple())
            , FilterRankTuple(type.GetFilterRankTuple())
            , TupleTimestampSuffix(type.GetTupleTimestampSuffix())
            , ExtraLogTupleList(SplitString(type.GetExtraLogTuplesNamesList(), ","))
        {
            SortUnique(ExtraLogTupleList);
        }

        TIncomingDocumentCtxPack Process(const NKwExport::TExportRecord& record) const override {
            TKiwiObjectAcquireOptions options;
            options.MergeTimestamps = true;
            options.TimestampSuffix = TupleTimestampSuffix;

            const NKiwi::TKiwiObject& o = GetKiwiObject(record.GetData(), options);
            NSaas::TAction action;
            NSaas::TDocument& document = action.AddDocument();

            const TString& servicesString = GetTuple<TString>(o, ServiceTuple);
            if (!servicesString) {
                throw yexception() << "empty services tuple " << ServiceTuple;
            }

            const TString& actionTypeString = GetTuple<TString>(o, ActionTypeTuple);
            const auto* behaviour = GetBehaviour(actionTypeString);
            if (!behaviour) {
                throw yexception() << "unknown action type " << actionTypeString;
            }
            action.SetProtobufActionType(behaviour->MessageType);

            const auto& distributorAttrs = SplitString(GetTuple<TString>(o, DistributorAttrsTuple), ",");
            for (auto&& value : distributorAttrs) {
                action.AddDistributorAttribute(value);
            }

            action.SetExternalShard(GetTuple<ui64>(o, ExternalShardTuple, 0));
            action.SetPrefix(GetTuple<ui64>(o, KeyPrefixTuple, 0));

            const TString& explicitUrl = GetTuple<TString>(o, RTYServerUrlTuple);
            const TString& url = explicitUrl ? explicitUrl : record.GetKey();
            document.SetUrl(url);
            if (HasTuple(o, VersionTuple)) {
                document.SetVersion(GetTuple<ui32>(o, VersionTuple, 0));
            }
            if (HasTuple(o, TimestampTuple)) {
                document.SetTimestamp(GetTuple<ui32>(o, TimestampTuple, 0));
            }
            if (HasTuple(o, RealtimeTuple)) {
                document.SetRealtime(GetTuple<bool>(o, RealtimeTuple, true));
            }
            if (HasTuple(o, DeadlineTuple)) {
                document.SetDeadline(TInstant::Seconds(GetTuple<ui32>(o, DeadlineTuple, 0)));
            }
            if (HasTuple(o, UpdateTypeTuple)) {
                const TString& updateTypeString = GetTuple<TString>(o, UpdateTypeTuple);
                if (updateTypeString) {
                    NRTYServer::TMessage::TUpdateType type;
                    if (!NRTYServer::TMessage::TUpdateType_Parse(GetTuple<TString>(o, UpdateTypeTuple), &type)) {
                        throw yexception() << "unknown update type " << updateTypeString;
                    }
                    document.SetUpdateType(type);
                }
            }
            if (HasTuple(o, FilterRankTuple)) {
                document.SetFilterRank(GetTuple<double>(o, FilterRankTuple, 0));
            }

            for (const auto& logTupleName: ExtraLogTupleList) {
                if (HasTuple(o, logTupleName)) {
                    const TString value = GetTuple<TString>(o, logTupleName, "");
                    if (!!value) {
                        auto* loggingProp = action.MutableProtobuf().AddLoggingProperties();
                        loggingProp->SetName(logTupleName);
                        loggingProp->SetValue(value);
                    }
                }
            }

            if (behaviour->MessageType != NRTYServer::TMessage::DELETE_DOCUMENT) {
                document.AddObjectContext(o.GetImage());
            }

            TIncomingDocumentCtxPack result;

            const TBlob& data = TBlob::FromString(action.ToProtobuf().SerializeAsString());

            for (const auto& serviceIt : StringSplitter(servicesString).Split(',').SkipEmpty()) {
                NSaas::TIncomingDocumentCtx dctx = CreateContext(record);
                dctx.Service = serviceIt.Token();
                dctx.Adapter = NIndexerProxy::TProtoAdapter::AdapterAlias;
                dctx.Data = data;
                result.push_back(dctx);
            }

            return result;
        }
    };
}

NSaas::TExportProcessorFactory::TRegistrator<NSaas::TOxyExportProcessor> Oxy("Oxy");
