#include <maps/wikimap/mapspro/libs/sender/include/gateway.h>
#include <maps/wikimap/mapspro/libs/sender/include/email_template_params.h>

#include <maps/libs/enum_io/include/enum_io.h>
#include <maps/libs/log8/include/log8.h>
#include <maps/libs/json/include/builder.h>
#include <maps/libs/json/include/std.h>
#include <maps/libs/json/include/value.h>
#include <maps/libs/common/include/base64.h>

namespace maps::wiki::sender {

namespace {

using namespace std::string_view_literals;

constexpr enum_io::Representations<Result> RESULT_STRINGS {
    {Result::Sent, "sent"sv},
    {Result::Failed, "failed"sv}
};

} // namespace

DEFINE_ENUM_IO(Result, RESULT_STRINGS);


BaseGateway::BaseGateway(
    std::string endPoint,
    Credentials credentials,
    const maps::common::RetryPolicy& retryPolicy)
    : endPoint_(std::move(endPoint))
    , credentials_(std::move(credentials))
    , retryPolicy_(retryPolicy)
{}

namespace {

std::string userToAuthHeader(const std::string& userId)
{
    // empty password required
    std::string toEncode = userId + ":";
    return "Basic " + base64Encode(toEncode);
}

} // namespace

http::URL BaseGateway::createUrlWithoutParams(const CampaignSlug& campaign) const
{
    return http::URL(
        "https://" +
        endPoint_ + "/api/0/" +
        credentials_.accountSlug + "/transactional/" +
        campaign + "/send"
    );
}

Result Gateway::sendToUrl(const http::URL& url, const std::string& requestBody) const
{
    http::Client httpClient;
    httpClient.setTimeout(std::chrono::seconds(60));

    http::HeaderMap headers = {
        {"Authorization", userToAuthHeader(credentials_.userId)},
        {"Content-Type", "application/json"}
    };

    auto [responseBody, status] = httpClient.post(
        url,
        headers,
        requestBody,
        retryPolicy_
    );

    if (status == 200) {
        return Result::Sent;
    }
    if (status == 202) {
        return Result::NoEmail;
    }
    if (status == 400) {
        return Result::Failed;
    }
    throw maps::RuntimeError() << "unexpected http code: " << status
        << "; POST url: " << url << ", response: " << responseBody;
}

Result Gateway::sendToEmail(
    const CampaignSlug& campaign,
    const std::string& toEmail,
    const EmailTemplateParams& templateParams) const
{
    auto url = createUrlWithoutParams(campaign);
    url.addParam("to_email", toEmail);
    return sendToUrl(url, templateParams.toJsonString());
}

Result Gateway::sendToYandexPuid(
    const CampaignSlug& campaign,
    uint64_t toYandexPuid,
    const EmailTemplateParams& templateParams) const
{
    auto url = createUrlWithoutParams(campaign);
    url.addParam("to_yandex_puid", toYandexPuid);
    return sendToUrl(url, templateParams.toJsonString());
}

Result Gateway::send(
    const CampaignSlug& campaign,
    const std::string& requestBody) const
{
    auto url = createUrlWithoutParams(campaign);
    return sendToUrl(url, requestBody);
}

Result DryGateway::sendToEmail(
    const CampaignSlug&,
    const std::string& toEmail,
    const EmailTemplateParams& templateParams) const
{
    INFO() << "Imitate sender request with email = " << toEmail
           << " and template params = " << templateParams;
    return Result::Sent;
}
Result DryGateway::sendToYandexPuid(
    const CampaignSlug&,
    uint64_t toYandexPuid,
    const EmailTemplateParams& templateParams) const
{
    INFO() << "Imitate sender request with yandex puid = " << toYandexPuid
           << " and template params = " << templateParams;
    return Result::Sent;
}

Result DryGateway::send(
    const CampaignSlug& campaign,
    const std::string& requestBody) const
{
    INFO() << "Imitate sender request for url="
        << createUrlWithoutParams(campaign)
        << " and body=" << requestBody;
    return Result::Sent;
}

} // namespace maps::wiki::sender
