local lu = require 'luaunit'
local metrics = require 'otel.metrics'
local utils = require 'mc.utils'
local paser = require 'parser.metric'
local dump_file = "./metrics.txt"

TestMetric = {}

local function read_file(filename)
    local file = io.open(filename, "r")
    lu.assertNotIsNil(file)
    local data = file:read("*all")
    file:close()
    return data
end

function TestMetric:setupClass()

end

function TestMetric:teardownClass()
    metrics.deinit_meter_provider()
    utils.remove_file(dump_file)
end

function TestMetric:test_metric()
    metrics.init_meter_provider(100, 50)
    local meter = metrics.get_meter("test_meter")
    metrics.set_enabled_metrics({"test_counter", "test_updowncounter", "test_histogram", "test_observable_counter",
        "test_observable_updowncounter", "test_observable_gauge", "test_gauge"});
    local counter = meter:create_counter("test_counter")
    counter:add(1)

    local updowncounter = meter:create_updowncounter("test_updowncounter", "a test updowncounter")
    updowncounter:add(-1)

    local histogram = meter:create_histogram("test_histogram", "a test histogram")
    histogram:record(2, {containter = 'histogram'})

    local observable_counter =
        meter:create_observable_counter("test_observable_counter", "a test observable counter")
    os.execute("sleep 0.2")
    observable_counter:add_callback(function() return 10 end)

    local observable_updowncounter =
        meter:create_observable_updowncounter("test_observable_updowncounter", "a test observable updowncounter")
    os.execute("sleep 0.2")
    observable_updowncounter:add_callback(function() return 20 end)

    local observable_gauge = meter:create_observable_gauge("test_observable_gauge", "a test observable gauge")
    os.execute("sleep 0.2")
    observable_gauge:add_callback(function() return 30 end)
    os.execute("sleep 0.2")

    local gauge = meter:create_gauge("test_gauge", "a test gauge")
    gauge:record(1.1, {prop1 = "value1", prop2 = 2.2})
    gauge:record(1.2, {prop1 = "value1", prop2 = 2.2})
    gauge:record(1.2, {prop1 = "value1", prop2 = 2.3})
    gauge:record(1.3, {prop1 = "value1", prop2 = 2.3})
    os.execute("sleep 0.2")

    local metrics = paser:parse_metric_file(dump_file)
    for _, metric in ipairs(metrics) do
        lu.assertEquals(metric.scope["scope name"], "test_meter")
        lu.assertEquals(metric.scope["version"], "")
        lu.assertEquals(metric.scope["schema url"], "")
        for _, instrument in ipairs(metric.instruments) do
            if instrument['instrument name'] == "test_counter" then
                lu.assertEquals(instrument.value, "1")
            elseif instrument['instrument name'] == "test_updowncounter" then
                lu.assertEquals(instrument.value, "-1")
                lu.assertEquals(instrument.description, "a test updowncounter")
            elseif instrument['instrument name'] == "test_histogram" then
                lu.assertEquals(instrument.sum, "2")
                lu.assertEquals(instrument.description, "a test histogram")
            elseif instrument['instrument name'] == "test_observable_counter" then
                lu.assertEquals(instrument.value, "10")
                lu.assertEquals(instrument.description, "a test observable counter")
            elseif instrument['instrument name'] == "test_observable_updowncounter" then
                lu.assertEquals(instrument.value, "20")
                lu.assertEquals(instrument.description, "a test observable updowncounter")
            elseif instrument['instrument name'] == "test_observable_gauge" then
                lu.assertEquals(instrument.value, "30")
                lu.assertEquals(instrument.description, "a test observable gauge")
            end
        end
    end
end

function TestMetric:test_metric_error()
    metrics.init_meter_provider(100, 50)
    local meter = metrics.get_meter("test_meter_error")
    lu.assertErrorMsgContains("name can not be empty", function()
        meter:create_counter("")
    end)
    lu.assertErrorMsgContains("name must start with an alphabetic character", function()
        meter:create_counter("1_aa")
    end)
    lu.assertErrorMsgContains("name contains invalid character", function()
        meter:create_counter("bc@de")
    end)
    metrics.set_enabled_metrics({"test_counter_error"})
    local counter = meter:create_counter("test_counter_error")
    lu.assertErrorMsgContains("value must be non-negative", function()
        counter:add(-1)
    end)
end

function TestMetric:test_metric_constraint()
    metrics.init_meter_provider(100, 50)
    local meter = metrics.get_meter("test_meter")
    -- 仪器未定义
    lu.assertErrorMsgContains("intrument name test_histogram_no is not defined", function()
        meter:create_histogram("test_histogram_no", "a test histogram")
    end)
    -- 仪器类型不符
    lu.assertErrorMsgContains("intrument name test_histogram type mismatch", function()
        meter:create_counter("test_histogram", "a test histogram")
    end)
    metrics.set_enabled_metrics({"test_histogram"})
    -- 缺少必选属性
    local histogram = meter:create_histogram("test_histogram", "a test histogram")
    lu.assertErrorMsgContains("attribute containter is required", function()
        histogram:record(2)
    end)
    -- 属性未定义
    lu.assertErrorMsgContains("attribute containter3 is not defined", function()
        histogram:record(2, {containter3 = 'histogram'})
    end)
    -- 可选属性可传入或者不传入
    histogram:record(2, {containter = 'histogram'})
    histogram:record(2, {containter = 'histogram', containter1 = 'histogram'})
    histogram:record(2, {containter = 'histogram', containter1 = 'histogram', containter2 = 'histogram'})
end

function TestMetric:test_lock()
    metrics.deinit_meter_provider()
    local meter
    lu.assertErrorMsgContains("get meter failed, provider has not been initialized", function()
        meter = metrics.get_meter("test_meter")
    end)
    metrics.init_meter_provider(100, 50)
    meter = metrics.get_meter("test_meter")
    lu.assertNotIsNil(meter)
    meter:flush()
end
