#pragma once

#include <saas/api/clientapi.h>

#include <library/cpp/logger/global/global.h>

#include <util/generic/vector.h>
#include <util/generic/noncopyable.h>
#include <util/network/socket.h>

namespace NSaas {
    void InitializeLogging();

    class TSendResult {
    public:
        enum ESendResult {
            srOK = 0,
            srIncorrectMessage,
            srServerError,
            srNotNow,
            srDataAccepted,
            srDeprecated,
            srUnknownError,
            srSendError,
            srUnauthorized,
            srDifferentAnswers,
            srCantStore,
            srPartial,
            srInternalTimeout,
            srNetworkResolutionError
        };

        enum ESource {
            PROXY, ARTIFICAL
        };

        class IImpl {
        public:
            virtual ~IImpl() {}

            ESendResult GetCode() const {
                return SendResult;
            }
            virtual TString GetMessage() const = 0;
            virtual ESource GetSource() const = 0;
            virtual bool IsAsync() const = 0;
            virtual TString GetAsyncHandle() const = 0;

        protected:
            ESendResult SendResult;
        };

    public:
        TSendResult(ESendResult result = srUnknownError);

        TVector<std::pair<ui16, TString>> GetReplies() const;
        bool ShouldRetryMessage() const;
        bool IsSucceeded() const;

        ui16 GetHttpCode() const {
            return InterpretSendResult(GetCode());
        }
        ESendResult GetCode() const {
            return Impl->GetCode();
        }
        TString GetMessage() const {
            return Impl->GetMessage();
        }
        ESource GetSource() const {
            return Impl->GetSource();
        }
        bool IsAsync() const {
            return Impl->IsAsync();
        }
        TString GetAsyncHandle() const {
            return Impl->GetAsyncHandle();
        }

        static TSendResult FromProxyReply(const TString& head, const TString& body);
        static TSendResult FromProxyReply(int code, const TString& body);
        static TSendResult FromCurrentExeption(TSendResult::ESendResult eSendResult = TSendResult::srUnknownError);


    private:
        ui16 InterpretSendResult(ESendResult result) const;

    private:
        TSimpleSharedPtr<IImpl> Impl;
    };

    struct TSendParams {
        TSendParams() = default;

        TSendParams(bool realtime, bool trace, bool verboseReply, const TString& auth = TString())
            : Realtime(realtime)
            , Trace(trace)
            , VerboseReply(verboseReply)
            , Auth(auth)
        {
        }

        bool Realtime = true;
        bool Trace = false;
        bool VerboseReply = true;
        bool InstantReply = false;
        TString Auth;

        TSendParams& SetVerbose(bool val = true) {
            VerboseReply = val;
            return *this;
        }

        TSendParams& SetTrace(bool val = true) {
            Trace = val;
            return *this;
        }

        TSendParams& SetRealtime(bool val = true) {
            Realtime = val;
            return *this;
        }

        TSendParams& SetInstantReply(bool val = true) {
            InstantReply = val;
            return *this;
        }
    };

    class TIndexingClient: public TNonCopyable {
    public:
        TIndexingClient(const TString& host, ui16 port, const TString& url = Default<TString>(), const TDuration& timeout = TDuration::Seconds(10));

        bool WaitAsync(TSendResult& result, TInstant deadline) const;
        bool WaitAsync(TSendResult& result, TDuration timeout) const;
        TSendResult Send(const TAction& action, const TString& type = "json", const TSendParams& params = Default<TSendParams>()) const;
        TSendResult SendJson(const TAction& action, const TSendParams& params = Default<TSendParams>()) const;
        TSendResult SendJsonRef(const TAction& action, const TSendParams& params = Default<TSendParams>()) const;
        TSendResult SendProto(const TAction& action, const TSendParams& params = Default<TSendParams>()) const;

        TAction CreateAction() const {
            return TAction();
        }

        TString GetIndexingUrl(const TSendParams& params) const;
        TString GetCommonJsonHttp(const NSaas::TAction& action, const TSendParams& params) const;
        TString GetJsonRefHttp(const NSaas::TAction& action, const TSendParams& params) const;
        TString GetJsonProtoHttp(const NSaas::TAction& action, const TSendParams& params) const;
        TString GetHttpMessage(const NSaas::TAction& action, const TString& type, const TSendParams& params) const;

    private:
        TString GetCommonJsonHttp(const TString& body, const TSendParams& params) const;

        TSendResult SendToProxy(const char* data, size_t length) const;
    private:
        TString Host;
        ui16 Port;
        TString Url;
        TDuration Timeout;
    };
}

namespace NRTY { // OBSOLETE
    using TSendResult = NSaas::TSendResult;
    using TSendParams = NSaas::TSendParams;
    using TIndexingClient = NSaas::TIndexingClient;
    using TIndexingServer = NSaas::TIndexingClient;
}
