-- 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: 网卡升级过程处理

local log = require 'mc.logging'
local utils = require 'mc.utils'
local parser_cfg = require 'upgrade.parser_cfg'
local upgrade_defs = require 'upgrade.upgrade_defs'
local c_network_adapter_firmware = require 'upgrade.network_adapter_firmware_object'
local fructl = require 'infrastructure.fructl'
local vos = require 'utils.vos'
local context = require 'mc.context'
local utils_core = require 'utils.core'
local client = require 'network_adapter.client'
local upgrade_flash = require 'upgrade.upgrade_flash'

local upgrade_process = {}

-- 初始化设置网卡路由
function upgrade_process.initial_set_route()
    c_network_adapter_firmware.collection:fold(function(acc, obj, key)
        if obj.Protocol == upgrade_defs.PROTOCOL_NIC_SFC then
            upgrade_flash.set_network_adapter_firmware_route(obj, obj.DefaultRoute)
        end
    end, {})
end

-- 获取升级前版本号
local function network_adapter_get_upgrade_before_version(cfg)
    if not cfg then
        return ''
    end

    local version = ''
    c_network_adapter_firmware.collection:fold(function(acc, obj, key)
        -- 检查当前对象是否匹配
        if obj.ComponentID == cfg.component_id and obj.ComponentIDEx == cfg.component_idex then
            log:notice("check network adapter firmware id(%s) id_ex(%s) fw_ver(%s) exist success",
                obj.ComponentID, obj.ComponentIDEx, obj.FirmwareVersion)
            version = obj.FirmwareVersion
        end
    end, {})

    if version == '' then
        log:error("[Network Adapter Upgrade] get before version failed")
    end
    return version
end

-- 更新电源状态
local function update_power_state(self)
    self.power_state = fructl.get_power_status()
    log:notice('[Nic] power state: %s', self.power_state)
end

-- 网卡升级对下电的Host增加上电锁
local function nic_upgrade_poweron_lock(self, system_id)
    -- 网卡下电升级且还未加上电锁，需加上电锁
    if self.power_state == 'OFF' and not self.poweron_locked then
        log:notice('[Nic] Adding poweron lock for system %d', system_id)
        -- 上电锁两小时
        fructl.set_poweron_lock_until_success(self.bus, system_id, true, upgrade_defs.LOCK_FOREVER, 'Nic')
        self.poweron_locked = true

        -- 加锁后再次检查电源状态
        local current_power_state = fructl.get_power_status()
        if current_power_state ~= 'OFF' then
            -- 如果加了上电锁之后环境处于上电状态，则解锁；此次升级不写flash，改为缓存
            log:notice('[Nic] System %d powered on after lock, unlocking', system_id)
            fructl.set_poweron_lock_until_success(self.bus, system_id, false, upgrade_defs.LOCK_FOREVER, 'Nic')
            self.poweron_locked = false
            self.power_state = current_power_state
        end
    end
end

-- 升级准备处理
function upgrade_process.network_adapter_upgrade_prepare(self, system_id, cfg_path, hpm_path)
    self.is_network_adapter_upgrading = true

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

    -- 获取升级前版本号
    local version = ''
    for _, cfg in pairs(self.cfgs) do
        version = network_adapter_get_upgrade_before_version(cfg)
        if version ~= '' then
            break
        end
    end

    -- 更新电源状态并加上电锁
    update_power_state(self)
    nic_upgrade_poweron_lock(self, system_id)

    self.hpm_path = hpm_path
    self.before_upgrade_version = version
    log:notice('[Network Adapter Upgrade] Prepare network adapter upgrade finish, old version is %s', version)
    return upgrade_defs.RET.OK, version
end

-- 缓存升级文件
local function cache_upgrade_file(hpm_path)
    if not hpm_path or not vos.get_file_accessible(hpm_path) then
        log:error('[Nic] cache_upgrade_file: hpm path is not accessible')
        return upgrade_defs.RET.ERR
    end

    -- 创建缓存目录
    local cache_dir = upgrade_defs.UPGRADE_NIC_DIR
    if not vos.get_file_accessible(cache_dir) then
        os.execute('mkdir -p ' .. cache_dir)
    end

    local target_path = string.format('%s%s', cache_dir, upgrade_defs.UPGRADE_NIC_FILE_NAME)
    local ok, err = pcall(utils.copy_file, hpm_path, target_path)
    if not ok then
        log:error('[Nic] Failed to cache hpm file, err: %s', err)
        return upgrade_defs.RET.ERR
    end
    log:notice('[Nic] Cached hpm file successfully')
    return upgrade_defs.RET.OK
end

-- 缓存网卡升级包（上电状态）
local function cache_nic_hpm(self)
    if self.power_state ~= 'OFF' then
        log:notice('[Nic] System is powered on, caching upgrade file')
        cache_upgrade_file(self.hpm_path)
        return true
    end
    return false
end

-- 升级执行处理
function upgrade_process.network_adapter_upgrade_process(self, system_id, firmware_type, file_path, parameters)
    -- 针对prepare阶段和process阶段上下电状态不一致的场景，避免写入固件时未加上电锁
    update_power_state(self)
    nic_upgrade_poweron_lock(self, system_id)

    -- 上电状态时缓存网卡升级包
    if cache_nic_hpm(self) then
        return upgrade_defs.RET.OK
    end

    -- 执行网卡固件升级
    local ok, upgrade_ret = pcall(function()
        return upgrade_flash.network_adapter_upgrade_flash(self, file_path)
    end)
    if not ok then
        log:error('[Network Adapter Upgrade] upgrade flash failed, ret = %s', upgrade_ret)
        return upgrade_defs.RET.ERR
    end

    return upgrade_ret
end

-- 填充生效注册列表
local function fill_register_active_list()
    return {
        { Key = 'FirmwareId',      Value = 'Nic' },
        { Key = 'FirmwareType',    Value = 'Nic' },
        { Key = 'ActiveCondition', Value = 'PowerOff' },
        { Key = 'ActiveMode',      Value = 'None' },
        { Key = 'ActiveStatus',    Value = 'Idle' }
    }
end

-- 注册网卡生效任务
local function register_network_adapter_active_action()
    -- 判断是否存在网卡升级包缓存文件
    local cache_dir = upgrade_defs.UPGRADE_NIC_DIR
    if not vos.get_file_accessible(cache_dir) or not next(utils_core.dir(cache_dir)) then
        log:notice('[Network Adapter Upgrade] No cached nic hpm found, dont register nic active')
        return
    end

    local active_register_list = fill_register_active_list()
    client:FirmwareActiveFirmwareActiveRegisterActiveAction(context.new(), active_register_list)
    log:notice('[Network Adapter Upgrade] Register active action successfully, ActiveCondition = PowerOff')
end

-- 升级完成处理
function upgrade_process.network_adapter_upgrade_finish(self)
    log:notice('[Network Adapter Upgrade] Upgrade finish start')
    -- 网卡注册生效策略
    local ok, err = pcall(function()
        return register_network_adapter_active_action()
    end)
    if not ok then
        log:error('[Network Adapter Upgrade] Try to register active action failed, err: %s', err)
    end
    return upgrade_defs.RET.OK
end

return upgrade_process
