-- 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 class = require "mc.class"
local prop_def = require "macros.property_def"
local boot_options_object = require 'pojo.boot_options_object'
local chassis_msg = require 'bios.ipmi.ipmi_message'
local bios_factory = require 'factory.bios_factory'
local boot_def = require 'macros.boot_def'
local bs_util = require 'util.base_util'
local obj_def = require 'macros.object_def'
local prop_method_app = require 'macros.property_method_app'
local vos = require 'utils.vos'
local ipmi = require 'ipmi'
local comp_code = ipmi.types.Cc
local singleton = require 'mc.singleton'
local json = require 'cjson'
local log = require 'mc.logging'
local cert_core = require 'certificate.core'
local base_messages = require 'messages.base'
local custom_messages = require 'messages.custom'
local boot_config_activate_service = require 'service.boot_config_activate_service'
local enums = require 'ipmi.enums'
local utils = require 'mc.utils'
local utils_core = require 'utils.core'
local tasks = require 'mc.orm.tasks'
local CT = enums.ChannelId
local bios_enum = require 'domain.bios_firmware.defs'
local PACKAGE_PATH<const> = '/bmc/kepler/Managers/1/Package'

-- CertificatesType类型(redfish标准定义的类型，PEM:单证书, PEMchain:证书链, PKCS7:p7形式单或多证书.)
local CERTIFICATE_TYPE_SINGLE_PEM < const > = 'PEM'

local boot_options_service = class()

function boot_options_service:ctor(db)
    self.db = db
    self.boot_option_collection = {}
end

function boot_options_service:delete_object(system_id)
    local boot_options_obj = self:get_obj(system_id)
    if not boot_options_obj then
        return
    end
    self.boot_option_collection[system_id] = nil
end

function boot_options_service:add_object(obj)
    local sysId = obj.SystemId
    if self.boot_option_collection[sysId] then
        return
    end
    self.boot_option_collection[sysId] = boot_options_object.new(obj, self.db)
end

function boot_options_service:get_obj(system_id)
    return self.boot_option_collection[system_id or 1]
end

-- 响应获取option的ipmi的返回值
local function response_get_boot_option(complete_code, data)
    local format = '<<ParameterVersion, BootOptionSelector, ConfigurationData/string>>'
    local bin_data = ''
    if data ~= '' then
        bin_data = bs_util.struct_to_binary(data, format)
    end

    return chassis_msg.GetBootOptionRsp.new(complete_code, bin_data)
end

-- 响应设置option的ipmi的返回值
local function response_set_boot_option(complete_code)
    return chassis_msg.SetBootOptionRsp.new(complete_code)
end

function boot_options_service:set_boot_option_valid(selector, valid, system_id)
    local boot_option_obj = self:get_obj(system_id)
    if not boot_option_obj or not selector or not valid then
        log:error('[bios]set_boot_option_valid:system id(%s) invalid', system_id)
        return false
    end

    return boot_option_obj:set_boot_option_valid(selector, valid)
end

--获取配置数据
function boot_options_service:get_option_info(real_selector, config_data, system_id)
    local boot_option_obj = self:get_obj(system_id)
    if not boot_option_obj or not real_selector then
        log:error('[bios]get_option_info:system id(%s) invalid', system_id)
        return nil
    end

    return boot_option_obj:get_option_info(real_selector, config_data)
end

local function transform_effective_times_to_string(effective_times)
    local start_option_string_tbl = {
        [boot_def.EFFECTIVE_ONCE] = "once",
        [boot_def.EFFECTIVE_FOREVER] = "permanent"
    }
    local times_str = start_option_string_tbl[effective_times]
    if not times_str then
        return "unspecified"
    end
    return times_str
end

function boot_options_service:receive_from_bios(ctx, system_id)
    local bios_ser = bios_factory.get_service('bios_service')
    if not bios_ser then
        return false
    end
    local bios_startup_state = bios_ser:get_prop(prop_def.SYSTEM_STARTUP_STATE, system_id)
    if ctx.chan_num == CT.CT_SYS:value() and bios_startup_state > prop_def.BIOS_STARTUP_STATE_OFF and
        bios_startup_state < prop_def.BIOS_STARTUP_POST_STAGE_FINISH then
        return true
    end
    return false
end

function boot_options_service:is_boot_option_policy(system_id)
    local boot_ser = bios_factory.get_service('boot_service')
    if not boot_ser then
        log:error('get boot option policy failed, boot_ser is nil')
        return
    end
    local boot_option_policy = boot_ser:get_prop("BootOptionPolicy", system_id)
    return boot_option_policy == 1
end

function boot_options_service:set_start_option_flag_ext(ctx, effective_times_str, system_id)
    local effective_times = boot_def.start_option_flag_tbl[effective_times_str]
    if not effective_times then
        log:error('[bios] set start option flag(%s) invalid', effective_times_str)
        bs_util.record_operation(ctx, system_id, "Set boot device effective mode (unspecified) failed")
        error(base_messages.PropertyValueNotInList(effective_times_str, "Boot/BootSourceOverrideEnabled"))
    end
    local ret = self:set_start_option_flag(ctx, effective_times, system_id)
    if ret ~= prop_def.RESPONSE_OK then
        return ret
    end
    if self:is_boot_option_policy(system_id) and effective_times_str == "Continuous" then
        log:notice("[bios] start option policy, set boot type order")
        local start_option = self:get_start_option(system_id)
        self:set_boot_type_order_1st(ctx, start_option, system_id)
    end
    return ret
end

-- 设置启动项生效次数:[0：Disabled; 1：Once; 2；Continuous]
function boot_options_service:set_start_option_flag(ctx, effective_times, system_id)
    local boot_option_obj = self:get_obj(system_id)
    local times_str = transform_effective_times_to_string(effective_times)
    if not boot_option_obj then
        log:error("boot_options_service: set_start_option_flag param invalid.")
        bs_util.record_operation(ctx, system_id, string.format("Set boot device effective mode (%s) failed", times_str))
        error(base_messages.InternalError())
    end

    local valid = boot_option_obj:set_effective_time(effective_times)
    if not valid then
        log:error("boot_options_service: set_effective_time faild.")
        bs_util.record_operation(ctx, system_id, string.format("Set boot device effective mode (%s) failed", times_str))
        error(base_messages.InternalError())
    end

    local boot_ser = bios_factory.get_service('boot_service')
    if not boot_ser then
        log:error("boot_options_service: boot_ser is null.")
        bs_util.record_operation(ctx, system_id, string.format("Set boot device effective mode (%s) failed", times_str))
        error(base_messages.InternalError())
    end

    boot_ser:set_start_option_flag(effective_times, system_id)
    self:set_boot_option_valid(boot_def.BIOS_BOOT_FLAGS_CMD,
        boot_def.BIOS_BOOT_OPTION_VALID, system_id)
    bs_util.record_operation(ctx, system_id, string.format("Set boot device effective mode (%s) successfully", times_str))
    return prop_def.RESPONSE_OK
end

-- 设置启动模式
local function construct_mode_map()
    local mode_map = {}
    mode_map[boot_def.BIOS_BOOT_UEFI] = boot_def.BIOS_UEFI_BOOT_STR
    return mode_map
end

local function get_setting_path(system_id)
    local bios_ser = bios_factory.get_service('bios_service')
    if not bios_ser then
        return nil
    end

    local path = bios_ser:get_bios_prop(prop_method_app.BIOS_FILE_SETTING_NAME, system_id)
    if not path or not vos.get_file_accessible(path) then
        log:error('Set boot mode:write_file setting path not exist')
        return nil
    end

    return path
end

local function write_mode_to_json(mode_str, system_id)
    local path = get_setting_path(system_id)
    if not path then
        return prop_def.E_FAILURE
    end

    local setting_json = bs_util.get_file_json(path)
    if not setting_json then
        setting_json = {}
    end

    setting_json[boot_def.BIOS_BOOT_TYPE_STR] = mode_str
    local json_str = json.encode(setting_json)
    local err_code = bs_util.write_file_data(path, json_str)
    if err_code == prop_def.E_FAILURE then
        log:error('Set boot mode failed: write_file_data fail')
        return prop_def.E_FAILURE
    end

    return prop_def.E_OK
end

function boot_options_service:set_boot_mode_ext(ctx, mode_str, system_id)
    if mode_str ~= boot_def.BIOS_UEFI_BOOT then
        log:error("boot_options_service: start mode(%s) invalid", mode_str)
        bs_util.record_operation(ctx, system_id, "Set boot mode failed")
        error(base_messages.PropertyValueNotInList(mode_str, "Boot/BootSourceOverrideMode"))
    end
    return self:set_boot_mode(ctx, boot_def.BIOS_BOOT_UEFI, system_id)
end

-- 设置启动模式:0表示LEGACY，1表示UEFI
function boot_options_service:set_boot_mode(ctx, mode, system_id)
    local mode_map = construct_mode_map()
    local boot_options_obj = self:get_obj(system_id)
    local boot_service = bios_factory.get_service('boot_service')
    if not mode or not boot_options_obj  or not mode_map or
        not boot_service then
        log:error('Set boot mode failed: param invalid.')
        bs_util.record_operation(ctx, system_id, "Set boot mode failed")
        error(base_messages.InternalError())
    end

    local mode_str = mode_map[mode]
    if not mode_str then
        log:error('Set boot mode failed: mode (' .. mode .. ') invalid.')
        bs_util.record_operation(ctx, system_id, "Set boot mode failed")
        error(base_messages.InternalError())
    end

    local err_code = write_mode_to_json(mode_str, system_id)
    if err_code == prop_def.E_FAILURE then
        log:error('Set boot mode failed: write_mode_to_json fail')
        bs_util.record_operation(ctx, system_id, "Set boot mode failed")
        error(base_messages.InternalError())
    end

    local valid = boot_options_obj:set_boot_mode(mode)
    if not valid then
        log:error('Set boot mode failed: set boot option fail.')
        bs_util.record_operation(ctx, system_id, "Set boot mode failed")
        error(base_messages.InternalError())
    end

    self:set_boot_option_valid(boot_def.BIOS_BOOT_FLAGS_CMD,
        boot_def.BIOS_BOOT_OPTION_VALID, system_id)

    boot_service:set_boot_prop(obj_def.BOOT_SOURCE_OVERRIDE_MODE, boot_def.BIOS_UEFI_BOOT, system_id)
    bs_util.record_operation(ctx, system_id, "Set boot mode UEFI successfully")
    return prop_def.RESPONSE_OK
end

-- 设置启动项,[0:None;1:Pxe;2:Hdd;5:Cd;6:BiosSetup;0x0f:Floppy]
local start_option_string_tbl = {
    [boot_def.NO_OVERRIDE] = 1,
    [boot_def.FORCE_PEX] = 1,
    [boot_def.FORCE_HARD_DRIVE] = 1,
    [boot_def.FORCE_CD_DVD] = 1,
    [boot_def.FORCE_BIOS_SETUP] = 1,
    [boot_def.FORCE_FLOPPY_REMOVABLE_MEDIA] = 1
}

local start_option_log_string_tbl = {
    [boot_def.NO_OVERRIDE] = "no override",
    [boot_def.FORCE_PEX] = "PXE",
    [boot_def.FORCE_HARD_DRIVE] = "hard_drive",
    [boot_def.FORCE_CD_DVD] = "CD/DVD",
    [boot_def.FORCE_BIOS_SETUP] = "BIOS setup",
    [boot_def.FORCE_FLOPPY_REMOVABLE_MEDIA] = "floppy/primary removable media"
}

function boot_options_service:set_start_option_ext(ctx, start_option_str, system_id)
    local start_option = boot_def.start_option_tbl[start_option_str]
    if not start_option then
        log:error("boot_options_service: start option(%s) invalid", start_option_str)
        bs_util.record_operation(ctx, system_id, "Set boot device failed")
        error(base_messages.PropertyValueNotInList(start_option_str, "Boot/BootSourceOverrideTarget"))
    end
    local ret = self:set_start_option(ctx, start_option, system_id)
    if ret ~= prop_def.RESPONSE_OK then
        return ret
    end
    if self:is_boot_option_policy(system_id) then
        log:notice("[bios] option policy: set start option flag once")
        self:set_start_option_flag_ext(ctx, "Once", system_id)
    end
    return ret
end

function boot_options_service:set_start_option(ctx, start_option, system_id)
    local start_option_string = start_option_log_string_tbl[start_option]
    local boot_options_obj = self:get_obj(system_id)
    local boot_ser = bios_factory.get_service('boot_service')
    if not boot_options_obj or not boot_ser or not start_option or
        not start_option_string_tbl[start_option] then
        log:error("boot_options_service: param invalid.")
        bs_util.record_operation(ctx, system_id, "Set boot device failed")
        error(base_messages.PropertyValueNotInList(tostring(start_option), "Boot/BootSourceOverrideTarget"))
    end

    local current_effctive_times = boot_ser:get_start_option_flag(system_id)
    local valid = boot_options_obj:set_start_option(start_option, current_effctive_times)
    if not valid then
        log:error("boot_options_service: update option fail.")
        bs_util.record_operation(ctx, system_id, string.format("Set boot device to (%s) failed", start_option_string))
        error(base_messages.InternalError())
    end

    local bmc_enable = boot_options_obj:get_bmc_enable()
    if not bmc_enable then
        log:error("boot_options_service: get bmc enable fail.")
        bs_util.record_operation(ctx, system_id, string.format("Set boot device to (%s) failed", start_option_string))
        error(base_messages.InternalError())
    end

    boot_ser:set_boot_options_property(bmc_enable, start_option, system_id)
    self:set_boot_option_valid(boot_def.BIOS_BOOT_FLAGS_CMD, boot_def.BIOS_BOOT_OPTION_VALID, system_id)

    bs_util.record_operation(ctx, system_id, string.format("Set boot device to (%s) successfully", start_option_string))

    return prop_def.RESPONSE_OK
end

local mode_map = {
    [boot_def.BIOS_UEFI_BOOT_STR] = boot_def.BIOS_BOOT_UEFI
}

function boot_options_service:update_boot_mode(boot_option_obj, system_id)
    local path = get_setting_path(system_id)
    if not path then
        return
    end

    local setting_json = bs_util.get_file_json(path)
    if not setting_json then
        log:debug("update_boot_mode: setting_json is null.")
        return
    end

    local boot_ser = bios_factory.get_service('boot_service')
    if not setting_json or not boot_option_obj or not boot_ser then
        log:error("update_boot_mode: param invalid.")
        return
    end

    local mode_str = setting_json[boot_def.BIOS_BOOT_TYPE_STR]
    if not mode_str then
        log:debug("update_boot_mode: has not mode setting.")
        return
    end

    local mode = mode_map[mode_str]
    if not mode then
        log:error("update_boot_mode: setting " .. mode_str .. " invalid.")
        return
    end

    boot_option_obj:set_boot_mode(mode)
    boot_ser:set_boot_prop(obj_def.BOOT_SOURCE_OVERRIDE_MODE, boot_def.BIOS_UEFI_BOOT, system_id)
    self:set_boot_option_valid(boot_def.BIOS_BOOT_FLAGS_CMD, boot_def.BIOS_BOOT_OPTION_VALID, system_id)
end

function boot_options_service:get_system_id(ctx)
    if not ctx then
        return 1
    end
    return ctx.HostId or 1
end

function boot_options_service:get_boot_option_data(req, ctx)
    local system_id = self:get_system_id(ctx)
    log:notice("[bios]get_boot_option_data start, system id is %s.", system_id)
    local boot_option_obj = self:get_obj(system_id)
    if not req or not req.BootOptionSelector or not boot_option_obj then
        log:error("get_boot_options: bios_get_boot_option param invalid.")
        return response_get_boot_option(comp_code.InvalidFieldRequest, '')
    end

    if req.BootOptionSelector == boot_def.BIOS_BOOT_FLAGS_CMD and string.len(req.OtherSelector) ~= 2 then
        log:error('[bios]ipmi cmd[%s] length err.', req.BootOptionSelector)
        return response_get_boot_option(comp_code.ReqDataLenInvalid, '')
    end

    local config_data = req.OtherSelector
    local option_selector = req.BootOptionSelector
    local real_selector = option_selector & boot_def.NBIT7
    if real_selector == boot_def.BIOS_BOOT_FLAGS_CMD then
        self:update_boot_mode(boot_option_obj, system_id)
    end
    -- 获取的启动参数是数组形式
    local option_data, error_code = self:get_option_info(real_selector, config_data, system_id)
    if not option_data then
        log:error("get_boot_options: bios_get_boot_option get option fail.")
        if error_code then
            return response_get_boot_option(error_code, '')
        else
            return response_get_boot_option(boot_def.COMP_CODE_PARAM_NOT_SUPPORTED, '')
        end
    end

    local boot_valid = boot_option_obj:get_boot_option_valid(real_selector)
    local resp_selector = (boot_valid << 7) | real_selector

    local resp_data = {}
    resp_data.ParameterVersion = boot_def.BOOT_OPTION_PARAMETER_VERSION
    resp_data.BootOptionSelector = resp_selector
    resp_data.ConfigurationData = bs_util.unit_arr_to_bin(option_data)

    if req.BootOptionSelector == 0x65 or req.BootOptionSelector == 0xE5 then
        return chassis_msg.GetBootOptionPowerOnDelay0Rsp.new(comp_code.Success,
            resp_data.ParameterVersion, resp_data.BootOptionSelector, resp_data.ConfigurationData)
    end

    return response_get_boot_option(comp_code.Success, resp_data)
end

-- 自动清除是否生效
function boot_options_service:auto_clear_valid(ctx)
    --非Mutihost不支持
    local system_id = 1
    -- IPMI标记是否打开
    if not self:get_auto_clear_valid() then
        return false
    end

    -- 过滤BIOS发送的set system boot options命令，在BIOS启动过程中，通过BT通道设置的IPMI命令也不启用清除功能
    local bios_ser = bios_factory.get_service('bios_service')
    if not bios_ser then
        return true
    end
    local bios_startup_state = bios_ser:get_prop(prop_def.SYSTEM_STARTUP_STATE, system_id)
    if ctx.chan_num == CT.CT_SYS:value() and bios_startup_state > prop_def.BIOS_STARTUP_STATE_OFF and
        bios_startup_state < prop_def.BIOS_STARTUP_POST_STAGE_FINISH then
        return false
    end

    return true
end

function boot_options_service:set_boot_option_data(req, ctx)
    local system_id = self:get_system_id(ctx)
    log:notice("[bios]set_boot_option_data start, system id is %s.", system_id)
    local boot_option_obj = self:get_obj(system_id)
    local boot_ser = bios_factory.get_service('boot_service')
    if not req or not req.BootOptionSelector or not boot_option_obj or
        not req.ConfigData or not boot_ser then
        log:error("set_boot_option_data: bios_get_boot_option param invalid.")
        return response_set_boot_option(comp_code.InvalidFieldRequest)
    end

    local option_selector = req.BootOptionSelector
    local real_selector = option_selector & boot_def.NBIT7
    local config_data = req.ConfigData
    local res_code = boot_option_obj:parse_ipmi_data(real_selector, config_data, ctx)
    if res_code ~= boot_def.COMP_CODE_SUCCESS then
        log:error("set_boot_option_data: parse_ipmi_data fail.")
        ipmi.ipmi_operation_log(ctx, 'BIOS', "Set system boot options failed")
        return response_set_boot_option(res_code)
    end

    local valid = (option_selector & boot_def.PBIT7) >> (boot_def.BIOS_BOOT_OPTION_BIT)
    -- 自动清除情况，valid标记也需要检测到上下电后再设置，需要过滤BIOS设置的命令
    if real_selector == boot_def.BIOS_BOOT_FLAGS_CMD and self:auto_clear_valid(ctx) then
        boot_config_activate_service.get_instance():update_valid(valid)
    else
        self:set_boot_option_valid(real_selector, valid, system_id)
    end
    return response_set_boot_option(comp_code.Success)
end

local function error_process(ret)
    local switch = {
        [prop_def.SECUREBOOT_METHOD_EXPIRED_CERT] = function ()
            error(custom_messages.CertificateExpired())
        end,
        [prop_def.SECUREBOOT_METHOD_BIOS_STATE_UNSUPPORT] = function ()
            error(custom_messages.BiosStateNotAllowed())
        end,
        [prop_def.SECUREBOOT_METHOD_PEM_FORMAT_ERR] = function ()
            error(custom_messages.PemFormatErr())
        end,
        [prop_def.SECUREBOOT_METHOD_STRING_TOO_LONG] = function ()
            error(custom_messages.StringValueTooLong("CertificateString", prop_def.CERT_MAX_SIZE))
        end,
        [prop_def.SECUREBOOT_METHOD_IMPORT_LIMIT] = function ()
            error(custom_messages.ImportLimitExceed())
        end,
        [prop_def.SECUREBOOT_METHOD_ERR] = function ()
            error(custom_messages.WrongFileFormat())
        end
    }
    if switch[ret] then
        switch[ret]()
    end
    error(base_messages.InternalError())
end

local function check_bios_startup_state(system_id)
    local bios_ser = bios_factory.get_service('bios_service')
    if bios_ser == nil then
        log:debug("bios_service not found")
        return prop_def.SECUREBOOT_METHOD_ERR
    end
    local startup_state = bios_ser:get_prop(prop_def.SYSTEM_STARTUP_STATE, system_id)
    if startup_state == prop_def.BIOS_STARTUP_STATE_OFF or startup_state == prop_def.BIOS_STARTUP_POST_STAGE_FINISH then
        return prop_def.SECUREBOOT_METHOD_OK
    else 
        return prop_def.SECUREBOOT_METHOD_BIOS_STATE_UNSUPPORT
    end
end

local function check_import_cert_string(cert_string)
    if string.len(cert_string) > prop_def.CERT_MAX_SIZE then
        return prop_def.SECUREBOOT_METHOD_STRING_TOO_LONG
    end

    local ok, info = pcall(function()
        return cert_core.get_cert_info_from_mem(cert_string)
    end)
    if not ok then
        log:error("invalid certificate format")
        return prop_def.SECUREBOOT_METHOD_PEM_FORMAT_ERR
    end
    local not_after = info['ValidNotAfter']
    local not_before = info['ValidNotBefore']
    local now = os.time()
    if not_after < now or not_before > now then
        log:error("certificate expired")
        return prop_def.SECUREBOOT_METHOD_EXPIRED_CERT
    end
    return prop_def.SECUREBOOT_METHOD_OK
end

-- 组装导入单个证书的json对象
local function makeup_import_item(cert_string, cert_type)
    local target = {}
    target[prop_def.INTERACT_KEY_OPERATION] = prop_def.CERT_OPERATION_ADD
    target[prop_def.INTERACT_KEY_CERTIFICATE_STRING] = cert_string
    target[prop_def.INTERACT_KEY_CERTIFICATE_TYPE] = cert_type
    target[prop_def.INTERACT_KEY_SIZE] = string.len(cert_string)
    return target
end

-- 创建默认的json模板，用于全量写到文件中
local function create_default_json_template()
    local database_db_obj = {}
    local database_dbx_obj = {}
    local database_obj = {}
    local secureboot_obj = {}
    -- 创建secureboot空对象
    database_db_obj[prop_def.INTERACT_KEY_CERTIFICATES] = {}
    database_dbx_obj[prop_def.INTERACT_KEY_CERTIFICATES] = {}
    database_obj[prop_def.INTERACT_KEY_SECUREBOOT_DB] = database_db_obj
    database_obj[prop_def.INTERACT_KEY_SECUREBOOT_DBX] = database_dbx_obj
    secureboot_obj[prop_def.INTERACT_KEY_SECUREBOOT_DATABASES] = database_obj
    -- 创建boot空对象
    local boot_obj = {}
    boot_obj[prop_def.INTERACT_KEY_CERTIFICATES] = {}
    boot_obj[prop_def.INTERACT_KEY_CRL] = {}
    -- 合并secureboot和boot对象
    local file_obj = {}
    file_obj[prop_def.INTERACT_KEY_SECUREBOOT] = secureboot_obj
    file_obj[prop_def.INTERACT_KEY_BOOT] = boot_obj

    return file_obj
end

-- 将待导入的证书文本写入到json文件中,type区分写入的区域
local function import_to_file(path, json_obj, type)
    local E_OK = 1
    local file_json = {}
    if bs_util.is_file_exist(path) == false or vos.get_file_length(path) == 0 then
        file_json = create_default_json_template()
    else
        file_json = bs_util.get_file_json(path)
    end
    if file_json == nil then
        return prop_def.SECUREBOOT_METHOD_ERR
    end
    if #file_json[prop_def.INTERACT_KEY_BOOT][type] >= prop_def.BIOS_MAX_CERT_NUM then
        return prop_def.SECUREBOOT_METHOD_IMPORT_LIMIT
    end
    table.insert(file_json[prop_def.INTERACT_KEY_BOOT][type], json_obj)
    if bs_util.write_file_data(path, json.encode(file_json)) ~= E_OK then
        return prop_def.SECUREBOOT_METHOD_ERR
    end
    utils_core.chmod_s(path, utils.S_IRUSR | utils.S_IWUSR)
    return prop_def.SECUREBOOT_METHOD_OK
end

-- Http证书相关接口
function boot_options_service:import_boot_certificate(ctx, cert_string, cert_type, system_id)
    local ret = check_bios_startup_state(system_id)
    local boot_option_obj = self:get_obj(system_id)
    if ret ~= prop_def.SECUREBOOT_METHOD_OK or not boot_option_obj then
        bs_util.record_operation(ctx, system_id, "Import BIOS Boot cert failed")
        error_process(ret)
    end
    if cert_type ~= CERTIFICATE_TYPE_SINGLE_PEM then
        bs_util.record_operation(ctx, system_id, "Import BIOS Boot cert failed")
            error(base_messages.PropertyValueFormatError(cert_type, "CertificateType"))
    end
    ret = check_import_cert_string(cert_string)
    if ret ~= prop_def.SECUREBOOT_METHOD_OK then
        bs_util.record_operation(ctx, system_id, "Import BIOS Boot cert failed")
        error_process(ret)
    end
    local added_json = makeup_import_item(cert_string, cert_type)
    ret = import_to_file(boot_option_obj:get_path('new_secureboot_file'),
        added_json, prop_def.INTERACT_KEY_CERTIFICATES)
    if ret ~= prop_def.SECUREBOOT_METHOD_OK then
        bs_util.record_operation(ctx, system_id, "Import BIOS Boot cert failed")
        error_process(ret)
    end
    bs_util.record_operation(ctx, system_id, "Import BIOS Boot cert successfully")
    return ret
end

-- 组装导入单个证书的json对象
local function makeup_reset_item(creset_type)
    local target = {}
    target[prop_def.INTERACT_KEY_OPERATION] = creset_type
    return target
end

-- 将待生效的重置动作记录到json文件中,并删除待导入证书,type区分操作的区域
local function reset_to_file(path, json_obj, type)
    local E_OK = 1
    local file_json = {}
    if bs_util.is_file_exist(path) == false or vos.get_file_length(path) == 0 or vos.get_file_length(path) == 1 then
        file_json = create_default_json_template()
    else
        file_json = bs_util.get_file_json(path)
    end
    if file_json == nil then
        return prop_def.SECUREBOOT_METHOD_ERR
    end
    file_json[prop_def.INTERACT_KEY_BOOT][type] = {}
    table.insert(file_json[prop_def.INTERACT_KEY_BOOT][type], json_obj)
    if bs_util.write_file_data(path, json.encode(file_json)) ~= E_OK then
        return prop_def.SECUREBOOT_METHOD_ERR
    end
    utils_core.chmod_s(path, utils.S_IRUSR | utils.S_IWUSR)
    return prop_def.SECUREBOOT_METHOD_OK
end

function boot_options_service:reset_boot_certificate(ctx, reset_type, system_id)
    local ret = check_bios_startup_state(system_id)
    local boot_option_obj = self:get_obj(system_id)
    if ret ~= prop_def.SECUREBOOT_METHOD_OK or not boot_option_obj then
        bs_util.record_operation(ctx, system_id, "Reset BIOS Boot cert failed")
        error_process(ret)
    end
    if reset_type ~= prop_def.CERT_OPERATION_DELETE and reset_type ~= prop_def.CERT_OPERATION_RESET then
        bs_util.record_operation(ctx, system_id, "Reset BIOS Boot cert failed")
        error(base_messages.PropertyValueFormatError("******", "ResetKeysType"))
    end
    local added_json = makeup_reset_item(reset_type)
    ret = reset_to_file(boot_option_obj:get_path('new_secureboot_file'),
        added_json, prop_def.INTERACT_KEY_CERTIFICATES)
    if ret ~= prop_def.SECUREBOOT_METHOD_OK then
        bs_util.record_operation(ctx, system_id, "Reset BIOS Boot cert failed")
        error_process(ret)
    end
    bs_util.record_operation(ctx, system_id, "Reset BIOS Boot cert successfully")
    return ret
end

local function check_import_crl_string(cert_string)
    if string.len(cert_string) > prop_def.CERT_MAX_SIZE then
        return prop_def.SECUREBOOT_METHOD_STRING_TOO_LONG
    end
    local ret = cert_core.verify_pem_crl(cert_string)
    if ret == -1 then
        error(custom_messages.PemFormatErr())
    end
    return prop_def.SECUREBOOT_METHOD_OK
end

function boot_options_service:import_boot_crl(ctx, cert_string, cert_type, system_id)
    local ret = check_bios_startup_state(system_id)
    local boot_option_obj = self:get_obj(system_id)
    if ret ~= prop_def.SECUREBOOT_METHOD_OK or not boot_option_obj then
        bs_util.record_operation(ctx, system_id, "Import BIOS Boot crl failed")
        error_process(ret)
    end
    if cert_type ~= CERTIFICATE_TYPE_SINGLE_PEM then
        bs_util.record_operation(ctx, system_id, "Import BIOS Boot crl failed")
        error_process(prop_def.SECUREBOOT_METHOD_INVALID_PARAM)
    end
    ret = check_import_crl_string(cert_string)
    if ret ~= prop_def.SECUREBOOT_METHOD_OK then
        bs_util.record_operation(ctx, system_id, "Import BIOS Boot crl failed")
        error_process(ret)
    end
    local added_json = makeup_import_item(cert_string, cert_type)
    ret = import_to_file(boot_option_obj:get_path('new_secureboot_file'),
        added_json, prop_def.INTERACT_KEY_CRL)
    if ret ~= prop_def.SECUREBOOT_METHOD_OK then
        bs_util.record_operation(ctx, system_id, "Import BIOS Boot crl failed")
        error_process(ret)
    end
    bs_util.record_operation(ctx, system_id, "Import BIOS Boot crl successfully")
    return ret
end

-- 重置http证书吊销列表
function boot_options_service:reset_boot_crl(ctx, reset_type, system_id)
    local ret = check_bios_startup_state(system_id)
    local boot_option_obj = self:get_obj(system_id)
    if ret ~= prop_def.SECUREBOOT_METHOD_OK or not boot_option_obj then
        bs_util.record_operation(ctx, system_id, "Reset BIOS Boot crl failed")
        error_process(ret)
    end
    if reset_type ~= prop_def.CERT_OPERATION_DELETE and reset_type ~= prop_def.CERT_OPERATION_RESET then
        bs_util.record_operation(ctx, system_id, "Reset BIOS Boot crl failed")
        error(base_messages.PropertyValueFormatError("******", "ResetKeysType"))
    end
    local added_json = makeup_reset_item(reset_type)
    ret = reset_to_file(boot_option_obj:get_path('new_secureboot_file'),
        added_json, prop_def.INTERACT_KEY_CRL)
    if ret ~= prop_def.SECUREBOOT_METHOD_OK then
        bs_util.record_operation(ctx, system_id, "Reset BIOS Boot crl failed")
        error_process(ret)
    end
    bs_util.record_operation(ctx, system_id, "Reset BIOS Boot crl successfully")
    return ret
end

-- 获取证书issuer的公共信息
local function get_issuer_common_parts(cert_detail)
    local issuer_json = {}
    issuer_json[prop_def.CERT_DETAILS_COUNTRY] = cert_detail['IssuerCountry']
    issuer_json[prop_def.CERT_DETAILS_STATE] = cert_detail['IssuerState']
    issuer_json[prop_def.CERT_DETAILS_CITY] = cert_detail['IssuerLocation']
    issuer_json[prop_def.CERT_DETAILS_ORGANIZATION] = cert_detail['IssuerOrgnization']
    issuer_json[prop_def.CERT_DETAILS_ORGANIZATION_UNIT] = cert_detail['IssuerOrgnizationUnit']
    issuer_json[prop_def.CERT_DETAILS_COMMON_NAME] = cert_detail['IssuerCommonName']
    issuer_json[prop_def.CERT_DETAILS_EMAIL] = cert_detail['IssuerEmail']
    return issuer_json
end
-- 获取证书subject的公共信息，可以与上面归一
local function get_subject_common_parts(cert_detail)
    local subject_json = {}
    subject_json[prop_def.CERT_DETAILS_COUNTRY] = cert_detail['SubjectCountry']
    subject_json[prop_def.CERT_DETAILS_STATE] = cert_detail['SubjectState']
    subject_json[prop_def.CERT_DETAILS_CITY] = cert_detail['SubjectLocation']
    subject_json[prop_def.CERT_DETAILS_ORGANIZATION] = cert_detail['SubjectOrgnization']
    subject_json[prop_def.CERT_DETAILS_ORGANIZATION_UNIT] = cert_detail['SubjectOrgnizationUnit']
    subject_json[prop_def.CERT_DETAILS_COMMON_NAME] = cert_detail['SubjectCommonName']
    subject_json[prop_def.CERT_DETAILS_EMAIL] = cert_detail['SubjectEmail']
    return subject_json
end

-- 根据SSL接口返回的Usage值，转换成字符串
local function parse_cert_usage(keyusage)
    local keyusage_str = {}
    local key_pair = {
        [prop_def.KU_DIGITAL_SIGNATURE] = prop_def.CERT_DETAILS_KEY_USAGE_DIGITAL_SIGNATURE,
        [prop_def.KU_NON_REPUDIATION] = prop_def.CERT_DETAILS_KEY_USAGE_NON_REPUDIATION,
        [prop_def.KU_KEY_ENCIPHERMENT] = prop_def.CERT_DETAILS_KEY_USAGE_KEY_ENCIPHERMENT,
        [prop_def.KU_DATA_ENCIPHERMENT] = prop_def.CERT_DETAILS_KEY_USAGE_DATA_ENCIPHERMENT,
        [prop_def.KU_KEY_AGREEMENT] = prop_def.CERT_DETAILS_KEY_USAGE_KEY_AGREEMENT,
        [prop_def.KU_KEY_CERT_SIGN] = prop_def.CERT_DETAILS_KEY_USAGE_KEY_CERT_SIGN,
        [prop_def.KU_CRL_SIGN] = prop_def.CERT_DETAILS_KEY_USAGE_CRL_SGINING,
        [prop_def.KU_ENCIPHER_ONLY] = prop_def.CERT_DETAILS_KEY_USAGE_ENCIPHER_ONLY,
        [prop_def.KU_DECIPHER_ONLY] = prop_def.CERT_DETAILS_KEY_USAGE_DECIPHER_ONLY
    }
    for k, v in pairs(key_pair) do
        if k & keyusage ~= 0 then
            table.insert(keyusage_str, v)
        end
    end
    return keyusage_str
end

local function parse_cert_info(cert_array)
    local formatted_array = {}
    local single = {}
    for _, cert in pairs(cert_array) do
        if cert[prop_def.INTERACT_KEY_CERTIFICATE_STRING] ~= nil then
            single[prop_def.INTERACT_KEY_CERTIFICATE_STRING] = cert[prop_def.INTERACT_KEY_CERTIFICATE_STRING]
            single[prop_def.INTERACT_KEY_CERTIFICATE_TYPE] = cert[prop_def.INTERACT_KEY_CERTIFICATE_TYPE]
            single[prop_def.INTERACT_KEY_UEFI_SIGNATUREOWNER] = cert[prop_def.INTERACT_KEY_UEFI_SIGNATUREOWNER]
            local cert_detail = cert_core.get_cert_info_from_mem(cert[prop_def.INTERACT_KEY_CERTIFICATE_STRING])
            single[prop_def.CERT_DETAILS_ISSUER] = get_issuer_common_parts(cert_detail)
            single[prop_def.CERT_DETAILS_SUBJECT] = get_subject_common_parts(cert_detail)
            single[prop_def.CERT_DETAILS_VALID_NOT_BEFORE] = os.date('%b %d %Y UTC', cert_detail['ValidNotBefore'])
            single[prop_def.CERT_DETAILS_VALID_NOT_AFTER] = os.date('%b %d %Y UTC', cert_detail['ValidNotAfter'])
            if cert_detail['KeyUsage'] ~= nil and cert_detail['KeyUsage'] ~= 0 then
                single[prop_def.CERT_DETAILS_KEY_USAGE] = parse_cert_usage(cert_detail['KeyUsage'])
            end
            single[prop_def.CERT_DETAILS_SERIAL_NUMBER] = cert_detail['SerialNumber']
            single[prop_def.CERT_DETAILS_FINGER_PRINT] = cert_detail['Fingerprint']
            single[prop_def.CERT_DETAILS_FINGER_PRINT_HASH_ALGO] = 'TPM_ALG_SHA256'
            single[prop_def.CERT_DETAILS_SIGNATURE_ALGO] = cert_detail['SignatureAlgorithm']
            table.insert(formatted_array, single)
        end
    end
    return formatted_array
end

local function parse_crl_info(cert_array)
    local formatted_array = {}
    local single = {}
    for _, cert in pairs(cert_array) do
        if cert[prop_def.INTERACT_KEY_CERTIFICATE_STRING] ~= nil then
            single[prop_def.INTERACT_KEY_CERTIFICATE_STRING] = cert[prop_def.INTERACT_KEY_CERTIFICATE_STRING]
            single[prop_def.INTERACT_KEY_CERTIFICATE_TYPE] = cert[prop_def.INTERACT_KEY_CERTIFICATE_TYPE]
            single[prop_def.INTERACT_KEY_UEFI_SIGNATUREOWNER] = cert[prop_def.INTERACT_KEY_UEFI_SIGNATUREOWNER]
            table.insert(formatted_array, single)
        end
    end
    return formatted_array
end

-- 将bios上报的文件里的信息转换成目标格式
local function transform_certificate_json(current_json)
    local target_json = {}
    if current_json[prop_def.INTERACT_KEY_BOOT] == nil or
        current_json[prop_def.INTERACT_KEY_BOOT][prop_def.INTERACT_KEY_CERTIFICATES] == nil or
        current_json[prop_def.INTERACT_KEY_BOOT][prop_def.INTERACT_KEY_CRL] == nil then
        log:error("invalid active certificate json")
        return nil
    end
    target_json[prop_def.INTERACT_KEY_BOOT] = {}
    target_json[prop_def.INTERACT_KEY_BOOT][prop_def.HTTPSBOOT_CERT] = {}
    target_json[prop_def.INTERACT_KEY_BOOT][prop_def.HTTPSBOOT_CRL] = {}
    target_json[prop_def.INTERACT_KEY_BOOT][prop_def.HTTPSBOOT_CERT] =
        parse_cert_info(current_json[prop_def.INTERACT_KEY_BOOT][prop_def.INTERACT_KEY_CERTIFICATES])
    target_json[prop_def.INTERACT_KEY_BOOT][prop_def.HTTPSBOOT_CRL] =
        parse_crl_info(current_json[prop_def.INTERACT_KEY_BOOT][prop_def.INTERACT_KEY_CRL])
    return target_json
end

-- 获取已生效证书的详细信息，按要求格式返回
function boot_options_service:get_boot_certificates(ctx, system_id)
    local ret = check_bios_startup_state(system_id)
    local boot_option_obj = self:get_obj(system_id)
    if ret ~= prop_def.SECUREBOOT_METHOD_OK or not boot_option_obj then
        log:debug('[bios]get boot certificate: state invalid')
        return
    end
    local path = boot_option_obj:get_path('current_secureboot_file')
    if not vos.get_file_accessible(path) then
        log:debug('[bios]get boot certificate: file no accessible')
        return
    end
    local current_json = bs_util.get_file_json(path)
    if current_json == nil then
        log:debug('[bios]get boot certificate: file json is nil')
        return
    end
    local target_json = transform_certificate_json(current_json)
    if target_json == nil then
        error_process(prop_def.SECUREBOOT_METHOD_ERR)
    end
    local target_str = json.encode(target_json)
    if target_str == nil then 
        error_process(prop_def.SECUREBOOT_METHOD_ERR)
    end
    return target_str
end

function boot_options_service:get_auto_clear_valid()
    local boot_option_obj = self:get_obj(1)
    if boot_option_obj then
        return boot_option_obj:get_auto_clear_valid()
    end
end

function boot_options_service:save_ipmi_boot_config(params_info)
    local boot_option_obj = self:get_obj(1)
    if boot_option_obj then
        return boot_option_obj:save_ipmi_data(params_info)
    end
end

function boot_options_service:update_boot_info(params_info)
    local boot_option_obj = self:get_obj(1)
    if boot_option_obj then
        return boot_option_obj:update_boot_info(params_info)
    end
end

function boot_options_service:collect_json_file(path, system_id)
end

function boot_options_service:get_dump_info(system_id)
    local obj = self:get_obj(system_id)
    if obj then
        return obj:get_dump_info()
    end
end

function boot_options_service:create_option_countdown_task(ctx, StartOption)
    if self:option_task_running()then
        self:option_clear_task()
    end

    -- 保存数据
    self.option_ctx = ctx
    self.StartOption = StartOption

    -- 启动倒计时60s任务，时间到则执行清除数据
    self.option_count_down_task = tasks.get_instance():timeout_ms(60 * 1000, function()
        self:option_clear_task()
        log:notice('power status not change, revoke set option')
    end)
end

function boot_options_service:on_option_power_state_changed()
    if not self:option_task_running() then
        log:error('boot_options_service: exist task running.')
        return
    end

    local ok, res = pcall(function ()
        return self:set_start_option_ext(self.option_ctx, self.StartOption, bios_enum.SINGLE_HOST_SYSTEM_ID)
    end)
    if not ok or not res then
        log:error('set StartOption fail, res = %s', res)
    end

    self:option_clear_task()
    log:notice('[boot_options_service]boot options service task detected power state changed, execute success')
end

function boot_options_service:option_task_running()
    return self.StartOption and self.option_count_down_task and self.option_count_down_task:is_running()
end

function boot_options_service:option_clear_task()
    self.option_ctx = nil
    self.StartOption = nil
    if self.option_count_down_task then
        self.option_count_down_task:cancel()
        self.option_count_down_task = nil
    end
end

function boot_options_service:create_times_countdown_task(ctx, StartOptionFlag)
    if self:times_task_running()then
        self:times_clear_task()
    end

    -- 保存数据
    self.times_ctx = ctx
    self.StartOptionFlag = StartOptionFlag

    -- 启动倒计时60s任务，时间到则执行清除数据
    self.times_count_down_task = tasks.get_instance():timeout_ms(60 * 1000, function()
        self:times_clear_task()
        log:notice('power status not change, revoke set effective time')
    end)
end

function boot_options_service:on_times_power_state_changed()
    if not self:times_task_running() then
        log:error('boot_options_service: exist task running.')
        return
    end

    local ok, res = pcall(function ()
        return self:set_start_option_flag_ext(self.times_ctx, self.StartOptionFlag, bios_enum.SINGLE_HOST_SYSTEM_ID)
    end)
    if not ok or not res then
        log:error('set effective times fail, res = %s', res)
    end

    self:times_clear_task()
    log:notice('[boot_options_service]boot options service task detected power state changed, execute success')
end

function boot_options_service:times_task_running()
    return self.StartOptionFlag and self.times_count_down_task and self.times_count_down_task:is_running()
end

function boot_options_service:times_clear_task()
    self.times_ctx = nil
    self.StartOptionFlag = nil
    if self.times_count_down_task then
        self.times_count_down_task:cancel()
        self.times_count_down_task = nil
    end
end

function boot_options_service:get_start_option_flag(system_id)
    local boot_ser = bios_factory.get_service('boot_service')
    return boot_ser:get_prop("BootSourceOverrideEnabled", system_id)
end

function boot_options_service:get_start_option(system_id)
    local boot_ser = bios_factory.get_service('boot_service')
    return boot_ser:get_prop("BootSourceOverrideTarget", system_id)
end

function boot_options_service:get_boot_order(system_id)
    local boot_ser = bios_factory.get_service('boot_service')
    return boot_ser:get_prop("BootOrder", system_id)
end

function boot_options_service:set_boot_type_order_1st(ctx, boot_order_option, system_id)
    local boot_ser = bios_factory.get_service('boot_service')
    boot_ser:set_prop("PreviousBootSourceOverrideTarget", boot_order_option, system_id)

    local order_table = self:get_boot_order(system_id)
    local boot_order1 = boot_def.start_option_to_order[boot_order_option]
    local boot_order_json = self:modify_start_order(order_table, boot_order1)
    boot_ser:set_boot_priority(ctx, boot_order_json, system_id)
end

function boot_options_service:modify_start_order(order_table, boot_order1)
    if #order_table == 0 or not boot_order1 then
        log:error("boot_options_service: the length of order_table is (%s) and boot_order1 is (%s)", #order_table, boot_order1)
        error(base_messages.InternalError())
    end

    for i = 1, #order_table do
        if boot_order1 == order_table[i] then
            order_table[1], order_table[i] = boot_order1, order_table[1]
        end
    end

    local boot_order_tbl = {}
    for i = 1, #order_table do
        boot_order_tbl[string.format('BootTypeOrder%s', i - 1)] = order_table[i]
    end
    return json.encode(boot_order_tbl)
end

local trans_user_map = {
    ['on'] = '\x00',
    ['off'] = '\x08'
}

function boot_options_service:import(set_value)
    local data = trans_user_map[set_value]
    if not data then
        log:error('Import(BMCSet_BootFlagValidTimeoutAutoClearEnabled) failed, value is %s', set_value)
        return
    end
    local boot_option_obj = self:get_obj(bios_enum.SINGLE_HOST_SYSTEM_ID)
    local result = boot_option_obj:parse_ipmi_data(boot_def.BIOS_BOOT_FLAG_VALID_CMD, data)
    if result == boot_def.COMP_CODE_SUCCESS then
        log:notice('Import(BMCSet_BootFlagValidTimeoutAutoClearEnabled) successfully, value is %s', set_value)
    else
        log:error('Import(BMCSet_BootFlagValidTimeoutAutoClearEnabled) failed, value is %s', set_value)
    end
end

function boot_options_service:export()
    local is_auto_clear = self:get_auto_clear_valid()
    return is_auto_clear and 'on' or 'off'
end

return singleton(boot_options_service)