#include <library/cpp/resource/resource.h>
#include <library/cpp/testing/unittest/registar.h>

#include <balancer/drlogger_nel/library/metric_storage.h>


namespace {

    const TString CONFIG_FILENAME = "simple_config.yaml";
    const TString TENANT = "yandex";

    const TString GetYasmExpectation(const TString& error_type, const TString& site, const TString& asn, int value) {
        return TStringBuilder{}
            << "[\"" 
            << asn        << "_" 
            << site       << "_" 
            << error_type << "_summ\","
            << value      << "]";
    }

    const TString GetSolomonExpectation(const TString& error_type, const TString& site, const TString& asn, int value) {
        return TStringBuilder{}
            << "{\"labels\":{\"asn\":\""          << asn 
            << "\",\"site\":\""                   << site
            << "\",\"error_type\":\""             << error_type 
            << "\"},\"type\":\"RATE\",\"value\":" << value 
            << "}";
    }

    void FillReport(TNELReport& report, const TString& errorType, const TString& url, const TString& asn) {
        report.GroupedErrorType = TString(errorType);
        auto result = report.Url.Parse(url, NUri::TFeature::FeaturesDefault | NUri::TFeature::FeatureSchemeKnown);
        UNIT_ASSERT_EQUAL(result, NUri::TState::ParsedOK);
        report.Sender.ASNs.push_back(asn);
    }

}


Y_UNIT_TEST_SUITE(TMetricKeyTest) {

    Y_UNIT_TEST(YASMMetricName) {
        TMetricKey key("asn", "site", "error");
        UNIT_ASSERT(!key.YASMMetricName.has_value());

        TMetricKey keyWithMetricName("asn", "site", "error", true);
        UNIT_ASSERT_STRINGS_EQUAL(*keyWithMetricName.YASMMetricName, "asn_site_error_summ");
    }

}
 
 
Y_UNIT_TEST_SUITE(TTenantMetricStorageTest) {

    Y_UNIT_TEST(WriteMetricsForYASM) {
        TConfiguration configuration;
        UNIT_ASSERT(configuration.ReadFromYAMLString(NResource::Find(CONFIG_FILENAME)));
        TTenantMetricStorage tenantMetricStorage(configuration.Tenants[TENANT]);

        TStringStream out;
        NJson::TJsonWriter jsonWriter(&out, false);
 
        // Didn't write anything into the metric storage yet
        tenantMetricStorage.WriteMetricsForYASM(jsonWriter);
        jsonWriter.Flush();
        UNIT_ASSERT_STRING_CONTAINS(out.Str(), GetYasmExpectation("http_error", "yandex_ru", "AS123", 0));

        out.Clear();

        // Valid report
        TNELReportSender sender;
        TNELReport report(sender);
        FillReport(report, "http_error", "https://yandex.ru", "AS123");
        tenantMetricStorage.StoreNELReport(report);

        // Check metric value
        tenantMetricStorage.WriteMetricsForYASM(jsonWriter);
        jsonWriter.Flush();
        UNIT_ASSERT_STRING_CONTAINS(out.Str(), GetYasmExpectation("http_error", "yandex_ru", "AS123", 1));
        UNIT_ASSERT_STRING_CONTAINS(out.Str(), GetYasmExpectation("http_error", "yandex_ru", "https", 1));
    }

    Y_UNIT_TEST(WriteMetricsForSolomonJson) {
        TConfiguration configuration;
        UNIT_ASSERT(configuration.ReadFromYAMLString(NResource::Find(CONFIG_FILENAME)));
        TTenantMetricStorage tenantMetricStorage(configuration.Tenants[TENANT]);

        TStringStream out;
        NJson::TJsonWriter jsonWriter(&out, false);

        // Didn't write anything into the metric storage yet
        tenantMetricStorage.WriteMetricsForSolomonJson(jsonWriter);
        jsonWriter.Flush();
        UNIT_ASSERT_STRING_CONTAINS(out.Str(), GetSolomonExpectation("http_error", "yandex_ru", "AS123", 0));

        out.Clear();

        // AS123 / yandex_ru / http_error += 1
        TNELReportSender sender;
        TNELReport report(sender);
        FillReport(report, "http_error", "https://yandex.ru", "AS123");
        tenantMetricStorage.StoreNELReport(report);

        // Check metric value
        tenantMetricStorage.WriteMetricsForSolomonJson(jsonWriter);
        jsonWriter.Flush();
        UNIT_ASSERT_STRING_CONTAINS(out.Str(), GetSolomonExpectation("http_error", "yandex_ru", "AS123", 1));
    }

    void TestStoreNELReport(const TNELReport& report, const TString& expectAfter) {
        TConfiguration configuration;
        UNIT_ASSERT(configuration.ReadFromYAMLString(NResource::Find(CONFIG_FILENAME)));
        TTenantMetricStorage tenantMetricStorage(configuration.Tenants[TENANT]);

        TStringStream out;
        NJson::TJsonWriter jsonWriter(&out, false);

        tenantMetricStorage.StoreNELReport(report);
        
        tenantMetricStorage.WriteMetricsForYASM(jsonWriter);
        jsonWriter.Flush();
        UNIT_ASSERT_STRING_CONTAINS(out.Str(), expectAfter);
    }

    Y_UNIT_TEST(StoreNELReportUnidentifiedSite) {
        TNELReportSender sender;
        TNELReport report(sender);
        FillReport(report, "tls_error", "https://yandex.com", "AS123");

        TestStoreNELReport(report, GetYasmExpectation("tls_error", "yandex_ru", "AS123", 0));
    }

    Y_UNIT_TEST(StoreNELReportASOther) {
        TNELReportSender sender;
        TNELReport report(sender);
        FillReport(report, "tcp_error", "https://yandex.ru", "AS");

        TestStoreNELReport(report, GetYasmExpectation("tcp_error", "yandex_ru", "ASother", 1));
    }

    Y_UNIT_TEST(StoreNELReportUrlWithPath) {
        TNELReportSender sender;
        TNELReport weatherReport(sender);
        FillReport(weatherReport, "http_error", "https://yandex.ru/weather", "AS123");

        TestStoreNELReport(weatherReport, GetYasmExpectation("http_error", "yandex_ru", "AS123", 1));
        TestStoreNELReport(weatherReport, GetYasmExpectation("http_error", "weather", "AS123", 1));

        TNELReport pogodaReport(sender);
        FillReport(pogodaReport, "http_error", "https://yandex.ru/pogoda/123", "AS123");

        TestStoreNELReport(weatherReport, GetYasmExpectation("http_error", "yandex_ru", "AS123", 1));
        TestStoreNELReport(weatherReport, GetYasmExpectation("http_error", "weather", "AS123", 1));
    }

}


Y_UNIT_TEST_SUITE(TMetricStorageTest) {

    void TestWriteMetrics(
        const EMetricWriteFormat format,
        const TString& expectBefore,
        const TString& expectAfter,
        const TString& expectFullResponsePart
    ) {
        TConfiguration configuration;
        UNIT_ASSERT(configuration.ReadFromYAMLString(NResource::Find(CONFIG_FILENAME)));
        TMetricStorage metricStorage(configuration);

        TStringStream out;

        // Didn't write anything into the metric storage yet
        metricStorage.WriteMetrics(format, TENANT, &out);
        UNIT_ASSERT_STRING_CONTAINS(out.Str(), expectBefore);
        UNIT_ASSERT_STRING_CONTAINS(out.Str(), expectFullResponsePart);

        out.Clear();

        // AS123 / yandex_ru / http_error += 1
        TNELReportSender sender;
        TNELReport report(sender);
        FillReport(report, "http_error", "https://yandex.ru", "AS123");
        metricStorage.StoreNELReport(TENANT, report);

        // Check metric value
        metricStorage.WriteMetrics(format, TENANT, &out);
        UNIT_ASSERT_STRING_CONTAINS(out.Str(), expectAfter);
        UNIT_ASSERT_STRING_CONTAINS(out.Str(), expectFullResponsePart);
    }

    Y_UNIT_TEST(WriteMetricsForYASM) {
        TestWriteMetrics(
            EMetricWriteFormat::YASM,
            GetYasmExpectation("http_error", "yandex_ru", "AS123", 0),
            GetYasmExpectation("http_error", "yandex_ru", "AS123", 1),
            "[["
        );

        TestWriteMetrics(
            EMetricWriteFormat::YASM,
            GetYasmExpectation("http_error", "yandex_ru", "AS123", 0),
            GetYasmExpectation("http_error", "yandex_ru", "https", 1),
            "[["
        );
    }

    Y_UNIT_TEST(WriteMetricsForSolomonJson) {
        TestWriteMetrics(
            EMetricWriteFormat::SOLOMON_JSON,
            GetSolomonExpectation("http_error", "yandex_ru", "AS123", 0),
            GetSolomonExpectation("http_error", "yandex_ru", "AS123", 1),
            "}]}"
        );
    }

}

