#include <library/cpp/neh/neh.h>
#include <library/cpp/neh/location.h>

#include <util/memory/tempbuf.h>
#include <util/stream/str.h>
#include <library/cpp/string_utils/quote/quote.h>

#include <stdexcept>

#include "http_client.h"

namespace NWebmaster {

TAtomicSharedPtr<std::string> TPostRequest::Perform() {
    try {
        NNeh::TParsedLocation loc(TStringBuf(Url->c_str(), Url->length()));

        TStringStream ss;

        ss << "POST /"sv << loc.Service << " HTTP/1.1\r\nHost: "sv << loc.EndPoint << "\r\n"sv;

        if (Data) {
            ss << "Content-Length: "sv << Data->size() << "\r\n"sv;
        }

        if (Headers) {
            for (const auto& h: *Headers) {
                ss << h << "\r\n"sv;
            }
        }

        if (!ContentType.empty()) {
            ss << "Content-Type: "sv << ContentType << "\r\n"sv;
        }

        ss << "\r\n"sv;

        if (Data) {
            ss << *Data;
        }

        #if defined(_freebsd_)
        NNeh::TResponseRef ret = NNeh::Request(NNeh::TMessage(TString("full2://") + loc.EndPoint + "/" + loc.Service, ss.Str()))->Wait(TDuration::MilliSeconds(Tout));
        #else
        NNeh::TResponseRef ret = NNeh::Request(NNeh::TMessage(TString("full://") + loc.EndPoint + "/" + loc.Service, ss.Str()))->Wait(TDuration::MilliSeconds(Tout));
        #endif

        if (!ret) {
            ythrow yexception() << "timeout"sv;
        }

        if (ret->IsError()) {
            ythrow yexception() << ret->GetErrorText();
        }

        return TAtomicSharedPtr<std::string>(new std::string(ret->Data.data(), ret->Data.size()));
    } catch (...) {
        throw std::runtime_error(CurrentExceptionMessage());
    }
}

std::string HttpGet(const std::string& _url, long timeout) {
    std::string url(_url);

    #if defined(_freebsd_)
    if (url.find("http://") == 0) {
        url = "http2://" + url.substr(7);
    }
    #endif

    try {
        NNeh::TResponseRef r = NNeh::Request(TString(url))->Wait(TDuration::MilliSeconds(timeout));

        if (r && !r->IsError()) {
            return std::string(r->Data.data(), r->Data.size());
        }

        ythrow yexception() << "unable to process HttpGet request";
    } catch (...) {
        throw std::runtime_error(CurrentExceptionMessage());
    }
}

std::string HttpPost(const std::string& url, const std::string& data, const std::string &contentType, long timeout) {
    TPostRequest post(url);
    post.SetTimeout(timeout);
    post.SetContentType(contentType);
    post.SetData(data);
    TAtomicSharedPtr<std::string> res = post.Perform();
    return *res;
}

void Quote(std::string& url, const char* safe) {
    TTempBuf tempBuf(CgiEscapeBufLen(url.size()));
    char* to = tempBuf.Data();

    url.assign(to, ::Quote(to, url.c_str(), safe));
}

} //namespace NWebmaster
