-- 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.
local skynet = require 'skynet'

local singleton = require 'mc.singleton'
local log = require 'mc.logging'
local class = require 'mc.class'
local ctx = require 'mc.context'
local mdb = require 'mc.mdb'
local c_object_manage = require 'mc.orm.object_manage'

local comm_defs = require 'device.class.nic_mgmt.comm_defs'
local c_network_adapter = require 'device.class.network_adapter'

local client = require 'network_adapter.client'

local firmware_objs_mgmt = class()
firmware_objs_mgmt.__index = firmware_objs_mgmt

-- 重试次数和时间间隔
local MAX_RETRY_TIMES <const> = 30  -- 重试30次，每次1000ms，总共30秒
local RETRY_INTERVAL <const> = 100   -- 每次间隔1秒（1000ms） skynet.sleep的单位是10ms
local OBJECT_IDENTIFIER_POSITION_INDEX <const> = 4   -- 对象的Position属性在ObjectIdentifier表的第4个位置

function firmware_objs_mgmt:init()
    self.upgradable_cards = {}  -- 在Position位置保存NetworkAdapterFirmware对象
    log:notice('[network_adapter] firmware_objs_mgmt init finished')
end

local function retry_update_firmware_info(firmware_id, version)
    skynet.fork_once(function()
        local retries = 0
        local max_retries = 3 -- 重试3次
        local retry_interval = 500 -- 每次间隔5秒（5000ms） skynet.sleep的单位是10ms
        local ok, obj
        while retries < max_retries do
            ok, obj = pcall(mdb.get_cached_object, c_object_manage.get_instance().bus,
                comm_defs.FWINVENTORY_PATH_PREFIX .. firmware_id, comm_defs.FIRMWARE_INFO_INTERFACE)
            if ok and obj then
                obj.Version = version
                log:notice('[network_adapter] %s update firmware info version to %s', firmware_id, version)
                break
            end

            retries = retries + 1
            if retries < max_retries then
                log:info('[network_adapter] get %s firmware info failed, retry %s/%s, err:%s',
                    firmware_id, retries, max_retries, obj)
                skynet.sleep(retry_interval)
            else
                log:error('[network_adapter] get %s firmware info failed after %s retries, path:%s err:%s',
                    firmware_id, max_retries, comm_defs.FWINVENTORY_PATH_PREFIX .. firmware_id, obj)
                return
            end
        end
    end)
end

local function retry_register_firmware_info(firmware_info_table, updateable, capability, max_package_size_mib, success_callback)
    skynet.fork(function()
        local retries = 0
        local max_retries = 3 -- 重试3次
        local retry_interval = 500 -- 每次间隔5秒（5000ms） skynet.sleep的单位是10ms
        local ok, rsp
        -- 确保参数表包含必要的字段
        local param = {
            Id = firmware_info_table.Id or '',
            Name = firmware_info_table.Name or '',
            Version = firmware_info_table.Version or '',
            BuildNum = firmware_info_table.BuildNum or '',
            ReleaseDate = firmware_info_table.ReleaseDate or '',
            LowestSupportedVersion = firmware_info_table.LowestSupportedVersion or '',
            SoftwareId = firmware_info_table.SoftwareId or '',
            Manufacturer = firmware_info_table.Manufacturer or '',
            Location = firmware_info_table.Location or '',
            State = firmware_info_table.State or 'Enabled',
            Severity = firmware_info_table.Severity or 'Informational',
            EstimatedDuration = firmware_info_table.EstimatedDuration or '',
            FirmwareType = firmware_info_table.FirmwareType or ''
        }
        while retries < max_retries do
            ok, rsp = client:PFirmwareInventoryFirmwareInventoryAdd(ctx.new(), param, updateable, capability, max_package_size_mib)
            if ok then
                log:notice('[network_adapter] register firmware info success, Id=%s, Version=%s', param.Id, param.Version)
                -- 注册成功后调用回调函数
                if success_callback then
                    success_callback()
                end
                return
            end

            retries = retries + 1
            if retries < max_retries then
                log:info('[network_adapter] %s register firmware info failed, retry %s/%s, err:%s', param.Id, retries, max_retries, rsp)
                skynet.sleep(retry_interval)
            else
                log:error('[network_adapter] %s register firmware info failed after %s retries, Id=%s, err:%s', param.Id, max_retries, param.Id, rsp)
                return
            end
        end

    end)
end

local function retry_unregister_firmware_info(firmware_info_id)
    -- 查找网卡对象并取消固件信息注册
    skynet.fork(function()
        local retry_count = 0
        while retry_count < MAX_RETRY_TIMES do
            local ok, rsp = client:PFirmwareInventoryFirmwareInventoryDelete(ctx.new(), firmware_info_id)
            if ok then
                log:notice('[network_adapter] unregister firmware info success, Id=%s', firmware_info_id)
                return
            end
            
            retry_count = retry_count + 1
            if retry_count < MAX_RETRY_TIMES then
                log:info('[network_adapter] unregister firmware info failed, retry %s/%s, err:%s, Id=%s', retry_count, MAX_RETRY_TIMES, rsp, firmware_info_id)
                skynet.sleep(RETRY_INTERVAL)
            else
                log:error('[network_adapter] unregister firmware info failed after %s retries, err:%s, Id=%s', MAX_RETRY_TIMES, rsp, firmware_info_id)
                return
            end
        end
    end)
end

local function register_firmware_info_by_firmware_version(network_adapter, firmware_id, firmware_version)
    local data = {
        Id = firmware_id,
        Name = network_adapter.NodeId,
        Version = firmware_version,
        Manufacturer = network_adapter.Manufacturer,
        State = 'Enabled',
        Severity = 'Informational',
        FirmwareType = "NIC_FIRMWARE",
    }
    -- 注册成功后设置标志位
    retry_register_firmware_info(data, true, 4, 10, function()
        network_adapter.register_firmware_info_flag = true
    end)
end

local function get_firmware_id(network_adapter)
    local part1, part2, part3 = string.match(network_adapter.NodeId, "^(%a+)(%d+)%(([^%)]+)%)$")
    if part1 and part2 and part3 then
        return part1 .. "_" .. part2 .. "_" .. part3
    end
    return network_adapter.NodeId:gsub("[()]", "")
end

local function register_firmware_info(network_adapter, firmware_id)
    -- 注册新的回调并保存槽位
    network_adapter.on_property_changed:on(function(prop_name, prop_value)
        if prop_name ~= 'FirmwareVersion' then
            return
        end
        if network_adapter.register_firmware_info_flag then
            retry_update_firmware_info(firmware_id, prop_value)
            return
        end
        register_firmware_info_by_firmware_version(network_adapter, firmware_id, prop_value)
    end)
end

-- 支持升级的网卡，每一个网卡都会执行一次
function firmware_objs_mgmt:on_add_firmware_object(class_name, firmware_obj, position)
    if class_name ~= comm_defs.UPGRADABLE_FIRMWARE_OBJ then
        return
    end
    log:notice('[network_adapter] Add object start, class: %s, position: %s', class_name, position)
    -- 等待网卡对象加载完成后再注册
    skynet.fork(function()
        -- 等待对象加载
        local retry_count = 0
        local network_adapter_obj = nil
        while retry_count < MAX_RETRY_TIMES do
            c_network_adapter.collection:fold(function (_, obj)
                if obj.ObjectIdentifier[OBJECT_IDENTIFIER_POSITION_INDEX] == position then
                    network_adapter_obj = obj
                end
            end)
            if network_adapter_obj then
                break
            end
            skynet.sleep(RETRY_INTERVAL)
            retry_count = retry_count + 1
        end

        if network_adapter_obj then
            local firmware_id = get_firmware_id(network_adapter_obj)
            self.upgradable_cards[position] = {
                firmware_obj = firmware_obj,
                firmware_id = firmware_id
            }
            register_firmware_info(network_adapter_obj, firmware_id)
        else
            log:warn('[network_adapter] failed to find network_adapter at position %s after %s retries when register object', position, MAX_RETRY_TIMES)
        end
    end)
end

function firmware_objs_mgmt:on_delete_firmware_object(class_name, _, position)
    if class_name ~= comm_defs.UPGRADABLE_FIRMWARE_OBJ then
        return
    end
    log:notice('[network_adapter] Delete object start, class: %s, position: %s', class_name, position)
    local firmware_info_id = self.upgradable_cards[position] and self.upgradable_cards[position].firmware_id
    self.upgradable_cards[position] = nil
    if not firmware_info_id then
        return
    end
    retry_unregister_firmware_info(firmware_info_id)
end

return singleton(firmware_objs_mgmt)