#pragma once

#include "configuration.h"
#include "nel_report.h"

#include <geobase/include/lookup.hpp>

#include <library/cpp/http/misc/httpreqdata.h>
#include <library/cpp/http/server/http.h>
#include <library/cpp/json/json_writer.h>
#include <library/cpp/monlib/encode/spack/spack_v1.h>

#include <optional>


enum EMetricWriteFormat {
    YASM,
    SOLOMON_JSON,
    SOLOMON_SPACK,
};


class TMetricKey {
public:
    TMetricKey(const TString asn, const TString site, const TString errorType, bool initMetricName = false)
        : ASN(asn)
        , Site(site)
        , ErrorType(errorType)
        // YASMMetricName is initialized using a boolean constructor argument instead of using lazy initialization
        // in order for WriteMetricsForYASM methods to be const.
        , YASMMetricName(initMetricName
            ? std::optional<TString>(TString::Join(ASN, "_", Site, "_", ErrorType, "_summ"))
            : std::nullopt
        )
        , ReportToSolomon(ASN != AnyASN)
    { }

    std::tuple<TString, TString, TString> MakeTuple() const {
        return std::make_tuple(ASN, Site, ErrorType);
    }

    bool operator==(const TMetricKey& b) const {
        return MakeTuple() == b.MakeTuple();
    }

public:
    static const TString OtherASN;
    static const TString AnyASN;

    const TString ASN;
    const TString Site;
    const TString ErrorType;
    const std::optional<TString> YASMMetricName;
    const bool ReportToSolomon;
};


template<>
struct THash<TMetricKey> {
    size_t operator()(const TMetricKey& key) {
        return THash<std::tuple<TString, TString, TString>>()(key.MakeTuple());
    }
};


class TTenantMetricStorage {
public:
    explicit TTenantMetricStorage(const TTenantConfiguration& configuration);

    void StoreNELReport(const TNELReport& report);
    
    void WriteMetricsForYASM(NJson::TJsonWriter& jsonWriter) const;
    void WriteMetricsForSolomonJson(NJson::TJsonWriter& jsonWriter) const;
    void WriteMetricsForSolomonSpack(NMonitoring::IMetricEncoderPtr& spackEncoder) const;

private:
    void GenerateMetrics();
    
    void MatchReportAsns(const TNELReport& report, THashSet<TString>& matchedASNs);
    void MatchReportSites(const TNELReport& report, THashSet<TString>& matchedReports);
    void ExtractReportErrorTypes(const TNELReport& report, THashSet<TString>& errorTypes);

private:
    const TTenantConfiguration& Configuration_;
    THashMap<TMetricKey, TAtomic> Metrics_; 
};


class TMetricStorage {
public:
    explicit TMetricStorage(const TConfiguration& configuration);

    void StoreNELReport(const TString& tenant, const TNELReport& report);
    // TODO
    // void CountTooLargeRequest();

    void WriteMetrics(const EMetricWriteFormat format, const TString& tenant, IOutputStream* out) const;

private:
    void WriteMetricsForYASM(const TString& tenant, IOutputStream* out) const;
    void WriteMetricsForSolomonJson(const TString& tenant, IOutputStream* out) const;
    void WriteMetricsForSolomonSpack(const TString& tenant, IOutputStream* out) const;

private:
    const TConfiguration& Configuration_;
    THashMap<TString, TTenantMetricStorage> Tenants_;
};

