#include "client.h"
#include "server.h"

#include <saas/library/daemon_base/metrics/servicemetrics.h>

#include <library/cpp/string_utils/quote/quote.h>
#include <util/stream/str.h>
#include <library/cpp/logger/global/global.h>

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

namespace NRTY {

    static const TString PrefixProcessor = "/process/";

    class TProcessorContext: public IProcessorContext {
    private:
        THolder<TClient> Client;
    public:
        TProcessorContext(TClient* client)
            : IProcessorContext()
        {
            CHECK_WITH_LOG(client);
            Client.Reset(client);
        }

        ~TProcessorContext() {
            DEBUG_LOG << "request processing finished for " << Client->GetInfo() << Endl;
        }

        virtual const TServerRequestData& GetRD() const {
            return Client->GetRD();
        }

        virtual TString GetProcessorName() const {
            TString url = Client->GetRD().ScriptName();
            return url.substr(url.find_first_of(PrefixProcessor) + PrefixProcessor.length());
        }

        virtual void DoReply(ui32 code, const TString& message) override {
            if (IsUserError(code) || IsServerError(code)) {
                DEBUG_LOG << Client->GetInfo() << ";status=fail;message=" << message << Endl;
            } else {
                DEBUG_LOG << Client->GetInfo() << ";status=success" << Endl;
            }
            WriteToOutput(code, message);
        }

    private:
        void WriteToOutput(ui32 code, const TString& message) {
            Client->Output() << "HTTP/1.1 " << HttpCodeStrEx(code) << "\r\n" << "Content-Type: " << ContentType << "\r\n\r\n" << message;
        }
    };

    void TClient::GetMetrics(IOutputStream& out) const {
        out << "HTTP/1.1 200 Ok\r\n\r\n"sv;
        ::CollectMetrics(out);
    }

    TClient::TClient(TStaticServiceMetrics& /*metrics*/, TProcessorsStorage& processors)
        : Processors(processors)
    {
        StartTime = Now();
    }

    TString TClient::GetInfo() const {
        return "ip=" + (TString)RD.RemoteAddr() + ";url=" + (TString)RD.ScriptName() + ";cgi=" + RD.CgiParam.Print() + ";query=" + (TString)RD.QueryString() + ";size=" + ToString(Buf.Length()) + ";duration=" + ToString(Now() - StartTime);
    }

    bool TClient::Reply(void* /*ThreadSpecificResource*/) {
        ProcessHeaders();
        RD.Scan();
        if (ProcessSpecialRequest()) {
            DEBUG_LOG << GetInfo() << Endl;
            return true;
        } else {
            TAutoPtr<TProcessorContext> context(new TProcessorContext(this));
            TString url = RD.ScriptName();
            if (strstr(~url, ~PrefixProcessor)) {
                Processors.Process(context.Release());
            } else {
                context->UserError("incorrect url");
            }
            return false;
        }
        FAIL_LOG("Undefined behavior");
    }
}
