/* Copyright (c) 2024 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include <lua.hpp>
#include <mutex>
#include <atomic>
#include <map>
#include <set>
#include "opentelemetry/metrics/meter.h"
#include "opentelemetry/metrics/provider.h"
#include "opentelemetry/metrics/meter_provider.h"
#include "opentelemetry/metrics/sync_instruments.h"
#include "opentelemetry/metrics/async_instruments.h"
#include "opentelemetry/metrics/observer_result.h"
#include "opentelemetry/sdk/metrics/meter_provider_factory.h"
#include "opentelemetry/sdk/metrics/meter_provider.h"
#include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader_options.h"
#include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader_factory.h"
#include "opentelemetry/sdk/metrics/push_metric_exporter.h"
#include "opentelemetry/exporters/otlp/otlp_grpc_metric_exporter_factory.h"
#include "opentelemetry/exporters/otlp/otlp_grpc_metric_exporter_options.h"
#include "opentelemetry/exporters/otlp/otlp_metric_manager.h"
#if defined(BUILD_TYPE) && defined(BUILD_TYPE_DT) && BUILD_TYPE == BUILD_TYPE_DT
#include "opentelemetry/exporters/ostream/metric_exporter_factory.h"
#include "opentelemetry/exporters/ostream/metric_manager.h"
#include <fstream>
#endif

#include "utils/utils.h"
#include "utils/parse_cfg.h"

namespace Otel {
namespace metrics_sdk = opentelemetry::sdk::metrics;
namespace metrics_api = opentelemetry::metrics;
std::atomic<bool> isInit{false};

class LuaOtelMeterProvider {
public:
    LuaOtelMeterProvider();

    void InitMeterProvider(int export_interval = 2000, int export_timeout = 1000)
    {
        std::lock_guard<std::recursive_mutex> guard(m_mutex);
        if (m_export_interval == export_interval && m_export_timeout == export_timeout) {
            return;
        }
#if defined(BUILD_TYPE) && defined(BUILD_TYPE_DT) && BUILD_TYPE == BUILD_TYPE_DT
        Utils::initialize_telemetry();
        file.open("./metrics.txt");
        auto exporter = opentelemetry::exporter::metrics::OStreamMetricExporterFactory::Create(file);
        metrics_sdk::PeriodicExportingMetricReaderOptions options;
        options.export_interval_millis = std::chrono::milliseconds(export_interval);
        options.export_timeout_millis = std::chrono::milliseconds(export_timeout);
        auto reader =
            metrics_sdk::PeriodicExportingMetricReaderFactory::Create(std::move(exporter), options);
        auto u_provider = metrics_sdk::MeterProviderFactory::Create();
        auto p = static_cast<metrics_sdk::MeterProvider *>(u_provider.get());
        p->AddMetricReader(std::move(reader));
        metrics_api::Provider::SetMeterProvider(std::shared_ptr<metrics_api::MeterProvider>(std::move(u_provider)));
#else
        Utils::initialize_telemetry();
        opentelemetry::exporter::otlp::OtlpGrpcMetricExporterOptions opts;
        opts.endpoint = "localhost:44318";
        opts.use_ssl_credentials = false;
        auto exporter = opentelemetry::exporter::otlp::OtlpGrpcMetricExporterFactory::Create(opts);
        metrics_sdk::PeriodicExportingMetricReaderOptions options;
        options.export_interval_millis = std::chrono::milliseconds(export_interval);
        options.export_timeout_millis = std::chrono::milliseconds(export_timeout);
        auto reader =
            metrics_sdk::PeriodicExportingMetricReaderFactory::Create(std::move(exporter), options);
        auto u_provider = metrics_sdk::MeterProviderFactory::Create();
        auto p = static_cast<metrics_sdk::MeterProvider *>(u_provider.get());
        p->AddMetricReader(std::move(reader));
        metrics_api::Provider::SetMeterProvider(std::shared_ptr<metrics_api::MeterProvider>(std::move(u_provider)));
#endif
        m_export_interval = export_interval;
        m_export_timeout = export_timeout;
        isInit.store(true, std::memory_order_release);
    }

    nostd::shared_ptr<metrics_api::Meter> GetMeter(
        nostd::string_view name, nostd::string_view version, nostd::string_view schema_url)
    {
        if (!isInit.load(std::memory_order_acquire)) {
            std::lock_guard<std::recursive_mutex> guard(m_mutex);
            if (!isInit.load(std::memory_order_acquire)) {
                InitMeterProvider();
            }
        }
        return metrics_api::Provider::GetMeterProvider()->GetMeter(name, version, schema_url);
    }

    void ForceFlush(std::chrono::microseconds timeout)
    {
        if (!isInit.load(std::memory_order_acquire)) {
            return;
        }
        static_cast<metrics_sdk::MeterProvider *>(metrics_api::Provider::GetMeterProvider().get())->ForceFlush(timeout);
    }

    void DeInitMeterProvider();
    
    ~LuaOtelMeterProvider()
    {
#if defined(BUILD_TYPE) && defined(BUILD_TYPE_DT) && BUILD_TYPE == BUILD_TYPE_DT
        file.close();
#endif
    }

private:
    int m_export_interval = 0;
    int m_export_timeout = 0;
    std::recursive_mutex m_mutex;
#if defined(BUILD_TYPE) && defined(BUILD_TYPE_DT) && BUILD_TYPE == BUILD_TYPE_DT
    std::ofstream file;
#endif
};

class SharedCounter {
public:
    SharedCounter(nostd::unique_ptr<metrics_api::Counter<double>> counter) : m_counter(std::move(counter))
    {
    }

    void Add(double value, AttributeMap &attributes)
    {
        std::lock_guard<std::mutex> guard(m_mutex);
        m_counter->Add(value, attributes);
    }

private:
    nostd::unique_ptr<metrics_api::Counter<double>> m_counter;
    std::mutex m_mutex;
};

class SharedUpDownCounter {
public:
    SharedUpDownCounter(nostd::unique_ptr<metrics_api::UpDownCounter<double>> updowncounter)
        : m_updowncounter(std::move(updowncounter))
    {
    }

    void Add(double value, AttributeMap &attributes)
    {
        std::lock_guard<std::mutex> guard(m_mutex);
        m_updowncounter->Add(value, attributes);
    }

private:
    nostd::unique_ptr<metrics_api::UpDownCounter<double>> m_updowncounter;
    std::mutex m_mutex;
};

class SharedHistogram {
    public:
        SharedHistogram(nostd::unique_ptr<metrics_api::Histogram<double>> histogram)
            : m_histogram(std::move(histogram))
        {
        }
    
        void Record(double value, AttributeMap &attributes)
        {
            std::lock_guard<std::mutex> guard(m_mutex);
            m_histogram->Record(value, attributes);
        }
    
    private:
        nostd::unique_ptr<metrics_api::Histogram<double>> m_histogram;
        std::mutex m_mutex;
};

static std::map<std::string, Utils::Metric> metrics;
static LuaOtelMeterProvider meterProvider;
static std::mutex enabledMetricsMutex;
static std::set<std::string> enabledMetrics;

class LuaOtelMeter {
public:
    LuaOtelMeter(nostd::string_view name, nostd::string_view version, nostd::string_view schema_url)
    {
        m_meter = meterProvider.GetMeter(name, version, schema_url);
    }

    void ForceFlush(std::chrono::microseconds timeout = std::chrono::microseconds(0))
    {
        meterProvider.ForceFlush(timeout);
    }

    nostd::shared_ptr<SharedCounter> CreateDoubleCounter(
        nostd::string_view name, nostd::string_view description, nostd::string_view unit)
    {
        std::lock_guard<std::mutex> guard(counter_mutex);
        auto iter = counter_map.find(name.data());
        if (iter != counter_map.end()) {
            return iter->second;
        }
        nostd::unique_ptr<metrics_api::Counter<double>> m = m_meter->CreateDoubleCounter(name, description, unit);
        nostd::shared_ptr<SharedCounter> n = nostd::shared_ptr<SharedCounter>(new SharedCounter(std::move(m)));
        counter_map.insert(std::pair<std::string, nostd::shared_ptr<SharedCounter>>(name.data(), n));
        return n;
    }

    nostd::shared_ptr<SharedUpDownCounter> CreateDoubleUpDownCounter(
        nostd::string_view name, nostd::string_view description, nostd::string_view unit)
    {
        std::lock_guard<std::mutex> guard(updowncounter_mutex);
        auto iter = updowncounter_map.find(name.data());
        if (iter != updowncounter_map.end()) {
            return iter->second;
        }
        nostd::unique_ptr<metrics_api::UpDownCounter<double>> m =
            m_meter->CreateDoubleUpDownCounter(name, description, unit);
        nostd::shared_ptr<SharedUpDownCounter> n =
            nostd::shared_ptr<SharedUpDownCounter>(new SharedUpDownCounter(std::move(m)));
        updowncounter_map.insert(std::pair<std::string, nostd::shared_ptr<SharedUpDownCounter>>(name.data(), n));
        return n;
    }

    nostd::shared_ptr<SharedHistogram> CreateDoubleHistogram(
        nostd::string_view name, nostd::string_view description, nostd::string_view unit)
    {
        std::lock_guard<std::mutex> guard(histogram_mutex);
        auto iter = histogram_map.find(name.data());
        if (iter != histogram_map.end()) {
            return iter->second;
        }
        nostd::unique_ptr<metrics_api::Histogram<double>> m = m_meter->CreateDoubleHistogram(name, description, unit);
        nostd::shared_ptr<SharedHistogram> n = nostd::shared_ptr<SharedHistogram>(new SharedHistogram(std::move(m)));
        histogram_map.insert(std::pair<std::string, nostd::shared_ptr<SharedHistogram>>(name.data(), n));
        return n;
    }

    nostd::shared_ptr<metrics_api::ObservableInstrument> CreateDoubleObservableCounter(
        nostd::string_view name, nostd::string_view description, nostd::string_view unit)
    {
        std::lock_guard<std::mutex> guard(async_counter_mutex);
        auto iter = async_counter_map.find(name.data());
        if (iter != async_counter_map.end()) {
            return iter->second;
        }
        nostd::shared_ptr<metrics_api::ObservableInstrument> n = m_meter->CreateDoubleObservableCounter(name, description, unit);
        async_counter_map.insert(std::pair<std::string, nostd::shared_ptr<metrics_api::ObservableInstrument>>(name.data(), n));
        return n;
    }

    nostd::shared_ptr<metrics_api::ObservableInstrument> CreateDoubleObservableUpDownCounter(
        nostd::string_view name, nostd::string_view description, nostd::string_view unit)
    {
        std::lock_guard<std::mutex> guard(async_updowncounter_mutex);
        auto iter = async_updowncounter_map.find(name.data());
        if (iter != async_updowncounter_map.end()) {
            return iter->second;
        }
        nostd::shared_ptr<metrics_api::ObservableInstrument> n = m_meter->CreateDoubleObservableUpDownCounter(name, description, unit);
        async_updowncounter_map.insert(std::pair<std::string, nostd::shared_ptr<metrics_api::ObservableInstrument>>(name.data(), n));
        return n;
    }

    nostd::shared_ptr<metrics_api::ObservableInstrument> CreateDoubleObservableGauge(
        nostd::string_view name, nostd::string_view description, nostd::string_view unit)
    {
        std::lock_guard<std::mutex> guard(async_gauge_mutex);
        auto iter = async_gauge_map.find(name.data());
        if (iter != async_gauge_map.end()) {
            return iter->second;
        }
        nostd::shared_ptr<metrics_api::ObservableInstrument> n = m_meter->CreateDoubleObservableGauge(name, description, unit);
        async_gauge_map.insert(std::pair<std::string, nostd::shared_ptr<metrics_api::ObservableInstrument>>(name.data(), n));
        return n;
    }

    static void CleanupMetric()
    {
        counter_map.clear();
        updowncounter_map.clear();
        histogram_map.clear();
        async_counter_map.clear();
        async_updowncounter_map.clear();
        async_gauge_map.clear();
    }

private:
    static std::map<std::string, nostd::shared_ptr<SharedCounter>> counter_map;
    static std::mutex counter_mutex;
    static std::map<std::string, nostd::shared_ptr<SharedUpDownCounter>> updowncounter_map;
    static std::mutex updowncounter_mutex;
    static std::map<std::string, nostd::shared_ptr<SharedHistogram>> histogram_map;
    static std::mutex histogram_mutex;

    static std::map<std::string, nostd::shared_ptr<metrics_api::ObservableInstrument>> async_counter_map;
    static std::mutex async_counter_mutex;
    static std::map<std::string, nostd::shared_ptr<metrics_api::ObservableInstrument>> async_updowncounter_map;
    static std::mutex async_updowncounter_mutex;
    static std::map<std::string, nostd::shared_ptr<metrics_api::ObservableInstrument>> async_gauge_map;
    static std::mutex async_gauge_mutex;

    nostd::shared_ptr<metrics_api::Meter> m_meter;
};

std::mutex LuaOtelMeter::counter_mutex;
std::map<std::string, nostd::shared_ptr<SharedCounter>> LuaOtelMeter::counter_map;
std::mutex LuaOtelMeter::updowncounter_mutex;
std::map<std::string, nostd::shared_ptr<SharedUpDownCounter>> LuaOtelMeter::updowncounter_map;
std::mutex LuaOtelMeter::histogram_mutex;
std::map<std::string, nostd::shared_ptr<SharedHistogram>> LuaOtelMeter::histogram_map;
std::mutex LuaOtelMeter::async_counter_mutex;
std::map<std::string, nostd::shared_ptr<metrics_api::ObservableInstrument>> LuaOtelMeter::async_counter_map;
std::mutex LuaOtelMeter::async_updowncounter_mutex;
std::map<std::string, nostd::shared_ptr<metrics_api::ObservableInstrument>> LuaOtelMeter::async_updowncounter_map;
std::mutex LuaOtelMeter::async_gauge_mutex;
std::map<std::string, nostd::shared_ptr<metrics_api::ObservableInstrument>> LuaOtelMeter::async_gauge_map;

LuaOtelMeterProvider::LuaOtelMeterProvider()
{
#if defined(BUILD_TYPE) && defined(BUILD_TYPE_DT) && BUILD_TYPE == BUILD_TYPE_DT
    metrics = Utils::parseMetrics("observability.json");
#else
    metrics = Utils::parseMetrics("/opt/bmc/conf/observability.json");
#endif
}

void LuaOtelMeterProvider::DeInitMeterProvider()
{
    if (!isInit.load(std::memory_order_acquire)) {
        return;
    }
    std::lock_guard<std::recursive_mutex> guard(m_mutex);
    if (!isInit.load(std::memory_order_acquire)) {
        return;
    }
    isInit.store(false, std::memory_order_release);
    m_export_interval = 0;
    m_export_timeout = 0;
    LuaOtelMeter::CleanupMetric();
    std::shared_ptr<metrics_api::MeterProvider> none;
    metrics_api::Provider::SetMeterProvider(none);
}

struct LuaCounter {
    std::string name;
    nostd::shared_ptr<SharedCounter> counter;
};

struct LuaUpDownCounter {
    std::string name;
    nostd::shared_ptr<SharedUpDownCounter> updowncounter;
};

struct LuaHistogram {
    std::string name;
    nostd::shared_ptr<SharedHistogram> histogram;
};

struct LuaObservableCounter {
    std::string name;
    nostd::shared_ptr<metrics_api::ObservableInstrument> observablecounter;
};

struct LuaObservableUpDownCounter {
    std::string name;
    nostd::shared_ptr<metrics_api::ObservableInstrument> observableupdowncounter;
};

struct LuaObservableGauge {
    std::string name;
    nostd::shared_ptr<metrics_api::ObservableInstrument> observablegauge;
};

struct LuaCallback {
    lua_State *L;
    int lua_callback_ref;
};

static int l_get_meter(lua_State *L)
{
    if (!isInit.load(std::memory_order_acquire)) {
        luaL_error(L, "get meter failed, provider has not been initialized");
    }
    const char *name = luaL_checkstring(L, 1);
    if (name == nullptr || strlen(name) == 0) {
        luaL_error(L, "meter name can not be empty");
    }

    const char *version = luaL_optstring(L, 2, "");
    const char *schema_url = luaL_optstring(L, 3, "");
    void *ud = lua_newuserdata(L, sizeof(LuaOtelMeter));
    new (ud) LuaOtelMeter(name, version, schema_url);
    luaL_getmetatable(L, "Meter");
    lua_setmetatable(L, -2);
    return 1;
}

static int l_init_meter_provider(lua_State *L)
{
    int interval = luaL_checkinteger(L, 1);
    int timeout = luaL_checkinteger(L, 2);
    meterProvider.InitMeterProvider(interval, timeout);
    return 0;
}

static int l_deinit_meter_provider(lua_State *L)
{
    meterProvider.DeInitMeterProvider();
    return 0;
}

// 设置上报指标集合，在集合中的指标则上报
static int l_set_enabled_metrics(lua_State *L)
{
    std::lock_guard<std::mutex> guard(enabledMetricsMutex);
    std::set<std::string> metric_set;
    if (!lua_istable(L, 1)) {
        luaL_error(L, "invalid input, expert table");
    }
    int len = lua_rawlen(L, 1);
    for (int i = 0; i < len; i++) {
        lua_geti(L, 1, i + 1); // 将table[i]压入栈顶
        if (!lua_isstring(L, -1)) {
            luaL_error(L, "invalid parameter, get metric list failed");
        }
        const char *str = lua_tostring(L, -1);
        auto res = metrics.find(str);
        if (res == metrics.end()) {
            luaL_error(L, "intrument name %s is not defined", str);
        }
        metric_set.insert(str);
        lua_pop(L, 1);
    }
    enabledMetrics = metric_set;
#if defined(BUILD_TYPE) && defined(BUILD_TYPE_DT) && BUILD_TYPE == BUILD_TYPE_DT
    opentelemetry::exporter::metrics::MetricManager::SetMetricList(metric_set);
#else
    opentelemetry::exporter::otlp::MetricManager::SetMetricList(metric_set);
#endif
    return 0;
}

// 验证name参数
static void validate_name(lua_State *L, const char *name, const char *type)
{
    if (name == nullptr || strlen(name) == 0) { // name命名长度不能为0
        luaL_error(L, "name can not be empty");
    }
    if (strlen(name) > 255) { // name命名长度不允许超过255个字符
        luaL_error(L, "name exceeds maximum length of 255 characters");
    }
    if (!isalpha(static_cast<unsigned char>(*name))) { // name命名必须以字母开头
        luaL_error(L, "name must start with an alphabetic character");
    }
    for (size_t i = 1; i < strlen(name); ++i) {
        char c = name[i];
        // name命名仅可包含'-'，'_'，'.'，'/'这几种特殊字符
        if (!isalnum(static_cast<unsigned char>(c)) && c != '-' && c != '_' && c != '.' && c != '/') {
            luaL_error(L, "name contains invalid character: %c", c);
        }
    }
    auto res = metrics.find(name);
    if (res == metrics.end()) {
        luaL_error(L, "intrument name %s is not defined", name);
    }
    if (res->second.type != type) {
        luaL_error(L, "intrument name %s type mismatch", name);
    }
}

// 验证unit参数
static void validate_unit(lua_State *L, const char *unit)
{
    if (unit != nullptr) {
        if (strlen(unit) > 63) { // unit长度不能超过63个字符
            luaL_error(L, "unit exceeds maximum length of 63 characters");
        }
        for (size_t i = 0; i < strlen(unit); ++i) {
            char c = unit[i];
            if (static_cast<unsigned char>(c) > 127) {
                luaL_error(L, "unit contains non-ASCII character");
            }
        }
    }
}

static int l_create_counter(lua_State *L)
{
    if (!isInit.load(std::memory_order_acquire)) {
        luaL_error(L, "create Counter failed, provider has not been initialized");
    }
    LuaOtelMeter *lua_meter = (LuaOtelMeter *)luaL_checkudata(L, 1, "Meter");
    const char *name = luaL_checkstring(L, 2);
    validate_name(L, name, "Counter");
    const char *description = luaL_optstring(L, 3, "");
    const char *unit = luaL_optstring(L, 4, "");
    validate_unit(L, unit);

    void *ud = lua_newuserdata(L, sizeof(LuaCounter));
    LuaCounter *lua_counter = new (ud) LuaCounter();
    lua_counter->name = name;
    lua_counter->counter = lua_meter->CreateDoubleCounter(name, description, unit);
    luaL_getmetatable(L, "Counter");
    lua_setmetatable(L, -2);

    return 1;
}

static int l_create_updowncounter(lua_State *L)
{
    if (!isInit.load(std::memory_order_acquire)) {
        luaL_error(L, "create UpDownCounter failed, provider has not been initialized");
    }
    LuaOtelMeter *lua_meter = (LuaOtelMeter *)luaL_checkudata(L, 1, "Meter");
    const char *name = luaL_checkstring(L, 2);
    validate_name(L, name, "UpDownCounter");
    const char *description = luaL_optstring(L, 3, "");
    const char *unit = luaL_optstring(L, 4, "");
    validate_unit(L, unit);

    void *ud = lua_newuserdata(L, sizeof(LuaUpDownCounter));
    LuaUpDownCounter *lua_updowncounter = new (ud) LuaUpDownCounter();
    lua_updowncounter->name = name;
    lua_updowncounter->updowncounter = lua_meter->CreateDoubleUpDownCounter(name, description, unit);
    luaL_getmetatable(L, "UpDownCounter");
    lua_setmetatable(L, -2);

    return 1;
}

static int l_create_histogram(lua_State *L)
{
    if (!isInit.load(std::memory_order_acquire)) {
        luaL_error(L, "create Histogram failed, provider has not been initialized");
    }
    LuaOtelMeter *lua_meter = (LuaOtelMeter *)luaL_checkudata(L, 1, "Meter");
    const char *name = luaL_checkstring(L, 2);
    validate_name(L, name, "Histogram");
    const char *description = luaL_optstring(L, 3, "");
    const char *unit = luaL_optstring(L, 4, "");
    validate_unit(L, unit);

    void *ud = lua_newuserdata(L, sizeof(LuaHistogram));
    LuaHistogram *lua_histogram = new (ud) LuaHistogram();
    lua_histogram->name = name;
    lua_histogram->histogram = lua_meter->CreateDoubleHistogram(name, description, unit);
    luaL_getmetatable(L, "Histogram");
    lua_setmetatable(L, -2);

    return 1;
}

static int l_create_observable_counter(lua_State *L)
{
    if (!isInit.load(std::memory_order_acquire)) {
        luaL_error(L, "create ObservableCounter failed, provider has not been initialized");
    }
    LuaOtelMeter *lua_meter = (LuaOtelMeter *)luaL_checkudata(L, 1, "Meter");
    const char *name = luaL_checkstring(L, 2);
    validate_name(L, name, "ObservableCounter");
    const char *description = luaL_optstring(L, 3, "");
    const char *unit = luaL_optstring(L, 4, "");
    validate_unit(L, unit);

    auto observablecounter = lua_meter->CreateDoubleObservableCounter(name, description, unit);
    void *ud = lua_newuserdata(L, sizeof(LuaObservableCounter));
    LuaObservableCounter *lua_observablecounter = new (ud) LuaObservableCounter();
    lua_observablecounter->name = name;
    lua_observablecounter->observablecounter = observablecounter;
    luaL_getmetatable(L, "ObservableCounter");
    lua_setmetatable(L, -2);

    return 1;
}

static int l_create_observable_updowncounter(lua_State *L)
{
    if (!isInit.load(std::memory_order_acquire)) {
        luaL_error(L, "create ObservableUpDownCounter failed, provider has not been initialized");
    }
    LuaOtelMeter *lua_meter = (LuaOtelMeter *)luaL_checkudata(L, 1, "Meter");
    const char *name = luaL_checkstring(L, 2);
    validate_name(L, name, "ObservableUpDownCounter");
    const char *description = luaL_optstring(L, 3, "");
    const char *unit = luaL_optstring(L, 4, "");
    validate_unit(L, unit);

    auto observableupdowncounter = lua_meter->CreateDoubleObservableUpDownCounter(name, description, unit);
    void *ud = lua_newuserdata(L, sizeof(LuaObservableUpDownCounter));
    LuaObservableUpDownCounter *lua_observableupdowncounter = new (ud) LuaObservableUpDownCounter();
    lua_observableupdowncounter->name = name;
    lua_observableupdowncounter->observableupdowncounter = observableupdowncounter;
    luaL_getmetatable(L, "ObservableUpDownCounter");
    lua_setmetatable(L, -2);

    return 1;
}

static int l_create_observable_gauge(lua_State *L)
{
    if (!isInit.load(std::memory_order_acquire)) {
        luaL_error(L, "create ObservableGauge failed, provider has not been initialized");
    }
    LuaOtelMeter *lua_meter = (LuaOtelMeter *)luaL_checkudata(L, 1, "Meter");
    const char *name = luaL_checkstring(L, 2);
    validate_name(L, name, "ObservableGauge");
    const char *description = luaL_optstring(L, 3, "");
    const char *unit = luaL_optstring(L, 4, "");
    validate_unit(L, unit);

    auto observablegauge = lua_meter->CreateDoubleObservableGauge(name, description, unit);
    void *ud = lua_newuserdata(L, sizeof(LuaObservableGauge));
    LuaObservableGauge *lua_observablegauge = new (ud) LuaObservableGauge();
    lua_observablegauge->name = name;
    lua_observablegauge->observablegauge = observablegauge;
    luaL_getmetatable(L, "ObservableGauge");
    lua_setmetatable(L, -2);

    return 1;
}

static int validate_attribute(lua_State *L, std::string &name, AttributeMap &attributes)
{
    auto res = metrics.find(name);
    if (res == metrics.end()) {
        luaL_error(L, "name %s is not defined", name.c_str());
    }

    std::map<std::string, Utils::Attribute> &attrs = res->second.attributes;
    // 检查是否存在非法属性
    for (const auto& pair : attributes) {
        const std::string &key = pair.first;
        auto attr_res = attrs.find(key);
        if (attr_res == attrs.end()) {
            luaL_error(L, "attribute %s is not defined", key.c_str());
        }
    }
    // 检查必选属性是否包含
    for (const auto &[key, value] : attrs) {
        if (value.level != "Required") {
            continue;
        }
        auto attr_res = attributes.find(key);
        if (attr_res == attributes.end()) {
            luaL_error(L, "attribute %s is required", key.c_str());
        }
    }
    return 0;
}

template <typename T>
static int l_add_impl(lua_State *L, const char *type_name, bool check_non_negative)
{
    T *obj = (T *)luaL_checkudata(L, 1, type_name);
    enabledMetricsMutex.lock();
    // 没有开启则不添加数据
    if (!enabledMetrics.count(obj->name)) {
        enabledMetricsMutex.unlock();
        return 0;
    }
    enabledMetricsMutex.unlock();
    double value = luaL_checknumber(L, 2);
    if (check_non_negative && value < 0) {
        luaL_error(L, "value must be non-negative");
    }
    AttributeMap attributes;
    if (lua_istable(L, 3)) {
        Utils::get_attributes(L, attributes, 3);
    }
    validate_attribute(L, obj->name, attributes);
    if constexpr (std::is_same_v<T, LuaCounter>) {
        obj->counter->Add(value, attributes);
    } else {
        obj->updowncounter->Add(value, attributes);
    }
    return 0;
}

static int l_counter_add(lua_State *L)
{
    return l_add_impl<LuaCounter>(L, "Counter", true);
}

static int l_updowncounter_add(lua_State *L)
{
    return l_add_impl<LuaUpDownCounter>(L, "UpDownCounter", false);
}

static int l_histogram_record(lua_State *L)
{
    LuaHistogram *obj = (LuaHistogram *)luaL_checkudata(L, 1, "Histogram");
    enabledMetricsMutex.lock();
    // 没有开启则不添加数据
    if (!enabledMetrics.count(obj->name)) {
        enabledMetricsMutex.unlock();
        return 0;
    }
    enabledMetricsMutex.unlock();
    double value = luaL_checknumber(L, 2);
    if (value < 0) {
        luaL_error(L, "value must be non-negative");
    }
    AttributeMap attributes;
    if (lua_istable(L, 3)) {
        Utils::get_attributes(L, attributes, 3);
    }
    validate_attribute(L, obj->name, attributes);
    obj->histogram->Record(value, attributes);
    return 0;
}

static int l_add_observable_callback(lua_State *L, void *observable_instrument)
{
    luaL_checktype(L, 2, LUA_TFUNCTION);

    lua_pushvalue(L, 2);
    int ref = luaL_ref(L, LUA_REGISTRYINDEX);

    auto callback = std::make_unique<LuaCallback>(LuaCallback{L, ref});

    auto callback_func = [](metrics_api::ObserverResult observer, void *state) {
        LuaCallback *cb = static_cast<LuaCallback *>(state);
        if (!cb) {
            return;
        }

        if (absl::holds_alternative<opentelemetry::nostd::shared_ptr<metrics_api::ObserverResultT<double>>>(observer)) {
            auto result_ptr =
                absl::get<opentelemetry::nostd::shared_ptr<metrics_api::ObserverResultT<double>>>(observer);
            auto &result = *result_ptr;
            lua_State *L = cb->L;
            int ref = cb->lua_callback_ref;
            lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
            if (lua_pcall(L, 0, 1, 0) != LUA_OK) {
                printf("Lua error: %s\n", lua_tostring(L, -1));
                lua_pop(L, 1);
                return;
            }

            if (lua_isnumber(L, -1)) {
                result.Observe(lua_tonumber(L, -1));
            }
            lua_pop(L, 1);
        }
    };

    static_cast<metrics_api::ObservableInstrument *>(observable_instrument)
        ->AddCallback(callback_func, callback.release());

    return 0;
}

static int l_add_observable_counter_callback(lua_State *L)
{
    LuaObservableCounter *observablecounter = (LuaObservableCounter *)luaL_checkudata(L, 1, "ObservableCounter");
    return l_add_observable_callback(L, observablecounter->observablecounter.get());
}

static int l_add_observable_updowncounter_callback(lua_State *L)
{
    LuaObservableUpDownCounter *observableupdowncounter =
        (LuaObservableUpDownCounter *)luaL_checkudata(L, 1, "ObservableUpDownCounter");
    return l_add_observable_callback(L, observableupdowncounter->observableupdowncounter.get());
}

static int l_add_observable_gauge_callback(lua_State *L)
{
    LuaObservableGauge *observablegauge = (LuaObservableGauge *)luaL_checkudata(L, 1, "ObservableGauge");
    return l_add_observable_callback(L, observablegauge->observablegauge.get());
}

static int l_meter_gc(lua_State *L)
{
    LuaOtelMeter *lua_meter = (LuaOtelMeter *)luaL_checkudata(L, 1, "Meter");
    lua_meter->~LuaOtelMeter();
    return 0;
}

static int l_counter_gc(lua_State *L)
{
    LuaCounter *lua_counter = (LuaCounter *)luaL_checkudata(L, 1, "Counter");
    lua_counter->~LuaCounter();
    return 0;
}

static int l_updowncounter_gc(lua_State *L)
{
    LuaUpDownCounter *lua_updowncounter = (LuaUpDownCounter *)luaL_checkudata(L, 1, "UpDownCounter");
    lua_updowncounter->~LuaUpDownCounter();
    return 0;
}

static int l_histogram_gc(lua_State *L)
{
    LuaHistogram *lua_histogram = (LuaHistogram *)luaL_checkudata(L, 1, "Histogram");
    lua_histogram->~LuaHistogram();
    return 0;
}

static int l_observable_counter_gc(lua_State *L)
{
    LuaObservableCounter *lua_observable_counter = (LuaObservableCounter *)luaL_checkudata(L, 1, "ObservableCounter");
    lua_observable_counter->~LuaObservableCounter();
    return 0;
}

static int l_observable_updowncounter_gc(lua_State *L)
{
    LuaObservableUpDownCounter *lua_observable_updowncounter =
        (LuaObservableUpDownCounter *)luaL_checkudata(L, 1, "ObservableUpDownCounter");
    lua_observable_updowncounter->~LuaObservableUpDownCounter();
    return 0;
}

static int l_observable_gauge_gc(lua_State *L)
{
    LuaObservableGauge *lua_observable_gauge = (LuaObservableGauge *)luaL_checkudata(L, 1, "ObservableGauge");
    lua_observable_gauge->~LuaObservableGauge();
    return 0;
}

static int l_flush(lua_State *L)
{
    LuaOtelMeter *lua_meter = (LuaOtelMeter *)luaL_checkudata(L, 1, "Meter");
    if (lua_gettop(L) < 2) {
        lua_meter->ForceFlush();
        return 0;
    }
    int timeout = luaL_checkinteger(L, 2);
    lua_meter->ForceFlush(std::chrono::microseconds(timeout));
    return 0;
}

static const luaL_Reg lmetry[] =
{
    {"get_meter", l_get_meter},
    {"init_meter_provider", l_init_meter_provider},
    {"deinit_meter_provider", l_deinit_meter_provider},
    {"set_enabled_metrics", l_set_enabled_metrics},
    {NULL, NULL}
};

static const luaL_Reg lmeter[] =
{
    {"create_counter", l_create_counter},
    {"create_updowncounter", l_create_updowncounter},
    {"create_observable_counter", l_create_observable_counter},
    {"create_observable_updowncounter", l_create_observable_updowncounter},
    {"create_observable_gauge", l_create_observable_gauge},
    {"create_histogram", l_create_histogram},
    {"flush", l_flush},
    {NULL, NULL}
};

static const luaL_Reg counter_methods[] =
{
    {"add", l_counter_add},
    {NULL, NULL}
};

static const luaL_Reg updowncounter_methods[] =
{
    {"add", l_updowncounter_add},
    {NULL, NULL}
};

static const luaL_Reg histogram_methods[] =
{
    {"record", l_histogram_record},
    {NULL, NULL}
};

static const luaL_Reg observable_counter_methods[] =
{
    {"add_callback", l_add_observable_counter_callback},
    {NULL, NULL}
};

static const luaL_Reg observable_updowncounter_methods[] =
{
    {"add_callback", l_add_observable_updowncounter_callback},
    {NULL, NULL}
};

static const luaL_Reg observable_gauge_methods[] =
{
    {"add_callback", l_add_observable_gauge_callback},
    {NULL, NULL}
};

static void set_index_metatable(lua_State *L)
{
    luaL_newmetatable(L, "Meter");
    luaL_setfuncs(L, lmeter, 0);
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");
    lua_pushstring(L, "__gc");
    lua_pushcfunction(L, l_meter_gc);
    lua_settable(L, -3);

    luaL_newmetatable(L, "Counter");
    luaL_setfuncs(L, counter_methods, 0);
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");
    lua_pushstring(L, "__gc");
    lua_pushcfunction(L, l_counter_gc);
    lua_settable(L, -3);

    luaL_newmetatable(L, "UpDownCounter");
    luaL_setfuncs(L, updowncounter_methods, 0);
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");
    lua_pushstring(L, "__gc");
    lua_pushcfunction(L, l_updowncounter_gc);
    lua_settable(L, -3);

    luaL_newmetatable(L, "Histogram");
    luaL_setfuncs(L, histogram_methods, 0);
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");
    lua_pushstring(L, "__gc");
    lua_pushcfunction(L, l_histogram_gc);
    lua_settable(L, -3);

    luaL_newmetatable(L, "ObservableCounter");
    luaL_setfuncs(L, observable_counter_methods, 0);
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");
    lua_pushstring(L, "__gc");
    lua_pushcfunction(L, l_observable_counter_gc);
    lua_settable(L, -3);

    luaL_newmetatable(L, "ObservableUpDownCounter");
    luaL_setfuncs(L, observable_updowncounter_methods, 0);
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");
    lua_pushstring(L, "__gc");
    lua_pushcfunction(L, l_observable_updowncounter_gc);
    lua_settable(L, -3);

    luaL_newmetatable(L, "ObservableGauge");
    luaL_setfuncs(L, observable_gauge_methods, 0);
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");
    lua_pushstring(L, "__gc");
    lua_pushcfunction(L, l_observable_gauge_gc);
    lua_settable(L, -3);
}

extern "C" int luaopen_otel_metrics(lua_State *L)
{
    luaL_checkversion(L);
    set_index_metatable(L);
    luaL_newlibtable(L, lmetry);
    luaL_setfuncs(L, lmetry, 0);
    return 1;
}
} // namespace Otel