#include <prometheus/counter.h>
#include <prometheus/exposer.h>
#include <prometheus/registry.h>

#include <array>
#include <chrono>
#include <cstdlib>
#include <memory>
#include <string>
#include <thread>

int main()
{
    using namespace prometheus;

    // create an http server running on port 8080
    Exposer exposer{"127.0.0.1:8080"};

    // create a metrics registry
    // @note it's the users responsibility to keep the object alive
    auto registry = std::make_shared<Registry>();

    // add a new counter family to the registry (families combine values with the
    // same name, but distinct label dimensions)
    //
    // @note please follow the metric-naming best-practices:
    // https://prometheus.io/docs/practices/naming/
    auto &packet_counter =
        BuildCounter().Name("observed_packets_total").Help("Number of observed packets").Register(*registry);

    // add and remember dimensional data, incrementing those is very cheap
    auto &tcp_rx_counter = packet_counter.Add({{"protocol", "tcp"}, {"direction", "rx"}});
    auto &tcp_tx_counter = packet_counter.Add({{"protocol", "tcp"}, {"direction", "tx"}});
    auto &udp_rx_counter = packet_counter.Add({{"protocol", "udp"}, {"direction", "rx"}});
    auto &udp_tx_counter = packet_counter.Add({{"protocol", "udp"}, {"direction", "tx"}});

    // add a counter whose dimensional data is not known at compile time
    // nevertheless dimensional values should only occur in low cardinality:
    // https://prometheus.io/docs/practices/naming/#labels
    auto &http_requests_counter =
        BuildCounter().Name("http_requests_total").Help("Number of HTTP requests").Register(*registry);

    // ask the exposer to scrape the registry on incoming HTTP requests
    exposer.RegisterCollectable(registry);

    for (;;)
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        const auto random_value = std::rand();

        if (random_value & 1)
            tcp_rx_counter.Increment();
        if (random_value & 2)
            tcp_tx_counter.Increment();
        if (random_value & 4)
            udp_rx_counter.Increment();
        if (random_value & 8)
            udp_tx_counter.Increment();

        const std::array<std::string, 4> methods = {"GET", "PUT", "POST", "HEAD"};
        auto method = methods.at(random_value % methods.size());
        // dynamically calling Family<T>.Add() works but is slow and should be
        // avoided
        http_requests_counter.Add({{"method", method}}).Increment();
    }
    return 0;
}
