-- 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 skynet = require 'skynet'
local log = require 'mc.logging'
local json = require 'cjson'
local frudata_intf = require 'frudata_intf'
local ipmi = require 'ipmi'
local comp_code = ipmi.types.Cc
local fru_ipmi = require 'fru_ipmi'
local class_mgnt = require 'mc.class_mgnt'
local common = require 'common'
local fru_singleton = require 'fru_singleton'
local custom_messages = require 'messages.custom'

local FRU_AREA = common.FRU_AREA
local FRU_FIELD = common.FRU_FIELD

local SYSTEM_FRU = 0
local FUNC_ON = 'on'
local FUNC_OFF = 'off'
local VERIFY_SUCCESS = 'success'
local VERIFY_FAIL = 'fail'
local PART_NUM_HEAD = '03'

local config_manage = {}
local other_fruids = {}
local frudata_custom_table = {}
local frudata_custom_weith_flag_table = {}
local storage_type = {TianChi = true, EepromV2 = true, File = true}

local function custom_log(ctx, name, value)
    local log_str = string.format('Set CustomSettings %s to (%s) successfully', name, value)
    log:notice(log_str)
    log:operation(ctx:get_initiator(), 'frudata', log_str)
end

local function configuration_log(ctx, name, value, ret)
    local log_str = string.format('Set FRU %s to (%s) %s', name, value, ret)
    log:notice(log_str)
    log:operation(ctx:get_initiator(), 'frudata', log_str)
end

local function ipmi_info_fill(area, field, datas)
    local ipmi_info = {area = area, field = field, offset = 0, len = #datas, datas = datas}
    return ipmi_info
end

-- ipmi命令写入资源树
local function import_fru_data(fru_id, ipmi_info, ctx)
    local buf = string.format('%02X-%02X-%02X-%02X-%02X-', fru_id, ipmi_info.area, ipmi_info.field, ipmi_info.offset,
        ipmi_info.len)

    for i = 1, ipmi_info.len - 1, 1 do
        buf = buf .. string.format('%02X-', ipmi_info.datas:sub(i):byte())
    end

    if ipmi_info.len ~= 0 then
        buf = buf .. string.format('%02X', ipmi_info.datas:sub(ipmi_info.len):byte())
    end

    if not common.validate_byte(ipmi_info.area, ipmi_info.field, ipmi_info.datas) then
        ipmi.ipmi_operation_log(ctx, 'frudata', 'import fru%d E-label data(RAW:%s) failed', fru_id, buf)
        error(custom_messages.IPMIOutOfRange())
    end

    local result = frudata_intf.ipmi_write_elabel(fru_id, ipmi_info.area, ipmi_info.field, ipmi_info.offset,
        ipmi_info.len, ipmi_info.datas)

    if result ~= comp_code.Success then
        log:error('import fru%d E-label data(RAW:%s) failed, ret(%s)', fru_id, buf, result)
        error("Write fru E-label data failed")
    end
    log:debug('import fru%d E-label data(RAW:%s) successfully', fru_id, buf)
    log:operation(ctx:get_initiator(), 'frudata', 'import fru%d E-label data(RAW:%s) successfully', fru_id, buf)
end

-- on定制系统序列号，否则使用产品序列号
local function set_sys_serial_num(name, value, flag, ctx, obj)
    if not value then
        log:notice('not customize %s', name)
        return
    end

    local data = obj.ProductSerialNumber
    if flag == FUNC_ON then
        -- 序列号不支持开头或者结尾存在空格
        if string.sub(value, 1, 1) == ' ' or string.sub(value, -1) == ' ' then
            log:error('not support start or end is space, set system serial number failed')
            return
        end
        data = value
    end

    import_fru_data(SYSTEM_FRU, ipmi_info_fill(FRU_AREA.SYSTEM, FRU_FIELD.SYSTEM.SERIALNUMBER, data), ctx)
    custom_log(ctx, name, data)
end

-- on定制TAG，否则清空
local function set_asset_tag(name, value, flag, ctx)
    if not value then
        log:notice('not customize %s', name)
        return
    end

    local data = flag == FUNC_ON and value or ''
    import_fru_data(SYSTEM_FRU, ipmi_info_fill(FRU_AREA.PRODUCT, FRU_FIELD.PRODUCT.ASSETTAG, data), ctx)
    custom_log(ctx, name, data)
end

-- 定制机器名称，为空则system域同步produc域的product name
local function set_machine_name(value, ctx, obj)
    if not value then
        log:notice('not customize BMCSet_MACHINENAME')
        return
    end

    if value ~= '' then
        import_fru_data(SYSTEM_FRU, ipmi_info_fill(FRU_AREA.SYSTEM, FRU_FIELD.SYSTEM.PRODUCTNAME, value), ctx)
        import_fru_data(SYSTEM_FRU, ipmi_info_fill(FRU_AREA.PRODUCT, FRU_FIELD.PRODUCT.NAME, value), ctx)
    else
        import_fru_data(SYSTEM_FRU, ipmi_info_fill(FRU_AREA.SYSTEM, FRU_FIELD.SYSTEM.PRODUCTNAME, obj.ProductName), ctx)
    end
end

-- on获取单板序列号（board-serialnumber）并保存在BOARD_PART_NUMBER
local function set_board_part_num(value, ctx, obj)
    if value == FUNC_ON then
        -- 使用03和BOARD_SN前6位拼接作为part number
        local part_num = PART_NUM_HEAD .. string.sub(obj.BoardSerialNumber, 1, math.min(#obj.BoardSerialNumber, 6))
        import_fru_data(SYSTEM_FRU, ipmi_info_fill(FRU_AREA.BOARD, FRU_FIELD.BOARD.PARTNUMBER, part_num), ctx)
    end
end

-- 多条扩展信息以';'隔开
local function get_ex_info(value)
    local result = {}
    for s in string.gmatch(value, '([^;]+)') do
        table.insert(result, s)
    end
    return result
end

-- 定制product域扩展信息
local function set_product_ex_desc(value, ctx)
    if not value or value == '' then
        log:notice('not customize BMCSet_FRUProductExtraDescription')
        return
    end
    local datas = get_ex_info(value)
    for _, data in pairs(datas) do
        import_fru_data(SYSTEM_FRU, ipmi_info_fill(FRU_AREA.PRODUCT, FRU_FIELD.PRODUCT.EXTRA, data), ctx)
    end
end

-- 定制扩展域扩展信息
local function set_fru_ex_desc(value, ctx)
    if not value or value == '' then
        log:notice('not customize BMCSet_FRUDescription')
        return
    end
    local datas = get_ex_info(value)
    for _, data in pairs(datas) do
        import_fru_data(SYSTEM_FRU, ipmi_info_fill(FRU_AREA.EXTENDELABLE, FRU_FIELD.EXTENDELABLE.FIELAD, data), ctx)
    end
end

-- 定制其他fru board域的厂商信息
local function set_board_manufacturer(value, ctx)
    if not value or value == '' then
        log:notice('not customize BMCSet_CustomBoardManufacturer')
        return
    end

    for _, fruid in pairs(other_fruids) do
        import_fru_data(fruid, ipmi_info_fill(FRU_AREA.BOARD, FRU_FIELD.BOARD.MANUFACTURER, value), ctx)
    end
end

-- 定制其他fru product域的厂商信息
local function set_product_manufacturer(value, ctx)
    if not value or value == '' then
        log:notice('not customize BMCSet_CustomProductManufacturer')
        return
    end
    for _, fruid in pairs(other_fruids) do
        import_fru_data(fruid, ipmi_info_fill(FRU_AREA.PRODUCT, FRU_FIELD.PRODUCT.MANUFACTURER, value), ctx)
    end
end

-- 定制其他fru board域的扩展信息
local function set_board_ex_desc(value, ctx)
    if not value or value == '' then
        log:notice('not customize BMCSet_CustomBoardExtendLabel')
        return
    end
    for _, fruid in pairs(other_fruids) do
        local datas = get_ex_info(value)
        for _, data in pairs(datas) do
            import_fru_data(fruid, ipmi_info_fill(FRU_AREA.BOARD, FRU_FIELD.BOARD.EXTRA, data), ctx)
        end
    end
end

local function set_board_ex_info_pcb_ver(value, ctx, frudata_obj)
    if not value or value == '' then
        log:notice('not customize BMCSet_FRUBoardExInfoPcbVer')
        return
    end

    local fru_obj
    for _, obj in pairs(class_mgnt('Fru'):get_all()) do
        if obj.FruId == SYSTEM_FRU then
            fru_obj = obj
            break
        end
    end
    if not fru_obj then
        log:error('fru_obj is nil')
        return
    end
    local pcb_version = ''
    if value == FUNC_ON then
        -- 如果没有pcbversion，则用‘ ’代替，避免定制失败
        pcb_version = fru_obj.PcbVersion == '' and ' ' or fru_obj.PcbVersion
    elseif not string.find(frudata_obj.BoardCustomInfo, 'PCBVer=') then
        return
    end

    import_fru_data(SYSTEM_FRU, ipmi_info_fill(FRU_AREA.BOARD, FRU_FIELD.BOARD.EXTRA, 'PCBVer=' .. pcb_version), ctx)
end

local function set_frudata_pro(name, value, flag, ctx, fru_obj)
    if not value then
        log:notice('not customize %s', name)
        return
    end

    if flag == FUNC_ON then
        local property = frudata_custom_weith_flag_table[name].rpc_name
        if fru_obj[property] == value then
            log:notice('Set CustomSettings %s to (%s) successfully', name, value)
            return
        end
        import_fru_data(SYSTEM_FRU,
            ipmi_info_fill(common.property[property][1], common.property[property][2], value), ctx)
        custom_log(ctx, name, value)
    end
end

local function set_fru_manufacturer(name, value, flag, ctx, fru_obj)
    local data = flag == FUNC_ON and value or common.DEFAULT_MANU
    local property = frudata_custom_weith_flag_table[name].rpc_name
    if fru_obj[property] == data then
        log:notice('Set CustomSettings %s to (%s) successfully', name, value)
        return
    end
    import_fru_data(SYSTEM_FRU, ipmi_info_fill(common.property[property][1], common.property[property][2], data), ctx)
    custom_log(ctx, name, data)
end

local function set_frudata_chassis_type(name, value, flag, ctx, fru_obj)
    local data = flag == FUNC_ON and value or 0
    local property = frudata_custom_weith_flag_table[name].rpc_name
    if fru_obj[property] == common.chassis_type[data + 1] then
        log:notice('Set CustomSettings %s to (%s) successfully', name, data)
        return
    end
    import_fru_data(SYSTEM_FRU,
        ipmi_info_fill(common.property[property][1], common.property[property][2], string.char(data)), ctx)
    custom_log(ctx, name, data)
end

local function get_all_fruids_except_system()
    local objs = class_mgnt('FruData'):get_all()
    for _, obj in pairs(objs) do
        if obj.FruId ~= SYSTEM_FRU and storage_type[obj.StorageType] then
            other_fruids[#other_fruids + 1] = obj.FruId
        end
    end
end

-- 获取主板Fru资源树对象
local function get_system_fru_obj()
    for _, obj in pairs(class_mgnt('FruData'):get_all()) do
        if obj.FruId == SYSTEM_FRU then
            return obj
        end
    end
end

local function update_fru_data(import_type)
    fru_ipmi.write_system_data_sig:emit(SYSTEM_FRU)
    fru_ipmi.write_fru_data_sig:emit(SYSTEM_FRU)

    if import_type == 'custom' then
        for _, fruid in pairs(other_fruids) do
            fru_ipmi.write_fru_data_sig:emit(fruid)
        end
    end
end

-- 不需要校验的定制项
local function custom_skip_verify(data, custom_config, name)
    data[name].Value = ''
    data[name].Result = 'none'
end

-- 需要校验的定制项
local function custom_verify(data, value, name, obj)
    local table = frudata_custom_table[name] or frudata_custom_weith_flag_table[name]
    local rpc_name = table.rpc_name
    -- ChassisType需要转换
    local tmp = rpc_name == 'ChassisType' and common.chassis_type[value + 1] or value
    data[name].Value = value
    data[name].Result = obj[rpc_name] == tmp and VERIFY_SUCCESS or VERIFY_FAIL

    if data[name].Result == VERIFY_FAIL then
        log:error('frudata custom (%s) to (%s) fail, local is (%s)', name, value, obj[rpc_name])
    end
end

-- 判断定制项是否全部存在于环境上
local function is_contain(local_infos, custom_infos)
    for i = 1, #custom_infos do
        local is_found = false
        for j = 1, #local_infos do
            if custom_infos[i] == local_infos[j] then
                is_found = true
                break
            end
        end
        if not is_found then
            return false
        end
    end
    return true
end

-- 定制化扩展信息，可能环境上已经存在其他扩展信息，只需校验是否存在已定制的内容即可
local function custom_verify_extend(data, custom_config, name, obj)
    local custom_value = custom_config[name].Value
    if custom_value == '' then
        return custom_skip_verify(data, custom_config, name)
    end

    local rpc_name = frudata_custom_table[name].rpc_name
    local local_infos = get_ex_info(obj[rpc_name])
    local custom_infos = get_ex_info(custom_value)
    data[name].Value = custom_value
    data[name].Result = is_contain(local_infos, custom_infos) and VERIFY_SUCCESS or VERIFY_FAIL
    if data[name].Result == VERIFY_FAIL then
        log:error('frudata custom (%s) to (%s) fail, local is (%s)', name, custom_value, obj[rpc_name])
    end
end

-- 需要通过flag判断是否需要校验的定制项
local function custom_verify_with_flag(data, custom_config, name, obj)
    local value = custom_config[name].Value
    if custom_config[frudata_custom_weith_flag_table[name].custom_flag].Value == FUNC_OFF then
        -- 带默认值的需求，在off场景下需要校验是否定制为默认值
        if frudata_custom_weith_flag_table[name].default_value then
            value = frudata_custom_weith_flag_table[name].default_value
        else
            return custom_skip_verify(data, custom_config, name)
        end
    end
    custom_verify(data, value, name, obj)
end

local function csutom_verify_ex_info_pcb_ver(data, custom_config, name, obj)
    local custom_value = custom_config[name].Value
    local is_find_pcb_ver_sys = string.find(obj.BoardCustomInfo, 'PCBVer=')
    local flag

    if custom_value == FUNC_OFF then
        flag = not is_find_pcb_ver_sys
    else
        flag = is_find_pcb_ver_sys
    end

    data[name].Value = custom_value
    data[name].Result = flag and VERIFY_SUCCESS or VERIFY_FAIL
    if data[name].Result == VERIFY_FAIL then
        log:error('frudata custom (%s) to (%s) fail', name, custom_value)
    end
end

local function csutom_verify_board_part_num(data, custom_config, name, obj)
    local custom_value = custom_config[name].Value
    if custom_value == FUNC_OFF then
        return custom_skip_verify(data, custom_config, name)
    end

    local part_num = PART_NUM_HEAD .. string.sub
        (obj.BoardSerialNumber, 1, math.min(#obj.BoardSerialNumber, 6))
    data[name].Value = custom_value
    data[name].Result = part_num == obj.BoardPartNumber and VERIFY_SUCCESS or VERIFY_FAIL
    if data[name].Result == VERIFY_FAIL then
        log:error('frudata custom (%s) to (%s) fail', name, custom_value)
    end
end

local function custom_verify_machine_name(data, custom_config, name, obj)
    local system_pro_name = obj.SystemProductName
    local product_name = obj.ProductName
    local custom_value = custom_config[name].Value
    if custom_value == '' then
        data[name].Value = product_name
        data[name].Result = system_pro_name == product_name and VERIFY_SUCCESS or VERIFY_FAIL
    else
        data[name].Value = custom_value
        data[name].Result = (system_pro_name == custom_value and product_name == custom_value) and
            VERIFY_SUCCESS or VERIFY_FAIL
    end

    if data[name].Result == VERIFY_FAIL then
        log:error('frudata custom (%s) to (%s) fail', name, product_name)
    end
end

frudata_custom_weith_flag_table = {
    Custom_SerialNUM = {
        import_func = set_sys_serial_num,
        rpc_name = 'SystemSerialNumber',
        custom_flag = 'BMCSet_CustomSerialNUM'
    },

    Custom_AssetTag = {
        import_func = set_asset_tag,
        rpc_name = 'AssetTag',
        custom_flag = 'BMCSet_CustomAssetTag'
    },

    Custom_ChassisPartNumber = {
        import_func = set_frudata_pro,
        rpc_name = 'ChassisPartNumber',
        custom_flag = 'BMCSet_CustomChassisPartNumber'
    },

    Custom_ChassisChassisType = {
        import_func = set_frudata_chassis_type,
        rpc_name = 'ChassisType',
        custom_flag = 'BMCSet_CustomChassisType',
        default_value = 0
    },

    Custom_Mainboard_BoardManufacturer = {
        import_func = set_fru_manufacturer,
        rpc_name = 'BoardManufacturer',
        custom_flag = 'BMCSet_CustomMainboard_BoardManufacturer',
        default_value = common.DEFAULT_MANU
    },

    Custom_Mainboard_ProductManufacturer = {
        import_func = set_fru_manufacturer,
        rpc_name = 'ManufacturerName',
        custom_flag = 'BMCSet_CustomMainboard_ProductManufacturer',
        default_value = common.DEFAULT_MANU
    },

    Custom_Mainboard_BoardProductName = {
        import_func = set_frudata_pro,
        rpc_name = 'BoardProductName',
        custom_flag = 'BMCSet_CustomMainboard_BoardProductName'
    },

    Custom_Mainboard_ProductPartNum = {
        import_func = set_frudata_pro,
        rpc_name = 'ProductPartNumber',
        custom_flag = 'BMCSet_CustomMainboard_ProductPartNum'
    },

    Custom_Mainboard_ProductSerialNum = {
        import_func = set_frudata_pro,
        rpc_name = 'ProductSerialNumber',
        custom_flag = 'BMCSet_CustomMainboard_ProductSerialNum'
    },

    Custom_Manufacturer = {
        import_func = set_fru_manufacturer,
        rpc_name = 'SystemManufacturer',
        custom_flag = 'BMCSet_CustomManufacturer',
        default_value = common.DEFAULT_MANU
    }
}

frudata_custom_table = {
    BMCSet_MACHINENAME = {
        import_func = set_machine_name,
        verify_func = custom_verify_machine_name,
        rpc_name = 'SystemProductName',
    },

    BMCSet_FRUProductExtraDescription = {
        import_func = set_product_ex_desc,
        verify_func = custom_verify_extend,
        rpc_name = 'ProductCustomInfo',
    },

    BMCSet_FRUDescription = {
        import_func = set_fru_ex_desc,
        verify_func = custom_verify_extend,
        rpc_name = 'BoardCustomInfo',
    },

    -- 特殊定制项
    BMCSet_FRUBoardExInfoPcbVer = {
        import_func = set_board_ex_info_pcb_ver,
        verify_func = csutom_verify_ex_info_pcb_ver,
    },

    BMCSet_CustomBoardPartNumberFlag = {
        import_func = set_board_part_num,
        verify_func = csutom_verify_board_part_num,
    },

    -- 定制非fru0的定制项
    BMCSet_CustomBoardManufacturer = {
        import_func = set_board_manufacturer,
        verify_func = custom_skip_verify,
        rpc_name = 'BoardCustomInfo',
    },

    BMCSet_CustomProductManufacturer = {
        import_func = set_product_manufacturer,
        verify_func = custom_skip_verify,
        rpc_name = 'ManufacturerName',
    },

    BMCSet_CustomBoardExtendLabel = {
        import_func = set_board_ex_desc,
        verify_func = custom_skip_verify,
        rpc_name = 'BoardCustomInfo',
    },

    -- 未实现定制项
    Custom_SerialnumSuffixBySlot = {
        import_func = function()
        end,
        verify_func = custom_skip_verify,
    }
}

-- 避免报错前其他定制项已经写入，先更新内存
local function fru_config_error(ctx, name, value)
    fru_ipmi.write_system_data_sig:emit(SYSTEM_FRU)
    fru_ipmi.write_fru_data_sig:emit(SYSTEM_FRU)
    configuration_log(ctx, name, value, 'failed')
    error(custom_messages.CollectingConfigurationErrorDesc(name))
end

local function get_chassis_id_by_type(ctx, type_str)
    for key, value in pairs(common.chassis_type) do
        if value == type_str then
            return string.char(key - 1)
        end
    end
    log:error('try to set not supported chassis type:(%s)', type_str)
    fru_config_error(ctx, 'ChassisType', type_str)
end

local function set_frudata_custom_info(fru_obj, name, value, ctx)
    local custom_infos = get_ex_info(value)
    local local_infos = get_ex_info(fru_obj[name])
    if is_contain(local_infos, custom_infos) then
        log:notice('Set FRU %s to (%s) successfully', name, value)
        return
    end

    local ok
    for _, data in pairs(custom_infos) do
        ok, _ = pcall(function (...)
            import_fru_data(SYSTEM_FRU,
                ipmi_info_fill(common.property[name][1], common.property[name][2], data), ctx)
        end)
        if not ok then
            fru_config_error(ctx, name, value)
        end
    end

    configuration_log(ctx, name, value, 'successfully')
end

local function set_frudata_pro_config(fru_obj, name, value, ctx)
    if not value or not value.Import then
        log:notice('FRU property [%s] import not supported.', name)
        return
    end

    local config_value = value.Value
    if fru_obj[name] == config_value then
        log:notice('Set FRU %s to (%s) successfully', name, config_value)
        return
    end
    if name == 'ProductCustomInfo' or name == 'BoardCustomInfo' then
        return set_frudata_custom_info(fru_obj, name, config_value, ctx)
    end

    -- 生成厂商不能设置为空，有默认值Huawei
    if (name == 'ManufacturerName' or name == 'BoardManufacturer') and config_value == '' then
        log:error('Frudata not support to set manufacturer null')
        fru_config_error(ctx, name, config_value)
    end

    -- ChassisType需要转换
    local data = name == 'ChassisType' and get_chassis_id_by_type(ctx, config_value) or config_value
    local ok, _ = pcall(function (...)
        import_fru_data(SYSTEM_FRU, ipmi_info_fill(common.property[name][1], common.property[name][2], data), ctx)
    end)

    if not ok then
        fru_config_error(ctx, name, config_value)
    end

    configuration_log(ctx, name, config_value, 'successfully')
end

local function configuration_import(configuration_config, fru_obj, ctx)
    for key, value in pairs(configuration_config) do
        set_frudata_pro_config(fru_obj, key, value, ctx)
    end
end

local function custom_import(custom_config, fru_obj, ctx)
    local custom_value, flag_name, flag_value
    for key, value in pairs(custom_config) do
        custom_value = value.Value
        if frudata_custom_weith_flag_table[key] then
            flag_name = frudata_custom_weith_flag_table[key].custom_flag
            flag_value = custom_config[flag_name].Value
            frudata_custom_weith_flag_table[key].import_func(key, custom_value, flag_value, ctx, fru_obj)
            custom_log(ctx, flag_name, flag_value) --打印falg项的操作日志
        end

        if frudata_custom_table[key] then
            frudata_custom_table[key].import_func(custom_value, ctx, fru_obj)
            custom_log(ctx, key, custom_value)
        end
    end
end

-- 配置导入接口
function config_manage.import(ctx, datas, import_type)
    local data = json.decode(datas)
    local config = data.ConfigData
    if not data or not config or type(config) ~= 'table' then
        log:error('import data [%s] is invalid.', datas)
        return
    end

    -- 获取本地配置
    local fru_obj = get_system_fru_obj()
    if not fru_obj then
        log:error('get system fru obj failed.')
        return
    end
    get_all_fruids_except_system()

    if import_type == 'configuration' then
        configuration_import(config.FRU, fru_obj, ctx)
    elseif import_type == 'custom' then
        custom_import(config.CustomSettings, fru_obj, ctx)
    else
        log:error('unknown import type(%s)', import_type)
    end

    -- 更新资源树
    update_fru_data(import_type)

    skynet.sleep(100)
    local ret

    while true do
        ret = 0
        log:info('fru0 update status = %s', fru_singleton.get_instance().update_status[SYSTEM_FRU])
        if fru_singleton.get_instance().update_status[SYSTEM_FRU] ~= 0 then
            ret = ret + 1
        end

        for _, fruid in pairs(other_fruids) do
            log:info('fru%s update status = %s', fruid, fru_singleton.get_instance().update_status[fruid])
            if fru_singleton.get_instance().update_status[fruid] ~= 0 then
                ret = ret + 1
            end
        end
        if ret == 0 then
            log:notice('write eeprom completed')
            break
        end
        skynet.sleep(500)
    end
    log:notice('frudata customize successfully')
end

local function configuration_export()
    local fru_obj = get_system_fru_obj()
    local data = {}
    if not fru_obj then
        log:error('get system fru obj failed.')
        return data
    end
    local configuration_config = {}

    configuration_config.SystemSerialNumber = fru_obj.SystemSerialNumber
    configuration_config.SystemProductName = fru_obj.SystemProductName
    configuration_config.AssetTag = fru_obj.AssetTag
    configuration_config.ManufacturerName = fru_obj.ManufacturerName
    configuration_config.ProductCustomInfo = fru_obj.ProductCustomInfo
    configuration_config.ProductName = fru_obj.ProductName
    configuration_config.ProductPartNumber = fru_obj.ProductPartNumber
    configuration_config.ProductSerialNumber = fru_obj.ProductSerialNumber
    configuration_config.BoardCustomInfo = fru_obj.BoardCustomInfo
    configuration_config.BoardPartNumber = fru_obj.BoardPartNumber
    configuration_config.BoardProductName = fru_obj.BoardProductName
    configuration_config.BoardSerialNumber = fru_obj.BoardSerialNumber
    configuration_config.BoardManufacturer = fru_obj.BoardManufacturer
    configuration_config.ChassisType = fru_obj.ChassisType
    configuration_config.ChassisPartNumber = fru_obj.ChassisPartNumber

    data.FRU = configuration_config
    log:notice('export configuration config successfully.')
    return data
end

local function custom_export()
    local data = {}
    local customize_config = {}
    customize_config.Custom_SerialnumSuffixBySlot = ''

    data.CustomSettings = customize_config
    log:notice('export custom config successfully.')
    return data
end

-- 配置导出接口
function config_manage.export(ctx, export_type)
    local data = {}
    if export_type == 'configuration' then
        data = configuration_export()
    elseif export_type == 'custom' then
        data = custom_export()
    end

    return json.encode({ ConfigData = data })
end

-- 定制化校验接口
function config_manage.verify_config(ctx, config_data)
    local fru_obj = get_system_fru_obj()
    local custom_config = json.decode(config_data).Components.frudata.ConfigData.CustomSettings
    local data = {}
    if not fru_obj then
        log:error('get system fru obj failed.')
        return json.encode(data)
    end
    local custom_skip_verify_table = {
        -- flag项，不校验
        BMCSet_CustomSerialNUM = true,
        BMCSet_CustomAssetTag = true,
        BMCSet_CustomManufacturer = true,
        BMCSet_CustomSerialnumSuffix = true,
        BMCSet_CustomSysBoardManufacturer = true,
        BMCSet_CustomChassisPartNumber = true,
        BMCSet_CustomChassisType = true,
        BMCSet_CustomMainboard_BoardManufacturer = true,
        BMCSet_CustomMainboard_ProductManufacturer = true,
        BMCSet_CustomMainboard_BoardProductName = true,
        BMCSet_CustomMainboard_ProductPartNum = true,
        BMCSet_CustomMainboard_ProductSerialNum = true,

        -- 已废弃，由CustomMainboard_BoardManufacturer和CustomMainboard_ProductManufacturer取代
        Custom_Manufacturer = true,
        -- 已废弃，由BMCSet_MACHINENAME承载功能
        BMCSet_CustomMainboard_ProductName = true,
        Custom_Mainboard_ProductName = true,
    }

    for name, _ in pairs(custom_config) do
        data[name] = {}
        if custom_skip_verify_table[name] then
            custom_skip_verify(data, custom_config, name)
        end
        if frudata_custom_weith_flag_table[name] then
            custom_verify_with_flag(data, custom_config, name, fru_obj)
        end
        if frudata_custom_table[name] then
            frudata_custom_table[name].verify_func(data, custom_config, name, fru_obj)
        end
    end

    log:notice('frudata verify customize successfully')
    return json.encode(data)
end

return config_manage
