#include <maps/wikimap/mapspro/libs/bell_client/include/client.h>

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

#include <sstream>

namespace maps::wiki::bell_client {

namespace {

const std::string AUTH_HEADER = "Authorization";
const std::string AUTH_HEADER_PREFIX = "OAuth ";
const std::string CONTENT_TYPE_HEADER = "Content-Type";
const std::string JSON_CONTENT_TYPE = "application/json;charset=UTF-8";

std::string bellUidTag(Uid uid) {
    std::stringstream sstr;
    sstr << "tag:uid==" << uid << " and app_id==bell";
    return sstr.str();
}

}  // namespace

http::HeaderMap Client::constructHeaders() const {
    http::HeaderMap headers;
    headers[AUTH_HEADER] = AUTH_HEADER_PREFIX + configuration_.supToken;
    headers[CONTENT_TYPE_HEADER] = JSON_CONTENT_TYPE;
    return headers;
}

std::string Client::push(const PushInfo& pushInfo) {
    json::Builder bodyBuilder;
    bodyBuilder << [&](json::ObjectBuilder builder) {
        builder["receiver"] << [&](json::ArrayBuilder builder) {
            builder << bellUidTag(pushInfo.uid);
        };
        builder["ttl"] << pushInfo.ttl;
        builder["project"] << configuration_.project;
        if (pushInfo.pushId) {
            builder["data"] << [&](json::ObjectBuilder builder) {
                builder["push_id"] << pushInfo.pushId.value();
            };
        }
        builder["bell_features"] << [&](json::ObjectBuilder builder) {
            builder["actor"] << pushInfo.actor;
            builder["type"] << toString(pushInfo.type);
            builder["service"] << configuration_.service;
            builder["meta"] << pushInfo.meta;

            if (pushInfo.groupKey) {
                builder["group_key"] << *pushInfo.groupKey;
            }
        };
    };

    auto url = http::URL(configuration_.baseUrl + "/pushes");

    if (configuration_.dryRun) {
        url.addParam("dry_run", "1");
    }

    DEBUG() << "Request body:";
    DEBUG() << bodyBuilder.str();

    auto response = httpClient_.post(
        url,
        constructHeaders(),
        bodyBuilder.str(),
        configuration_.retryPolicy
    );

    DEBUG() << "Response body:";
    DEBUG() << response.first;

    REQUIRE(response.second == 200, "Unexpected response code: " << response.second);

    auto responseValue = json::Value::fromString(response.first);
    auto pushId = responseValue["id"].as<std::string>();
    DEBUG() << "Response value id: " << pushId;

    return pushId;
}

}  // namespace maps::wiki::bell_client
