-- 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 or type_id == common_def.PMC_2100_8I_SMART_HBA 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
    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) 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
    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
    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) 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
    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) 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
    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
    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) 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)
    for _, vendor in ipairs(common_def.DRIVE_VENDOR_TABLE) do
        local vendor_id = string.format("%X", vendor.vendor_id)
        if vendor_id == manufacturer_oui then
            return vendor.vendor_name
        end
    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 ''
        }
        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)