-- 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 skynet = require 'skynet'
local log = require 'mc.logging'
local context = require 'mc.context'
local singleton = require 'mc.singleton'
local client = require 'network_adapter.client'
local upgrade_defs = require 'upgrade.upgrade_defs'
local c_network_adapter_firmware = require 'upgrade.network_adapter_firmware_object'
local fructl = require 'infrastructure.fructl'
local upgrade_process = require 'upgrade.upgrade_process'
local active_process = require 'upgrade.active_process'

local network_adapter_upgrade = {}

-- 检查是否是网卡相关的固件类型
local function is_network_adapter_firmware(firmware_type)
    return firmware_type == 'Nic'
end

-- 解除上电锁
local function poweron_unlock(self, system_id)
    if self.poweron_locked then
        log:notice('[Nic] Removing poweron lock for system %d', system_id)
        fructl.set_poweron_lock_until_success(self.bus, system_id, false, upgrade_defs.LOCK_FOREVER, 'Nic')
        self.poweron_locked = false
    end
end

local function clear_network_adapter_upgrade_resource(self)
    poweron_unlock(self, self.system_id)
    self.cfgs = {}
    self.upgrade_list = {}
    self.before_upgrade_version = ''
    self.is_network_adapter_upgrading = false
    self.is_network_adapter_activing = false
    self.hpm_path = ""
    self.power_state = nil
    self.poweron_locked = false
end

local function upgrade_file_path_chown(cfg_path, hpm_path)
    local ok, rsp = client:PFileFileChown(context.new(), nil, cfg_path, 104, 104)  --设置配置文件属主为secbox
    if not ok then
        log:error('[Network Adapter Upgrade] chown cfg file failed, error %s', rsp)
        return false
    end

    ok, rsp = client:PFileFileChown(context.new(), nil, hpm_path, 104, 104)  --设置升级文件属主为secbox
    if not ok then
        log:error('[Network Adapter Upgrade] chown hpm file failed, error %s', rsp)
        return false
    end
    return true
end

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

    -- 检查网卡固件对象是否存在
    if not c_network_adapter_firmware.collection.objects or c_network_adapter_firmware.collection.count <= 0 then
        log:error('[Network Adapter Upgrade] network adapter firmware not found')
        return upgrade_defs.RET.ERR
    end

    return upgrade_defs.RET.OK
end

-- 升级准备阶段
function network_adapter_upgrade:on_upgrade_prepare(_, system_id, firmware_type, cfg_path, hpm_path, parameters)
    -- 只处理网卡类型
    if not is_network_adapter_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('[Network Adapter Upgrade] prepare phase: system_id = %s, firmware_type = %s', system_id, firmware_type)
    local ok, rsp = upgrade_file_path_chown(cfg_path, hpm_path)
    if not ok then
        log:error('[Network Adapter 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 ()
        client:UpdateServiceUpdateServiceUpdateUpgradeStatus(context.new(),
            system_id, firmware_type, upgrade_defs.RET.OK, 20, 'Running', parameters)

        local ret_ok, ret_code, version_str = pcall(function ()
            return upgrade_process.network_adapter_upgrade_prepare(self, system_id, cfg_path, hpm_path)
        end)
        if not ret_ok then
            log:error('[Network Adapter Upgrade] call network adapter 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_network_adapter_upgrade_resource(self)
            log:error('[Network Adapter 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 check_upgrade_process_preconditions(self)
    if not next(self.cfgs) then
        log:error('[Network Adapter Upgrade] upgrade process failed, there is no cfgs')
        return upgrade_defs.RET.ERR
    end

    -- 检查电源状态
    local power_state = fructl.get_power_status()
    if not power_state then
        log:error('[Network Adapter Upgrade] system not ready: power_state is nil')
        return upgrade_defs.RET.ERR
    end
    return upgrade_defs.RET.OK
end

-- 升级执行阶段
function network_adapter_upgrade:on_upgrade_process(_, system_id, firmware_type, file_path, parameters)
    -- 只处理网卡类型
    if not is_network_adapter_firmware(firmware_type) then
        return
    end

    log:notice('[Network Adapter Upgrade] process phase: system_id = %s, type = %s, file = %s',
        system_id, firmware_type, file_path)
    local ret = check_upgrade_process_preconditions(self)
    if ret ~= upgrade_defs.RET.OK then
        clear_network_adapter_upgrade_resource(self)
        log:error('[Network Adapter Upgrade] upgrade process failed, firmware_type:%s', firmware_type)
        client:UpdateServiceUpdateServiceProcessReply(context.new(), system_id, firmware_type, upgrade_defs.RET.ERR,
            parameters)
        return
    end

    skynet.fork(function ()
        client:UpdateServiceUpdateServiceUpdateUpgradeStatus(context.new(),
            system_id, firmware_type, upgrade_defs.RET.OK, 40, 'Running', parameters)

        local ret_ok, ret_code = pcall(function ()
            return upgrade_process.network_adapter_upgrade_process(self, system_id, firmware_type, file_path, parameters)
        end)
        if not ret_ok then
            log:error('[Network Adapter Upgrade] call network adapter upgrade process failed, err: %s', ret_code)
            ret_code = upgrade_defs.RET.ERR
        end

        if ret_code ~= upgrade_defs.RET.OK then
            clear_network_adapter_upgrade_resource(self)
            log:error('[Network Adapter Upgrade] upgrade process failed, firmware_type:%s, ret_ok:%s, ret_code: %s', firmware_type,
                tostring(ret_ok), ret_code)
            log:maintenance(log.MLOG_INFO, log.FC__PUBLIC_OK, 'Upgrade %s from version %s failed',
                firmware_type, self.before_upgrade_version)
        else
            log:maintenance(log.MLOG_INFO, log.FC__PUBLIC_OK, 'Upgrade %s from version %s successfully',
                firmware_type, self.before_upgrade_version)
        end
        client:UpdateServiceUpdateServiceProcessReply(context.new(), system_id, firmware_type, ret_code, parameters)
        log:notice('[Network Adapter Upgrade] process completed for network adapter')
    end)
end

-- 升级完成阶段
function network_adapter_upgrade:on_upgrade_finish(_, system_id, firmware_type, parameters)
    -- 只处理网卡类型
    if not is_network_adapter_firmware(firmware_type) then
        return
    end

    skynet.fork(function()
        client:UpdateServiceUpdateServiceUpdateUpgradeStatus(context.new(),
            system_id, firmware_type, upgrade_defs.RET.OK, 90, 'Running', parameters)

        log:notice('[Network Adapter Upgrade] finish phase start: system_id = %s, type = %s', system_id, firmware_type)
        local ok, ret_code = pcall(function()
            return upgrade_process.network_adapter_upgrade_finish(self)
        end)

        if not ok then
            log:error('[Network Adapter Upgrade] call network adapter upgrade finish failed, ret: %s', ret_code)
            ret_code = upgrade_defs.RET.ERR
        end
        clear_network_adapter_upgrade_resource(self)
        client:UpdateServiceUpdateServiceFinishReply(context.new(), system_id, firmware_type, ret_code, parameters)
        log:notice('[Network Adapter Upgrade] network adapter upgrade finish completed')
    end)
end

local function check_upgrade_active_preconditions(self)
    if self.is_network_adapter_upgrading then
        log:error('[Network Adapter Upgrade] firmware is upgrading')
        return upgrade_defs.RET.ERR
    end

    if self.is_network_adapter_activing then
        log:error('[Network Adapter Upgrade] firmware is activing')
        return upgrade_defs.RET.ERR
    end
    return upgrade_defs.RET.OK
end

function network_adapter_upgrade:on_active_process(_, system_id, firmware_type, parameters)
    log:notice('[Network Adapter Upgrade] active process phase start: system_id = %s, type = %s', system_id, firmware_type)
    local ret = check_upgrade_active_preconditions(self)
    if ret ~= upgrade_defs.RET.OK then
        client:FirmwareActiveFirmwareActiveActiveProcessReply(context.new(), system_id, firmware_type, ret, parameters)
        return
    end

    skynet.fork(function()
        local ok, ret_code = pcall(function()
            return active_process.network_adapter_active_process(self)
        end)
        if not ok then
            log:error('[Network Adapter Upgrade] call network adapter active process failed, ret: %s', ret_code)
            ret_code = upgrade_defs.RET.ERR
        end
        -- 等待10s，防止响应过快firmware_mgmt没有收到回应
        skynet.sleep(1000)
        client:FirmwareActiveFirmwareActiveActiveProcessReply(context.new(), system_id, firmware_type, ret_code, parameters)
        log:notice('[Network Adapter Upgrade] network adapter active process completed')
    end)
end

-- 初始化：订阅网卡升级信号
function network_adapter_upgrade:init(bus)
    log:notice('[Network Adapter Upgrade] initializing network adapter upgrade service')
    self.bus = bus
    self.cfgs = {}
    self.upgrade_list = {}
    self.before_upgrade_version = ''
    self.is_network_adapter_upgrading = false
    self.is_network_adapter_activing = false
    self.hpm_path = ""
    self.power_state = nil
    self.poweron_locked = false

    -- 切换网卡通道路由到默认方式
    local ok = pcall(function()
        upgrade_process.initial_set_route()
    end)
    if not ok then
        log:error('set network adapter initial route failed')
    end

    -- 监听升级生效信号
    client:SubscribeUpdateServiceUpdateServiceUpgradePrepareSignal(function (...)
        self:on_upgrade_prepare(...)
    end)
    client:SubscribeUpdateServiceUpdateServiceUpgradeProcessSignal(function (...)
        self:on_upgrade_process(...)
    end)
    client:SubscribeUpdateServiceUpdateServiceUpgradeFinishSignal(function (...)
        self:on_upgrade_finish(...)
    end)
    client:SubscribeFirmwareActiveFirmwareActiveActiveProcessSignal(function (...)
        self:on_active_process(...)
    end)
    log:notice('[Network Adapter Upgrade] network adapter upgrade service initialized')
end

return singleton(network_adapter_upgrade)

