-- 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.

local singleton = require('mc.singleton')
local class = require('mc.class')
local common_def = require('common_def')
local initiator = require('mc.initiator')
local context = require('mc.context')
local client = require('storage.client')
local skynet = require('skynet')
local object_manage = require('mc.orm.object_manage')
local log = require('mc.logging')
local mdb = require('mc.mdb')

local method_misc = class()

local NOT_SUPPORT_OOB_CTRL_LIST = {
    [10] = true,
    [19] = true,
    [20] = true,
}

function method_misc:remove_ele_by_position(list, position)
    -- 使用反向遍历避免删除元素时索引变化导致的问题
    for i = #list, 1, -1 do
        if list[i] and list[i].position == position then
            table.remove(list, i)
            break
        end
    end
end

-- 如果val不合法返回默认值
function method_misc:update_if_valid(invalid, val, default)
    if val and val ~= invalid then
        return val
    end
    return default
end

-- 根据RAID卡TypeId判断是否支持OOBSUPPORT
function method_misc:test_controller_oob_support(type_id)
    if not NOT_SUPPORT_OOB_CTRL_LIST[type_id] then
        return true
    end
    return false
end

-- 检查RAID卡的厂商类型
local function test_controller_vendor(type_id, vendor_id)
    if vendor_id == common_def.VENDER_LSI then
        if type_id < common_def.PMC_3152_8I_SMART_RAID then
            return true
        end
    elseif vendor_id == common_def.VENDER_PMC then
        if type_id >= common_def.PMC_3152_8I_SMART_RAID and type_id <= common_def.PMC_8222_8I_SMART_RAID then
            return true
        end
    elseif vendor_id == common_def.VENDER_HUAWEI then
        if type_id >= common_def.HI1880_SP186_M_16i and type_id <= common_def.HI1880_SP186_M_8i then
            return true
        end
    elseif vendor_id == common_def.VENDER_HRDT then
        if type_id == common_def.HRDT_B261s then
            return true
        end
    end
    return false
end

-- 根据RAID卡类型判断缓存策略是否正确
function method_misc:check_io_policy(io_policy, type_id)
    -- vendor_lsi 只有博通卡支持
    if test_controller_vendor(type_id, common_def.VENDER_LSI) then
        if io_policy > common_def.LD_CACHE_DIRECT_IO then
            return common_def.SM_CODE_PARA_DATA_ILLEGAL
        end
    elseif
        test_controller_vendor(type_id, common_def.VENDER_PMC)
        or test_controller_vendor(type_id, common_def.VENDER_HUAWEI)
        or test_controller_vendor(type_id, common_def.VENDER_HRDT)
    then
        return common_def.SM_CODE_PARA_DATA_ILLEGAL
    end
    return common_def.SM_CODE_SUCCESS
end

-- 根据RAID卡类型判断厂商类型
function method_misc:test_controller_vendor(type_id, vendor_id)
    return test_controller_vendor(type_id, vendor_id)
end

-- true表示支持升级,false表示不支持升级,当前仅自研raid卡支持升级
function method_misc:check_is_suport_upgrade(type_id)
    return type_id >= common_def.HI1880_SP686C_M_16i_2G and type_id <= common_def.HI1880_SP686C_M_40i_4G
end

-- 根据RAID卡类型判断启动类型是否正确
function method_misc:check_boot_priority_param(boot_priority, type_id)
    -- vendor_lsi
    if test_controller_vendor(type_id, common_def.VENDER_LSI) then
        if boot_priority ~= common_def.BOOT_PRIORITY_PRIMARY then
            return common_def.SM_CODE_PARA_DATA_ILLEGAL
        end
    -- vendor_pmc
    elseif test_controller_vendor(type_id, common_def.VENDER_PMC) then
        if boot_priority > common_def.BOOT_PRIORITY_ALL then
            return common_def.SM_CODE_PARA_DATA_ILLEGAL
        end
    -- vendor_huawei
    elseif test_controller_vendor(type_id, common_def.VENDER_HUAWEI) then
        if boot_priority > common_def.BOOT_PRIORITY_PRIMARY then
            return common_def.SM_CODE_PARA_DATA_ILLEGAL
        end
    -- vendor_hrdt
    elseif test_controller_vendor(type_id, common_def.VENDER_HRDT) then
        if boot_priority > common_def.BOOT_PRIORITY_ALL then
            return common_def.SM_CODE_PARA_DATA_ILLEGAL
        end
    end
    return common_def.SM_CODE_SUCCESS
end

-- 根据RAID卡类型判断多启动盘数量是否支持新增
function method_misc:check_boot_devices_count(boot_priority, controller_obj)
    -- vendor_huawei
    if not test_controller_vendor(controller_obj.TypeId, common_def.VENDER_HUAWEI) then
        return common_def.SM_CODE_SUCCESS
    end

    if boot_priority ~= common_def.BOOT_PRIORITY_PRIMARY then
        return common_def.SM_CODE_SUCCESS
    end

    if controller_obj.SupportedBootDeviceCount <= 1 then
        return common_def.SM_CODE_SUCCESS
    end

    local count = 0
    for _, name in ipairs(controller_obj.BootDevices) do
        if name and name ~= 'None' then
            count = count + 1
        end
    end
    if count == controller_obj.SupportedBootDeviceCount then
        return common_def.SM_CODE_PARA_DATA_ILLEGAL
    end

    return common_def.SM_CODE_SUCCESS
end

-- 根据RAID卡类型判断读策略是否正确
function method_misc:check_read_policy(read_policy, type_id)
    -- vendor_lsi
    if test_controller_vendor(type_id, common_def.VENDER_LSI) then
        if read_policy > common_def.READ_POLICY_READ_AHEAD_ADAPTIVE then
            return common_def.SM_CODE_PARA_DATA_ILLEGAL
        end
    -- vendor_pmc
    elseif test_controller_vendor(type_id, common_def.VENDER_PMC) then
        return common_def.SM_CODE_PARA_DATA_ILLEGAL
    -- vendor_huawei
    elseif test_controller_vendor(type_id, common_def.VENDER_HUAWEI) then
        if read_policy > common_def.LD_CACHE_READ_AHEAD then
            return common_def.SM_CODE_PARA_DATA_ILLEGAL
        end
    -- vendor_hrdt
    elseif test_controller_vendor(type_id, common_def.VENDER_HRDT) then
        return common_def.SM_CODE_PARA_DATA_ILLEGAL
    end
    return common_def.SM_CODE_SUCCESS
end

-- 根据RAID卡类型判断BGI是否正确
function method_misc:check_BGI(BGI_enable, type_id)
    -- vendor_lsi 只有博通卡支持
    if test_controller_vendor(type_id, common_def.VENDER_LSI) then
        if BGI_enable > common_def.LD_CACHE_DIRECT_IO then
            return common_def.SM_CODE_PARA_DATA_ILLEGAL
        end
    elseif
        test_controller_vendor(type_id, common_def.VENDER_PMC)
        or test_controller_vendor(type_id, common_def.VENDER_HUAWEI)
        or test_controller_vendor(type_id, common_def.VENDER_HRDT)
    then
        return common_def.SM_CODE_PARA_DATA_ILLEGAL
    end
    return common_def.SM_CODE_SUCCESS
end

-- 根据RAID卡类型获取设置启动设备的操作日志描述
function method_misc:get_boot_priority_op_desc(boot_priority, type_id)
    -- vendor_lsi
    if test_controller_vendor(type_id, common_def.VENDER_LSI) then
        return common_def.AS_BOOT_DEVICE
    end
    -- vendor_pmc
    if test_controller_vendor(type_id, common_def.VENDER_PMC) then
        return 'boot priority to ' .. (common_def.PD_BOOT_PRIORITY[boot_priority] or common_def.INVALID_STRING)
    end
    -- vendor_huawei
    if test_controller_vendor(type_id, common_def.VENDER_HUAWEI) then
        return common_def.PD_BOOTABLE_DESC[boot_priority] or common_def.INVALID_STRING
    end
    -- vendor_hrdt
    if test_controller_vendor(type_id, common_def.VENDER_HRDT) then
        return 'boot priority to ' .. (common_def.PD_BOOT_PRIORITY[boot_priority] or common_def.INVALID_STRING)
    end
    return common_def.INVALID_STRING
end

-- 根据RAID卡类型判断访问策略是否正确
function method_misc:check_access_policy(access_policy, type_id)
    -- vendor_lsi 只有博通卡支持
    if test_controller_vendor(type_id, common_def.VENDER_LSI) then
        if access_policy > common_def.LD_ACCESS_BLOCKED then
            return common_def.SM_CODE_PARA_DATA_ILLEGAL
        end
    elseif
        test_controller_vendor(type_id, common_def.VENDER_PMC)
        or test_controller_vendor(type_id, common_def.VENDER_HUAWEI)
        or test_controller_vendor(type_id, common_def.VENDER_HRDT)
    then
        return common_def.SM_CODE_PARA_DATA_ILLEGAL
    end
    return common_def.SM_CODE_SUCCESS
end

-- 检查逻辑盘名称
function method_misc:check_volume_name(name, type_id)
    if self:check_ld_name_too_long(#name, type_id) then
        return common_def.SM_CODE_LD_NAME_EXCEED_MAX_LEN
    end
    if not self:check_string_is_valid_ascii(name) then
        return common_def.SM_CODE_LD_NAME_INVALID_ASCII
    end
    return common_def.SM_CODE_SUCCESS
end

function method_misc:get_ld_name_max_length(type_id)
    -- 博通卡逻辑盘最大长度15
    if test_controller_vendor(type_id, common_def.VENDER_LSI) then
        return 15
    -- PMC卡逻辑盘最大长度64
    elseif test_controller_vendor(type_id, common_def.VENDER_PMC) then
        return 64
    -- 自研卡逻辑盘最大长度20
    elseif test_controller_vendor(type_id, common_def.VENDER_HUAWEI) then
        return 20
    -- HRDT卡逻辑盘最大长度20
    elseif test_controller_vendor(type_id, common_def.VENDER_HRDT) then
        return 20
    end
    return 0
end

function method_misc:check_ld_name_too_long(len, type_id)
    -- 博通卡逻辑盘最大长度15
    if test_controller_vendor(type_id, common_def.VENDER_LSI) and len > common_def.SL_LD_NAME_LENGTH then
        return true
    end
    -- PMC卡逻辑盘最大长度64
    if test_controller_vendor(type_id, common_def.VENDER_PMC) and len > common_def.SC_LD_NAME_LENGTH then
        return true
    end
    -- 自研卡逻辑盘最大长度20
    if test_controller_vendor(type_id, common_def.VENDER_HUAWEI) and len > common_def.HS_LD_NAME_LENGTH then
        return true
    end
    -- HRDT卡逻辑盘最大长度20
    if test_controller_vendor(type_id, common_def.VENDER_HRDT) and len > common_def.HRDT_LD_NAME_LENGTH then
        return true
    end
    return false
end

function method_misc:check_string_is_valid_ascii(name)
    for i = 1, #name do
        local ch = name:byte(i, i)
        if ch > 0x7e or ch < 0x20 then
            return false
        end
    end
    return true
end

-- 根据RAID卡类型判断带外管理通道类型
function method_misc:get_oob_interface_type(type_id)
    if
        test_controller_vendor(type_id, common_def.VENDER_HUAWEI)
        or test_controller_vendor(type_id, common_def.VENDER_PMC)
    then
        return common_def.OOB_TYPE_OVER_PCIE
    elseif
        test_controller_vendor(type_id, common_def.VENDER_LSI)
        or test_controller_vendor(type_id, common_def.VENDER_HRDT)
    then
        return common_def.OOB_TYPE_OVER_I2C
    else
        return common_def.INVALID_U8
    end
end

function method_misc:get_op_initiator()
    local m_initiator = initiator.new('N/A', 'N/A', 'localhost')
    local ctx = context.get_context()
    return (ctx and ctx:has_initiator()) and ctx:get_initiator() or m_initiator
end

-- 根据 ManufacturerOUI 查找 ManufacturerName
function method_misc:get_manufacturer_name_by_oui(manufacturer_oui)
    local vendor_id = tonumber(manufacturer_oui, 16)
    if vendor_id and common_def.VENDOR_ID_2_VENDOR_NAME[vendor_id] then
        return common_def.VENDOR_ID_2_VENDOR_NAME[vendor_id]
    end
    return 'ATA'
end

-- 重试注册FirmwareInfo到FirmwareInventory
-- @param firmware_info_table table FirmwareInfo的参数表
-- @param updateable boolean 是否可升级
-- @param capability integer 固件支持的能力,每个bit代表一种能力
-- @param max_package_size_mib integer 升级固件包的最大大小,单位为MB
-- @param success_callback function 注册成功后的回调函数,可选参数
function method_misc:retry_register_firmware_info(
    firmware_info_table,
    updateable,
    capability,
    max_package_size_mib,
    success_callback
)
    skynet.fork(function()
        local retries = 0
        local max_retries = 3 -- 重试3次
        local retry_interval = 5000 -- 每次间隔5秒(5000ms)
        local ok, rsp

        -- 确保参数表包含必要的字段
        local param = {
            Id = firmware_info_table.Id or '',
            Name = firmware_info_table.Name or '',
            Version = firmware_info_table.Version or '',
            BuildNum = firmware_info_table.BuildNum or '',
            ReleaseDate = firmware_info_table.ReleaseDate or '',
            LowestSupportedVersion = firmware_info_table.LowestSupportedVersion or '',
            SoftwareId = firmware_info_table.SoftwareId or '',
            Manufacturer = firmware_info_table.Manufacturer or '',
            Location = firmware_info_table.Location or '',
            State = firmware_info_table.State or 'Enabled',
            Severity = firmware_info_table.Severity or 'Informational',
            EstimatedDuration = firmware_info_table.EstimatedDuration or '',
            ComponentID = firmware_info_table.ComponentID or '255',
            SubComponentID = firmware_info_table.SubComponentID or '4294967295',
            FirmwareType = firmware_info_table.FirmwareType or '',
            TargetUIDs = firmware_info_table.TargetUIDs or '',
            ActiveCondition = firmware_info_table.ActiveCondition or '',
            RelatedItem = firmware_info_table.RelatedItem or '',
            StageSupported = firmware_info_table.StageSupported or 'false',
            Model = firmware_info_table.Model or '',
        }
        while retries < max_retries do
            ok, rsp = client:PFirmwareInventoryFirmwareInventoryAdd(
                context.new(),
                param,
                updateable,
                capability,
                max_package_size_mib
            )
            if ok then
                log:notice('[MethodMisc] register firmware info success, Id=%s, Version=%s', param.Id, param.Version)
                -- 注册成功后调用回调函数
                if success_callback then
                    success_callback()
                end
                return
            end

            retries = retries + 1
            if retries < max_retries then
                log:info('[MethodMisc] register firmware info failed, retry %s/%s, err:%s', retries, max_retries, rsp)
                skynet.sleep(retry_interval / 10) -- skynet.sleep的单位是100ms,所以5000ms需要除以10
            else
                log:error(
                    '[MethodMisc] register firmware info failed after %s retries, Id=%s, err:%s',
                    max_retries,
                    param.Id,
                    rsp
                )
                return
            end
        end
    end)
end

-- 重试取消注册FirmwareInfo从FirmwareInventory
-- @param firmware_id string FirmwareInfo的Id
-- @param success_callback function 取消注册成功后的回调函数,可选参数
function method_misc:retry_unregister_firmware_info(firmware_id, success_callback)
    skynet.fork(function()
        local retries = 0
        local max_retries = 3 -- 重试3次
        local retry_interval = 5000 -- 每次间隔5秒(5000ms)
        local ok, rsp

        while retries < max_retries do
            ok, rsp = client:PFirmwareInventoryFirmwareInventoryDelete(context.new(), firmware_id)
            if ok then
                log:notice('[MethodMisc] unregister firmware info success, Id=%s', firmware_id)
                -- 取消注册成功后调用回调函数
                if success_callback then
                    success_callback()
                end
                return
            end

            retries = retries + 1
            if retries < max_retries then
                log:info('[MethodMisc] unregister firmware info failed, retry %s/%s, err:%s', retries, max_retries, rsp)
                skynet.sleep(retry_interval / 10) -- skynet.sleep的单位是100ms,所以5000ms需要除以10
            else
                log:error(
                    '[MethodMisc] unregister firmware info failed after %s retries, Id=%s, err:%s',
                    max_retries,
                    firmware_id,
                    rsp
                )
                return
            end
        end
    end)
end

function method_misc:update_firmware_info(firmware_obj, revision)
    skynet.fork_once(function()
        local retries = 0
        local max_retries = 3 -- 重试3次
        local retry_interval = 5000 -- 每次间隔5秒(5000ms)
        local ok, obj

        while retries < max_retries do
            ok, obj = pcall(
                mdb.get_cached_object,
                object_manage.get_instance().bus,
                common_def.FWINVENTORY_PATH_PREFIX .. firmware_obj.NodeId,
                common_def.FIRMWARE_INFO_INTERFACE
            )
            if ok and obj then
                obj.Version = revision
                log:notice('[Storage] %s update firmware info version to %s', firmware_obj.Name, revision)
                break
            end

            retries = retries + 1
            if retries < max_retries then
                log:info(
                    '[Storage] %s get firmware info failed, retry %s/%s, err:%s',
                    firmware_obj.Name,
                    retries,
                    max_retries,
                    obj
                )
                skynet.sleep(retry_interval / 10) -- skynet.sleep的单位是100ms,所以3000ms需要除以10
            else
                log:error(
                    '[Storage] %s get firmware info failed after %s retries, path:%s err:%s',
                    firmware_obj.Name,
                    max_retries,
                    common_def.FWINVENTORY_PATH_PREFIX .. firmware_obj.NodeId,
                    obj
                )
                return
            end
        end
    end)
end

return singleton(method_misc)