#include "ShardokLatencyTrace.hpp"

#include <cstdlib>
#include <iostream>
#include <mutex>
#include <sstream>

namespace net::eagle0::common {
namespace {

auto ReadEnvEnabled() -> bool {
    const char* value = std::getenv("EAGLE0_SHARDOK_LATENCY_TRACE");
    if (value == nullptr) { return false; }
    const std::string text(value);
    return text == "1" || text == "true" || text == "TRUE" || text == "yes" || text == "YES";
}

auto TraceEnabled() -> bool {
    static const bool enabled = ReadEnvEnabled();
    return enabled;
}

auto ReadGameFilter() -> std::string {
    const char* value = std::getenv("EAGLE0_SHARDOK_LATENCY_GAME_ID");
    return value == nullptr ? "" : std::string(value);
}

auto GameFilter() -> const std::string& {
    static const std::string filter = ReadGameFilter();
    return filter;
}

auto JsonEscape(std::string_view value) -> std::string {
    std::ostringstream out;
    for (const char c : value) {
        switch (c) {
            case '"': out << "\\\""; break;
            case '\\': out << "\\\\"; break;
            case '\n': out << "\\n"; break;
            case '\r': out << "\\r"; break;
            case '\t': out << "\\t"; break;
            default: out << c; break;
        }
    }
    return out.str();
}

auto LogMutex() -> std::mutex& {
    static auto mutex = new std::mutex();
    return *mutex;
}

}  // namespace

auto ShardokLatencyTraceEnabledForGame(std::string_view gameId) -> bool {
    if (!TraceEnabled()) { return false; }
    const std::string& filter = GameFilter();
    return filter.empty() || filter == gameId;
}

void EmitShardokLatencyTrace(
        std::string_view event,
        std::string_view gameId,
        const ShardokLatencyFields& fields,
        std::chrono::steady_clock::duration duration) {
    if (!ShardokLatencyTraceEnabledForGame(gameId)) { return; }

    const auto durationMs =
            std::chrono::duration_cast<std::chrono::microseconds>(duration).count() / 1000.0;

    std::ostringstream out;
    out << "{\"event\":\"shardok_latency\","
        << "\"component\":\"shardok\","
        << "\"span\":\"" << JsonEscape(event) << "\","
        << "\"game_id\":\"" << JsonEscape(gameId) << "\","
        << "\"duration_ms\":" << durationMs;
    for (const auto& [key, value] : fields) {
        out << ",\"" << JsonEscape(key) << "\":\"" << JsonEscape(value) << "\"";
    }
    out << "}";

    std::scoped_lock lock(LogMutex());
    std::cerr << out.str() << '\n';
}

ScopedShardokLatencyTrace::ScopedShardokLatencyTrace(
        std::string event,
        std::string gameId,
        ShardokLatencyFields fields)
    : event_(std::move(event)),
      gameId_(std::move(gameId)),
      fields_(std::move(fields)) {
    if (ShardokLatencyTraceEnabledForGame(gameId_)) { start_ = std::chrono::steady_clock::now(); }
}

ScopedShardokLatencyTrace::~ScopedShardokLatencyTrace() {
    if (!start_.has_value()) { return; }
    EmitShardokLatencyTrace(event_, gameId_, fields_, std::chrono::steady_clock::now() - *start_);
}

}  // namespace net::eagle0::common
