#pragma once

#include <saas/searchproxy/common/abstract.h>

#include <library/cpp/object_factory/object_factory.h>

#include <util/generic/map.h>
#include <util/generic/vector.h>
#include <library/cpp/cgiparam/cgiparam.h>

class TSearchProxyServer;
class TBaseServerRequestData;
class TSearchProxyConfig;
class TServiceConfig;
class TReqEnv;

class ICorrectorRule {
    public:
        virtual ~ICorrectorRule() {};
        virtual void Apply(TCgiParameters& cgi) const = 0;

        using TPtr = TAtomicSharedPtr<ICorrectorRule>;
        using TFactory = NObjectFactory::TObjectFactory<ICorrectorRule, TString>;
};

class TBaseCgiCorrector: public ICgiCorrector {
public:
    TBaseCgiCorrector(const TServiceConfig& config);

    virtual void FormContextCgi(IRequestContext* requestContext, ISearchContext** searchContext) final;

protected:
    void GetAllDocInfos(TCgiParameters& cgi);
    void FormKeyPrefixField(TCgiParameters& cgi);
    void FormRequestId(TCgiParameters& cgi, const TBaseServerRequestData* rd);
    void FormServiceRelated(TCgiParameters& cgi);
    void FormMetaServiceRelated(TCgiParameters& cgi);
    void SanitizeRequest(TCgiParameters& cgi);
    void ApplyCorrectorRules(TCgiParameters& cgi) const;

protected:
    TString Service;
    const TServiceConfig& Config;
    const bool UsingWizard;

    TMultiMap<TString, TString> ServiceSpecificCgi;
    TVector<ICorrectorRule::TPtr> Rules;
};

class TServiceSearchCgiCorrector: public TBaseCgiCorrector {
public:
    TServiceSearchCgiCorrector(const TString& service, const TServiceConfig& config)
        : TBaseCgiCorrector(config)
    {
        Service = service;
    }

    virtual void FormCgi(TCgiParameters& cgi, const TBaseServerRequestData* rd) override;
    virtual void SetClientCgi(const TCgiParameters& requestCgi, TCgiParameters& clientCgi, const TRequestParams& rp) override;

private:
    void EraseAuthParams(TCgiParameters& cgi);
};

class TServiceGlobalCgiCorrector: public ICgiCorrector {
public:
    TServiceGlobalCgiCorrector(const TServiceConfig& config)
        : Config(config) {}

    virtual void FormCgi(TCgiParameters& cgi, const TBaseServerRequestData* rd) override;
    virtual void SetClientCgi(const TCgiParameters& /*requestCgi*/, TCgiParameters& /*clientCgi*/, const TRequestParams& /*rp*/) override {}
    virtual void FormContextCgi(IRequestContext* /*requestContext*/, ISearchContext** /*searchContext*/) override {}
private:
    const TServiceConfig& Config;
};

class TMetaSearchCgiCorrector: public TServiceSearchCgiCorrector {
public:
    TMetaSearchCgiCorrector(const TString& service, const TServiceConfig& config)
        : TServiceSearchCgiCorrector(service, config)
    {
    }

    virtual void FormCgi(TCgiParameters& cgi, const TBaseServerRequestData* rd) override;
    virtual void SetClientCgi(const TCgiParameters& requestCgi, TCgiParameters& clientCgi, const TRequestParams& rp) override;

private:
    void ProxyAuthParams(const TCgiParameters& requestCgi, TCgiParameters& clientCgi);
};

class TUpperSearchCgiCorrector: public TMetaSearchCgiCorrector {
public:
    TUpperSearchCgiCorrector(const TServiceConfig& config)
        : TMetaSearchCgiCorrector("upper", config)
    {}

    virtual void FormCgi(TCgiParameters& cgi, const TBaseServerRequestData* rd);
};
