#include "beacon.h"

#include <library/cpp/json/json_reader.h>
#include <library/cpp/iterator/cartesian_product.h>

#include <util/charset/utf8.h>
#include <util/string/subst.h>

#include <cmath>

TBeacon::TBeacon(const NJson::TJsonValue& beacon)
{
    auto beaconMap = beacon.GetMapSafe();

    // Url
    const TString& urlStr = beaconMap.at("url").GetStringSafe();
    NUri::TState::EParsed state = Url.Parse(urlStr,
        NUri::TFeature::FeaturesDefault | NUri::TFeature::FeatureSchemeKnown);
    if (state != NUri::TState::ParsedOK) {
        ythrow yexception() << "can't parse url: " << state << ", " << urlStr.Quote();
    }

    // ServerIp
    const TStringBuf& serverIpPort = beaconMap.at("server_ip").GetStringSafe();
    ServerIp = serverIpPort.RBefore(':').RBefore(']').After('[');

    // Age
    Age = TDuration::MilliSeconds(beaconMap.at("request_age_ms").GetUIntegerSafe());

    // Weight
    double sample_rate = beaconMap.at("sample_rate").GetDoubleSafe();
    if (sample_rate < 0.005 || sample_rate > 1) {
        ythrow yexception() << "sample_rate out of expected range: " << sample_rate;
    }
    Weight = std::lround(1 / sample_rate);

    // LegacyStatus
    if (beaconMap.contains("http_response_code")) {
        LegacyStatus = "code_any";
    } else {
        const TString& error = beaconMap.at("failure_data").GetMapSafe()
            .at("custom_error").GetStringSafe();
        if (KnownErrors_.contains(error)) {
            LegacyStatus = ErrorToLegacyStatus(error);
        } else {
            LegacyStatus = "error_other";
        }
    }
}

TVector<TString> TBeacon::AllLegacyStatuses() {
    TVector<TString> result;
    for (const auto& e : KnownErrors_) {
        result.push_back(ErrorToLegacyStatus(e));
    }
    result.push_back("error_other");
    result.push_back("code_any");
    return result;
}

TString TBeacon::ErrorToLegacyStatus(TStringBuf error) {
    TString result = ToLowerUTF8(error);
    SubstGlobal(result, "::", "_");
    return "error_" + result;
}

const THashSet<TString> TBeacon::KnownErrors_ = {
    "net::ERR_ABORTED",
    "net::ERR_ADDRESS_UNREACHABLE",
    "net::ERR_CERT_AUTHORITY_INVALID",
    "net::ERR_SSL_PROTOCOL_ERROR",
    "net::ERR_SSL_VERSION_OR_CIPHER_MISMATCH",
    "net::ERR_INSECURE_RESPONSE",
    "net::ERR_DNS_TIMED_OUT",
    "net::ERR_NAME_NOT_RESOLVED",
    "net::ERR_NAME_RESOLUTION_FAILED",
    "net::ERR_CONNECTION_ABORTED",
    "net::ERR_CONNECTION_CLOSED",
    "net::ERR_CONNECTION_FAILED",
    "net::ERR_CONNECTION_REFUSED",
    "net::ERR_CONNECTION_RESET",
    "net::ERR_CONNECTION_TIMED_OUT",
    "net::ERR_EMPTY_RESPONSE",
    "net::ERR_INVALID_RESPONSE",
    "net::ERR_SPDY_PING_FAILED",
    "net::ERR_SPDY_PROTOCOL_ERROR",
    "net::ERR_TIMED_OUT",
};
