-- Copyright (c) 2026 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.

-- Description: RAID控制器升级处理

local skynet = require 'skynet'
local log = require 'mc.logging'
local context = require 'mc.context'
local utils = require 'mc.utils'
local class = require 'mc.class'
local singleton = require 'mc.singleton'
local client = require 'storage.client'
local parser_cfg = require 'upgrade.parser_cfg'
local upgrade_defs = require 'upgrade.upgrade_defs'
local firmware_objs = require 'controller_firmware.controller_firmware_object'
local c_storageconfig = require 'storageconfig.storageconfig_object'
local sml = require 'sml'

local SMBIOS_WRITE_FINISH = 3

local controller_upgrade = class()

-- 检查是否是RAID控制器相关的固件类型
local function is_controller_firmware(firmware_type)
    return firmware_type == 'Raid'
end

local function get_upgrade_list(cfg)
    if not cfg then
        return nil
    end
    return firmware_objs.get_instance():find_upgradable_raid_firmware_obj(cfg.component_id, cfg.component_idex)
end

local function clear_controller_upgrade_resource(self)
    self.cfgs = {}
    self.upgrade_list = {}
    self.before_upgrade_version = ''
    self.is_controller_upgrading = false
    self.upgrade_controller_count = 0
end

-- 检查是否可以执行RAID卡升级
local function check_upgrade_preconditions(self)
    -- 检查是否已经在升级中
    if self.is_controller_upgrading then
        log:error('[Controller Upgrade] firmware is upgrading')
        return upgrade_defs.RET.IN_UPGRADING
    end

    -- 检查电源和SMBIOS状态
    local smbios_status = c_storageconfig.get_instance().smbios_status
    local power_state = c_storageconfig.get_instance().power_state
    if smbios_status ~= SMBIOS_WRITE_FINISH or power_state ~= 'ON' then
        log:error('[Controller Upgrade] system not ready: smbios_status = %s, power_state = %s',
            smbios_status, power_state)
        return upgrade_defs.RET.ERR
    end

    -- 检查是否有可升级的 RAID 控制器
    if not firmware_objs.get_instance():has_valid_upgradable_raid() then
        log:error('[Controller Upgrade] no controller with OOB support found')
        return upgrade_defs.RET.ERR
    end
    return upgrade_defs.RET.OK
end

local function controller_upgrade_prepare(self, cfg_path)
    self.is_controller_upgrading = true

    -- 保存升级配置信息
    self.cfgs = parser_cfg.get_cfgs(cfg_path)

    -- 获取升级前版本号
    for _, cfg in pairs(self.cfgs) do
        version = firmware_objs.get_instance():get_cfg_match_controller_version(cfg.component_id, cfg.component_idex)
        if version ~= '' then
            break
        end
    end

    self.before_upgrade_version = version
    log:notice('[Controller Upgrade] Prepare controller upgrade finish, old version is %s', version)
    return upgrade_defs.RET.OK, version
end

-- 升级准备阶段
function controller_upgrade:on_upgrade_prepare(_, system_id, firmware_type, cfg_path, _, parameters)
    -- 只处理RAID控制器类型
    if not is_controller_firmware(firmware_type) then
        return
    end

    -- 检查是否可以执行升级
    local ret = check_upgrade_preconditions(self)
    if ret ~= upgrade_defs.RET.OK then
        client:UpdateServiceUpdateServicePrepareReply(context.new(), system_id, firmware_type, '',
            ret, parameters)
        return
    end

    log:notice('[Controller Upgrade] prepare phase: system_id = %s, firmware_type = %s', system_id, firmware_type)
    local ok, rsp = client:PFileFileChown(context.new(), nil, cfg_path, upgrade_defs.SECBOX_UID, upgrade_defs.SECBOX_GID)  --设置配置文件属主为secbox
    if not ok then
        log:error('[Controller Upgrade] chown cfg file failed, error %s', rsp)
        client:UpdateServiceUpdateServicePrepareReply(context.new(), system_id, firmware_type, '', upgrade_defs.RET.ERR,
            parameters)
        return
    end

    skynet.fork(function ()
        local ret_ok, ret_code, version_str = pcall(function ()
            return controller_upgrade_prepare(self, cfg_path)
        end)
        if not ret_ok then
            log:error('[Controller Upgrade] call controller upgrade prepare failed, err: %s', ret_code)
            ret_code = upgrade_defs.RET.ERR
            version_str = ''
        end

        if ret_code ~= upgrade_defs.RET.OK then
            clear_controller_upgrade_resource(self)
            log:error('[Controller Upgrade] upgrade prepare failed, firmware_type:%s, ret_ok:%s, ret_code:%s', firmware_type,
                tostring(ret_ok), ret_code)
        end
        client:UpdateServiceUpdateServicePrepareReply(context.new(), system_id, firmware_type, version_str, ret_code,
            parameters)
    end)
end

local function update_progress_range(system_id, firmware_type, parameters,
    start_percent, end_percent, current_step, total_steps)
    if not total_steps or total_steps <= 0 then
        return
    end

    if current_step < 0 then
        current_step = 0
    elseif current_step > total_steps then
        current_step = total_steps
    end

    local range = end_percent - start_percent
    local percent = start_percent + range * current_step / total_steps
    percent = math.floor(percent + 0.5)

    client:UpdateServiceUpdateServiceUpdateUpgradeStatus(context.new(), system_id, firmware_type,
        0, percent, 'Running', parameters)
end

local function controller_upgrade_process(self, system_id, firmware_type, file_path, parameters)
    -- 解析出升级目录和文件名
    local dir, firmware_name = parser_cfg.parse_dir(file_path)
    -- 解析出文件路径对应的cfg配置文件，并获取升级列表
    self.upgrade_list = get_upgrade_list(self.cfgs[firmware_name])
    if not next(self.upgrade_list) then
        log:error('[Controller Upgrade] No fw need to upgrade during process stage')
        return upgrade_defs.RET.ERR
    end

    -- 解压固件，最大解压限制100M
    utils.secure_tar_unzip(file_path, dir, 0x6400000, 1024)
    dir = dir .. "packet_raid.img"
    client:UpdateServiceUpdateServiceUpdateUpgradeStatus(context.new(), system_id, firmware_type,
    0, 20, 'Running', parameters)
    local upgrade_times = #self.upgrade_list
    for index, upgrade_obj in pairs(self.upgrade_list) do
        log:notice('[Controller Upgrade] Controller_ID:%s', upgrade_obj.ref_controller.Id)
        local ok, upgrade_ret = pcall(sml.upgrade_ctrl_process, upgrade_obj.ref_controller.Id, dir)
        if ok and upgrade_ret == upgrade_defs.RET.OK then
            log:notice('[Controller Upgrade] Controller_ID:%s upgrade success', upgrade_obj.ref_controller.Id)
            self.upgrade_controller_count = self.upgrade_controller_count + 1
        else
            log:error('[Controller Upgrade] Controller_ID:%s upgrade failed, ret = %s', upgrade_obj.ref_controller.Id, upgrade_ret)
        end
        update_progress_range(system_id, firmware_type, parameters, 20, 90, index, upgrade_times)
    end

    if self.upgrade_controller_count == 0 then
        log:error('[Controller Upgrade] no one controller upgrade success')
        return upgrade_defs.RET.ERR
    end
    return upgrade_defs.RET.OK
end

-- 升级执行阶段
function controller_upgrade:on_upgrade_process(_, system_id, firmware_type, file_path, parameters)
    -- 只处理RAID控制器类型
    if not is_controller_firmware(firmware_type) then
        return
    end

    log:notice('[Controller Upgrade] process phase: system_id = %s, type = %s, file = %s',
        system_id, firmware_type, file_path)
    if not next(self.cfgs) then
        clear_controller_upgrade_resource(self)
        log:error('[Controller Upgrade] upgrade process failed, there is no cfgs, firmware_type:%s', firmware_type)
        client:UpdateServiceUpdateServiceProcessReply(context.new(), system_id, firmware_type, upgrade_defs.RET.ERR,
            parameters)
        return
    end

    local ok, rsp = client:PFileFileChown(context.new(), nil, file_path, upgrade_defs.SECBOX_UID, upgrade_defs.SECBOX_GID)  --设置配置文件属主为secbox
    if not ok then
        log:error('[Controller Upgrade] chown file failed, error %s', rsp)
        client:UpdateServiceUpdateServiceProcessReply(context.new(), system_id, firmware_type, '', upgrade_defs.RET.ERR,
            parameters)
        return
    end

    skynet.fork(function ()
        local ret_ok, ret_code = pcall(function ()
            return controller_upgrade_process(self, system_id, firmware_type, file_path, parameters)
        end)
        if not ret_ok then
            log:error('[Controller Upgrade] call controller upgrade process failed, err: %s', ret_code)
            ret_code = upgrade_defs.RET.ERR
        end

        if ret_code ~= upgrade_defs.RET.OK then
            clear_controller_upgrade_resource(self)
            log:error('[Controller Upgrade] upgrade process failed, firmware_type:%s, ret_ok:%s, ret_code: %s', firmware_type,
                tostring(ret_ok), ret_code)
        end
        client:UpdateServiceUpdateServiceProcessReply(context.new(), system_id, firmware_type, ret_code, parameters)
        -- 升级结束
        self.is_controller_upgrading = false
        log:notice('[Controller Upgrade] process completed for controller')
    end)
end

-- 升级完成阶段
function controller_upgrade:on_upgrade_finish(_, system_id, firmware_type, parameters)
    -- 只处理RAID控制器类型
    if not is_controller_firmware(firmware_type) then
        return
    end
    clear_controller_upgrade_resource(self)
    log:notice('[Controller Upgrade] finish phase: system_id = %s, type = %s', system_id, firmware_type)
    client:UpdateServiceUpdateServiceFinishReply(context.new(), system_id, firmware_type, upgrade_defs.RET.OK, parameters)
    log:notice('[Controller Upgrade] finish completed for controller')
end

-- 初始化：订阅RAID控制器升级信号
function controller_upgrade:init()
    log:notice('[Controller Upgrade] initializing controller upgrade service')
    self.cfgs = {}
    self.upgrade_list = {}
    self.before_upgrade_version = ''
    self.is_controller_upgrading = false
    self.upgrade_controller_count = 0

    -- 监听升级生效信号
    client:SubscribeUpdateServiceUpdateServiceUpgradePrepareSignal(function (...)
        self:on_upgrade_prepare(...)
    end)
    client:SubscribeUpdateServiceUpdateServiceUpgradeProcessSignal(function (...)
        self:on_upgrade_process(...)
    end)
    client:SubscribeUpdateServiceUpdateServiceUpgradeFinishSignal(function (...)
        self:on_upgrade_finish(...)
    end)

    log:notice('[Controller Upgrade] controller upgrade service initialized')
end

return singleton(controller_upgrade)