#pragma once

#include "multipart_ctx.h"

#include <saas/rtyserver/merger/interface/common.h>
#include <ysite/yandex/srchmngr/arcmgr.h>
#include <kernel/walrus/advmerger.h>
#include <kernel/groupattrs/docsattrswriter.h>
#include <library/cpp/streams/special/throttled.h>
#include <util/datetime/base.h>
#include <util/system/file.h>
#include <util/generic/set.h>
#include <util/generic/ptr.h>
#include <util/generic/size_literals.h>
#include <util/memory/blob.h>
#include <util/stream/file.h>

class TMergeDocInfos;

class TRTYMerger {
private:
    const std::atomic<bool>* StopFlag;
public:
    enum TOperationType {
        otKI = 1,
        otArch = 2,
        otAttrs = 4,
        otLength = 8,
        otMpArch = 16,
        otDocUrl = 32,
    };

    typedef ::TRTYMergerAddress TAddress;
    typedef ::IRTYMergerDocIdInfo IRTYMergerDocIdInfo;
    typedef ::IRTYMergerDocIdDecoder IRTYMergerDocIdDecoder;

    struct TContext : public TRTYMergerContextBase {
        bool IgnoreNoAttrs;
        TString AdditionalSuffixIndexName;
        IExternalHitWriter* ExternalHitWriter;
        TMap<TString, const TArchiveManager*> AdditionalData;
        TThrottle::TOptions WriteOption = TThrottle::TOptions::FromMaxPerSecond(30_MB, TDuration::Seconds(1));
        NGroupingAttrs::TVersion IndexaaVersion;
        TAtomicSharedPtr<NRTYArchive::TMultipartMergerContext> MultipartMergerContext;
        TString MultipartArcFile;
        bool StripKeys = true;
        bool UniqueHits = true;
        bool WriteSentLens = true;
        bool MergeIndexAttrs = false;

    public:
        TContext(const TVector<TString>& sources, const TVector<TString>& dests, const TVector<TString>& tmpfsDests, TString tempDir,
            IRTYMergerDocIdDecoder* decoder, IRTYMergerDocIdInfo* info, IMergerCallback* callback = nullptr,
            bool ignoreNoAttrs = false)
            : TRTYMergerContextBase(sources, dests, tmpfsDests, tempDir, decoder, info, callback)
            , IgnoreNoAttrs(ignoreNoAttrs)
            , ExternalHitWriter(nullptr)
            , IndexaaVersion(NGroupingAttrs::TDocsAttrsWriter::DefaultWritableVersion)
        {
        }

        TContext(const TVector<TString>& sources, const TVector<TString>& dests, TString tempDir,
            IRTYMergerDocIdDecoder* decoder, IRTYMergerDocIdInfo* info, IMergerCallback* callback = nullptr,
            bool ignoreNoAttrs = false)
            : TRTYMergerContextBase(sources, dests, TVector<TString>{}, tempDir, decoder, info, callback)
            , IgnoreNoAttrs(ignoreNoAttrs)
            , ExternalHitWriter(nullptr)
            , IndexaaVersion(NGroupingAttrs::TDocsAttrsWriter::DefaultWritableVersion)
        {
        }

        TContext(const TVector<TString>& sources, const TString& dest, TString tempDir,
            IRTYMergerDocIdDecoder* decoder, IRTYMergerDocIdInfo* info, IMergerCallback* callback = nullptr,
            bool ignoreNoAttrs = false)
            : TRTYMergerContextBase(sources, TVector<TString>{dest}, TVector<TString>{}, tempDir, decoder, info, callback)
            , IgnoreNoAttrs(ignoreNoAttrs)
            , ExternalHitWriter(nullptr)
            , IndexaaVersion(NGroupingAttrs::TDocsAttrsWriter::DefaultWritableVersion)
        {
        }
    };

    class IMergerOperator {
        public:
            virtual ~IMergerOperator() {}
            virtual bool Merge(const std::atomic<bool>* stopKey, TMergeDocInfos& infos, const TRTYMerger::TContext& context) = 0;
            virtual TString GetName() const = 0;
    };

    TRTYMerger(const std::atomic<bool>* stopFlag, ui64 operations = otKI | otArch | otAttrs | otLength);

    bool MergeIndicies(const TRTYMerger::TContext& context);
private:
    TVector<TSimpleSharedPtr<IMergerOperator> > MergerOperatorsWithUpdater;
    TVector<TSimpleSharedPtr<IMergerOperator> > MergerOperatorsWithoutUpdater;
    bool MergeActors(const TRTYMerger::TContext& context, TMergeDocInfos& infos, TVector<TSimpleSharedPtr<IMergerOperator> >& actors);
};
