#pragma once

#include "reply_guard.h"
#include "report.h"

#include <saas/library/searchserver/http_status_config.h>
#include <saas/searchproxy/proxy_meta/rearrange/abstract/rearrange.h>
#include <saas/util/messages_collector.h>

#include <search/common/cphash.h>
#include <search/meta/scatter/options/options.h>
#include <search/meta/scatter/result.h>
#include <search/meta/scatter/task.h>

#include <library/cpp/eventlog/eventlog.h>
#include <library/cpp/http/misc/httpcodes.h>
#include <library/cpp/object_factory/object_factory.h>

#include <util/system/spinlock.h>

class IReplyContext;
class TServiceConfig;

namespace NProxyMeta {

    class TAnswerBuilder {
    private:
        struct TSourceError {
            TString Text;
            int Code = 0;
        };

        IReplyContext& Context;
        const TSelfFlushLogFramePtr EventLogFrame;
        bool AddEventLogToReport = false;
        const TRearrangeEngine* RearrangeEnginePtr;
        const THttpStatusManagerConfig& ConfigHttpStatus;

        TAdaptiveLock Lock;

        ui64 SourcesCount = 0;
        TAtomic SucceededSources = 0;
        TAtomic FailedSources = 0;
        TAtomic IncompleteReplies = 0;
        ui32 DocumentsCount = 0;
        TVector<NProxyMeta::TSourceReply> Answers;
        TRTYMetaProtoReportConstructor ReportConstructor;
        HttpCodes AnsweredHttpCode = HTTP_CODE_MAX;
        TVector<TSourceError> SourcesErrors;

    public:
        TAnswerBuilder(
            IReplyContext& context,
            const TSelfFlushLogFramePtr eventLogFrame,
            const bool addEventLogToReport,
            const TRearrangeEngine* rearrangeEnginePtr,
            const THttpStatusManagerConfig& configHttpStatus
        );

        ui32 GetDocumentsCount() const {
            return DocumentsCount;
        }

        ui64 GetReportByteSize() const {
            return ReportConstructor.GetReportByteSize();
        }

        ui32 GetAnswerIsComplete() const {
            return (ui64)AtomicGet(SucceededSources) == SourcesCount;
        }

        ui32 GetFailedAttempts() const {
            return IncompleteReplies;
        }

        HttpCodes GetAnsweredHttpCode() const {
            CHECK_WITH_LOG(AnsweredHttpCode != HTTP_CODE_MAX);
            return AnsweredHttpCode;
        }

        const ICustomReportBuilder& GetReport() const {
            return ReportConstructor;
        }

        bool RegisterReport(const TString& source, const NScatter::TTaskReply& reply, TAtomic& sourceSuccessesCounter);
        void RegisterRequest(const TString& source);
        void RegisterFail(const TString& source, const TString& details);
        void Answer(const TString& proxyType);
        void AnswerFail(const TString& message);
    };

    class TSearchContext: public NScatter::ITask {
    private:
        IReplyContext* Context;
        const CPHashFunction HashFunction;
        NScatter::TSourceOptions Options;
        const TVector<TString> TextOverride;
        TAnswerBuilder* AnswerBuilder;
        const TServiceConfig& ServiceConfig;
        TAtomic RepliesCounter = 0;
        TMTMessagesCollector ErrorsCollector;
        TAtomic SuccessesCounter = 0;
    public:
        TSearchContext(IReplyContext* context, TAnswerBuilder* answerBuilder, TVector<TString> textOverride, NScatter::ISource& source, CPHashFunction hashFunction, const TServiceConfig& serviceConfig);

        virtual const NScatter::TSourceOptions& GetSourceOptions() const override {
            return Options;
        }

        virtual void DoHandleError(const NScatter::TErrorDetails& details) override;

        virtual size_t CalcHash() const override;
        virtual size_t GetParallelRequestCount() const override;
        virtual TStringBuf GetTypeName() const override;

        virtual NScatter::TParseResult DoParse(const NScatter::TTaskReply& reply) override;

        virtual void DoFinalize() override;
        virtual void FillRequestData(NScatter::TRequestData& rd) const override;
    };
}
