#include "httpstatusmanagerfactory.h"

#include "extendedcontext.h"
#include "httpstatusmanager.h"

#include <saas/library/searchserver/http_status_config.h>
#include <search/session/reqenv.h>

#include <kernel/search_daemon_iface/cntintrf.h>
#include <kernel/reqerror/reqerror.h>

#include <library/cpp/http/misc/httpcodes.h>
#include <util/generic/ymath.h>
#include <util/string/cast.h>
#include <util/system/defaults.h>

class TConfigurableHttpStatusManager: public IHttpStatusManager {
private:
    const THttpStatusManagerConfig Config;

public:
    inline TConfigurableHttpStatusManager(
        const THttpStatusManagerConfig& config)
        : Config(config)
    {
    }

    int CalculateHttpStatus(const TExtendedSearchContext& searchContext) const override
    {
        const TReqEnv* reqEnv = static_cast<const TReqEnv*>(searchContext->ReqEnv());
        if (reqEnv->GetFieldCount(TStringBuf("mslevel"))) {
            return HTTP_OK;
        }

        if (!reqEnv->AnswerIsComplete()) {
            return Config.IncompleteStatus;
        }

        const size_t unresponsive = reqEnv->BaseSearchNotRespondCount();
        const size_t total = reqEnv->BaseSearchCount();
        const float success = 1 - (total ? unresponsive / float(total) : 0);
        Y_ASSERT(success >= 0.0f && success <= 1.0f);

        if (unresponsive) {
            if (Abs(Config.PartialThreshold) > 0.01f && success > Config.PartialThreshold) {
                return Config.PartialStatus;
            } else {
                return Config.IncompleteStatus;
            }
        }

        {
            const int errorCode = reqEnv->ErrorCode();
            switch (errorCode) {
            case yxSYNTAX_ERROR:
                return Config.SyntaxErrorStatus;
            case yxREQ_EMPTY:
                return Config.EmptyRequestStatus;
            case yxUNKNOWN_ERROR:
                return Config.UnknownErrorStatus;
            default:
                break;
            }
        }

        if (searchContext.Timeouted()) {
            return Config.TimeoutStatus;
        }

        if (searchContext->Cluster()->TotalDocCount(0) == 0) {
            return Config.EmptySetStatus;
        }

        if (searchContext.NotFetched()) {
            return Config.NotFetchedStatus;
        }

        return HTTP_OK;
    }
};

TAutoPtr<IHttpStatusManager> CreateHttpStatusManager(const THttpStatusManagerConfig& config) {
    return new TConfigurableHttpStatusManager(config);
}
