#include "filters.h"
#include "cachedloader.h"

#include <maps/libs/http/include/http.h>
#include <maps/libs/common/include/exception.h>
#include <maps/libs/log8/include/log8.h>

namespace maps {
namespace wiki {
namespace renderer {

namespace {

const size_t TIMEOUT_DEFAULT = 3; //seconds
const std::string ERROR_ELEMENT_NAME = "error";
const std::string VIEW_SQL_FORMAT = "view-sql";

const int HTTP_STATUS_OK = 200;

} // namespace

class Filters::Cache : public CachedLoaderHolder<std::string, std::string>
{
public:
    explicit Cache(const xml3::Node& node)
        : CachedLoaderHolder<std::string, std::string>(node)
    {}
};

Filters::Filters(const xml3::Node& node)
    : url_(node.node(URL, true).value<std::string>())
    , timeout_(node.node(TIMEOUT, true).value<size_t>(TIMEOUT_DEFAULT))
{
    REQUIRE(!url_.empty(), "Empty filters url");
    cache_.reset(new Cache(node));
}

Filters::~Filters()
{}

std::string
Filters::filterClause(const std::string& id, const std::string& token) const
{
    auto loader = [&](const std::string& id)
    {
        return requestFilterClause(id, token);
    };
    return cache_->load(id, loader);
}

std::string
Filters::requestFilterClause(
        const std::string& id, const std::string& token) const
{
    std::string requestString =
        url_ + id + "?format=" + VIEW_SQL_FORMAT + "&token=" + token;
    DEBUG() << "requestFilterClause: query filters source: " << requestString;

    http::Client httpClient;
    if (timeout_) {
        httpClient.setTimeout(std::chrono::seconds(timeout_));
    }

    http::URL url(requestString);
    http::Request request(httpClient, http::GET, url);
    auto response = request.perform();
    REQUIRE(response.status() == HTTP_STATUS_OK,
            "HTTP error requesting filter expression id: " << id);

    auto respXml = xml3::Doc::fromString(response.readBody());
    REQUIRE(respXml.root().name() != ERROR_ELEMENT_NAME,
            "Error requesting filter expression id: " << id <<
            " cause: " << respXml.root().value<std::string>());

    return respXml.root().firstChild().value<std::string>();
}

}}}//namespaces
