-- Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
-- 
-- this file licensed under the 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.

local skynet = require 'skynet'
local class = require 'mc.class'
local Singleton = require 'mc.singleton'
local ipmi = require 'ipmi'
local log = require 'mc.logging'
local initiator = require 'mc.initiator'
local privilege = require 'mc.privilege'
local enums = require 'basic_cooling.define.cooling_enums'
local util_file = require 'utils.file'
local utils = require 'mc.utils'

local cooling_utils = class()
local thermal_log = nil

function cooling_utils:ctor()
    self.last_time_map = {}
end

function cooling_utils.ipmi_op(ctx, fmt, ...)
    ipmi.ipmi_operation_log(ctx, 'thermal_mgmt', fmt, ...)
end

function cooling_utils.op(ctx, fmt, ...)
    local intf = ctx.Interface and ctx.Interface or 'N/A'
    local user_name = ctx.UserName and ctx.UserName or 'N/A'
    local client_addr = ctx.ClientAddr and ctx.ClientAddr or 'N/A'

    log:operation(initiator.new(intf, user_name, client_addr), 'thermal_mgmt', fmt, ...)
end

-- 限频日志
---@param level number params: 1,error 2,info 3,debug
---@param interval_s number params: interval seconds
function cooling_utils:frequency_limit_log(level, interval_s, fmt, ...)
    local last_time_key = debug.getinfo(2, 'S').short_src .. ':' .. debug.getinfo(2, "l").currentline
    local cur_time = os.time()
    local log_callback = {
        [enums.log_level.ERROR] = log.error,
        [enums.log_level.INFO] = log.info,
        [enums.log_level.DEBUG] = log.debug
    }
    local last_time = self.last_time_map[last_time_key]
    if not last_time or cur_time - last_time >= interval_s then
        self.last_time_map[last_time_key] =  cur_time
        log_callback[level](log, fmt, ...)
    end
end

function cooling_utils.size_of(t)
    local cnt = 0
    for _, _ in pairs(t) do
        cnt = cnt + 1
    end
    return cnt
end

-- 数组升序判断函数
function cooling_utils.is_array_ascending(arr)
    for i = 2, #arr do
        if arr[i] <= arr[i - 1] then
            return false
        end
    end
    return true
end
-- 数组非降序判断函数
function cooling_utils.is_array_not_descending(arr)
    for i = 2, #arr do
        if arr[i] < arr[i - 1] then
            return false
        end
    end
    return true
end

function cooling_utils.verify_privilege_for_rpc(prop, expect_priv)
    privilege.validate_set_prop(prop, expect_priv)
end

-- input字符串按照delimiter进行分隔, 返回table
function cooling_utils.split(input, delimiter)
    if type(input) ~= 'string' or #input <= 0 then
        return nil
    end
    if type(delimiter) ~= 'string' or #delimiter <= 0 then
        return nil
    end
    local res = {}
    -- 使用 Lua 的 string.gmatch 函数进行字符串的拆分
    for match in string.gmatch(input, "([^" .. delimiter .. "]+)") do
        table.insert(res, match)
    end
    return res
end

function cooling_utils.parse_table_info(t)
    local str = ""
    for k, v in pairs(t) do
        str = str .. k .. ":" .. v .. " "
    end
    return str
end

-- 对嵌套的table数据进行扁平操作，即将{ {1, 2, 3}, {4, 5, 6} }数据变为{1, 2, 3, 4, 5, 6}
function cooling_utils.flatten_table(data)
    local res = {}
    for _, v in pairs(data) do
        if type(v) == 'table' then
            local flattened = cooling_utils.flatten_table(v)
            for _, inner_v in pairs(flattened) do
                table.insert(res, inner_v)
            end
        else
            table.insert(res, v)
        end
    end
    return res
end

function cooling_utils.average(t)
    local sum = 0
    local count = 0
    for _, value in pairs(t) do
        sum = sum + value
        count = count + 1
    end
    return sum / count
end

function cooling_utils.get_array_from_rpc_u8_array(arrays)
    local res = {}
    for i = 1, #arrays do
        res[i] = string.byte(arrays, i)
    end
    return res
end

function cooling_utils.get_max_value(t)
    local max = -math.huge
    for _, value in pairs(t) do
        if type(value) == 'number' and value > max then
            max = value
        end
    end
    return max
end

function cooling_utils.write_out_file(path, error_msg, contents)
    local f = util_file.open_s(path, 'a')
    if not f then
        log:error('Open failed, file:[%s]', error_msg)
        return
    end
    utils.safe_close_file(f, function()
        f:write('\n')
        f:write(table.concat(contents, '\n'))
    end)
    utils.chmod(path, utils.S_IRUSR | utils.S_IWUSR | utils.S_IRGRP)
end

function cooling_utils.add_dump_task(path, error_msg)
    if thermal_log then
        return
    end
    thermal_log = {}
    skynet.fork_once(function()
        -- 每5秒写入一次
        skynet.sleep(500)
        cooling_utils.write_out_file(path, error_msg, thermal_log)
        for index, _ in ipairs(thermal_log) do
            thermal_log[index] = nil
        end
        thermal_log = nil
    end)
end

function cooling_utils.thermal_log(fmt, ...)
    cooling_utils.add_dump_task('/var/log/Thermal.log', 'Thermal.log')
    thermal_log[#thermal_log + 1] = string.format('[%s] %s', os.date('%Y-%m-%d %H:%M:%S'), string.format(fmt, ...))
end

return Singleton(cooling_utils)