-- Copyright (c) 2025 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: Independent VRD管理对象，管理一相同UID的VRD芯片

local log = require 'mc.logging'
local mdb = require 'mc.mdb'
local context = require 'mc.context'
local cmn = require 'common'
local defs = require 'independent_vrd.ind_vrd_defs'
local fw_manager = require 'fw_manager'

local FWINVENTORY_PATH = '/bmc/kepler/UpdateService/FirmwareInventory'
local FWINVENTORY_INTERFACE = 'bmc.kepler.UpdateService.FirmwareInventory'
local FWINFO_INTERFACE = 'bmc.kepler.UpdateService.FirmwareInfo'

local ind_vrd_manager = {}
ind_vrd_manager.__index = ind_vrd_manager

--- 创建VRD对象
--- @param uid string VRD的唯一标识
--- @param board_type string 板卡类型
--- @param software_id string 软件ID
--- @param position string|number 位置信息
--- @return table VRD对象
function ind_vrd_manager.new(uid, board_type, software_id, position)
    local obj = {
        UID = uid,
        BoardType = board_type,
        SoftwareId = software_id or '',
        position = position,
        vrd_chips = {},          -- 存储所有芯片对象，key为VrdId
        objects_complete = false -- 对象是否获取完成
    }
    return setmetatable(obj, ind_vrd_manager)
end

--- 添加芯片对象到中
--- @param vrd_id number VRD芯片ID（从1递增）
--- @param chip_obj table 芯片对象（如sd500x实例）
--- @return boolean 添加是否成功
function ind_vrd_manager:add_chip(vrd_id, chip_obj)
    if not vrd_id or not chip_obj then
        log:error('[IndVrdManager] Invalid vrd_id or chip_obj')
        return false
    end

    self.vrd_chips[vrd_id] = chip_obj
    log:notice('[IndVrdManager] Added chip with VrdId=%s', vrd_id)
    return true
end

--- 获取芯片数量
--- @return number 芯片数量
function ind_vrd_manager:get_chip_count()
    local count = 0
    for _, _ in pairs(self.vrd_chips) do
        count = count + 1
    end
    return count
end

--- 获取拼接的版本号（按VrdId从小到大排序，用"."拼接）
--- @return string 拼接后的版本号
function ind_vrd_manager:get_all_version()
    local vrd_ids = {}
    for vrd_id, _ in pairs(self.vrd_chips) do
        table.insert(vrd_ids, vrd_id)
    end

    -- 排序VrdId
    table.sort(vrd_ids)

    -- 拼接版本号
    local versions = {}
    for _, vrd_id in ipairs(vrd_ids) do
        local chip_obj = self.vrd_chips[vrd_id]
        local ver = chip_obj:get_version()
        if ver and ver ~= '' then
            table.insert(versions, ver)
        else
            table.insert(versions, '-') -- 空版本使用空字符串
        end
    end

    local version_str = table.concat(versions, '.')
    log:debug('[IndVrdManager] UID=%s version=%s', self.UID, version_str)
    return version_str
end

--- 注册固件清单信息
--- @param bus table 总线对象
function ind_vrd_manager:register_firmware_info(bus)
    cmn.skynet.fork(function()
        local ok, obj = pcall(mdb.get_object, bus, FWINVENTORY_PATH, FWINVENTORY_INTERFACE)
        local times = 120 -- 重试120s, 防止资源树未加载
        while not ok and times > 0 do
            ok, obj = pcall(mdb.get_object, bus, FWINVENTORY_PATH, FWINVENTORY_INTERFACE)
            cmn.skynet.sleep(100)
            times = times - 1
        end
        if not ok then
            log:error('[IndVrdManager] UID=%s Get FirmwareInventory object failed. err: %s', self.UID, obj)
            return
        end

        local version = self:get_all_version()

        local param = {
            Id = 'VRD_' .. self.BoardType .. '_' .. self.position,
            Name = fw_manager:get_device_name_by_position(self.BoardType, self.position) .. ' VRD',
            Version = version,
            BuildNum = '',
            ReleaseDate = '',
            LowestSupportedVersion = '',
            SoftwareId = self.SoftwareId,
            Manufacturer = 'Huawei',
            Location = '',
            State = 'Enabled',
            Severity = 'Informational',
            StageSupported = 'true',
            RelatedItem = fw_manager:get_related_item_by_position(self.BoardType, self.position),
            ComponentID = defs.COMPONENT_ID,
            SubComponentID = defs.COMPONENT_ID_EX,
            TargetUIDs = self.UID,
            FirmwareType = 'INDEPENDENT_VRD'
        }
        obj:Add(context.new(), param, true, 1, 90)

        log:notice('[IndVrdManager] register firmware info success, SoftwareId=%s, Version=%s',
            self.SoftwareId, version)
    end)
end

--- 更新固件清单版本信息
--- @param bus table 总线对象
function ind_vrd_manager:update_firmware_version(bus)
    local version = self:get_all_version()
    local firmware_id = 'VRD_' .. self.BoardType .. '_' .. self.position
    local firmware_path = FWINVENTORY_PATH .. '/' .. firmware_id

    cmn.skynet.fork(function()
        local ok, obj = pcall(mdb.get_cached_object, bus, firmware_path, FWINFO_INTERFACE)
        if not ok then
            log:error('[IndVrdManager] UID=%s Get FirmwareInfo object failed. err: %s', self.UID, obj)
            return
        end

        log:info('[IndVrdManager] %s update firmware version to %s', firmware_id, version)
        obj.Version = version
    end)
end

-- 记录单个芯片升级日志
local function record_chip_upgrade_log(self, chip_obj, vrd_id, old_version, ret)
    local chip_name = string.format('Vrd_%02d', vrd_id)
    local device_name = fw_manager:get_device_name_by_position(chip_obj.obj.BoardType, chip_obj.position)
    local name = device_name == 'NA' and '' or device_name .. ' '
    if ret == defs.RET.OK then
        local new_version = chip_obj:get_version()
        log:maintenance(log.MLOG_INFO, log.FC__PUBLIC_OK,
            'Upgrade VRD chip %s from version %s to version %s successfully',
            chip_name, old_version, new_version)
        log:running(log.RLOG_INFO, 'Upgrade %sVRD chip %s Firmware (From: %s) successfully',
            name, chip_name, old_version)
    else
        log:maintenance(log.MLOG_INFO, log.FC__PUBLIC_OK, 'Upgrade VRD chip %s fail', chip_name)
        log:running(log.RLOG_ERROR, 'Upgrade %sVRD chip %s Firmware (From: %s) failed',
            name, chip_name, old_version)
    end
end

--- 执行升级操作
--- @param dir string 升级文件目录
--- @param progress_callback function|nil 进度回调函数 function(chip_index, chip_percentage)
--- @return number 升级结果码
function ind_vrd_manager:vrd_upgrade(dir, progress_callback)
    log:notice('[IndVrdManager] Start upgrade for UID=%s', self.UID)

    local vrd_ids = {}
    for vrd_id, _ in pairs(self.vrd_chips) do
        table.insert(vrd_ids, vrd_id)
    end
    table.sort(vrd_ids)

    local total_chips = self:get_chip_count()
    if total_chips == 0 then
        log:error('[IndVrdManager] No chips to upgrade for UID=%s', self.UID)
        return defs.RET.ERR
    end

    local success_count = 0
    local chip_logs = {}

    -- 依次升级每个芯片
    for idx, vrd_id in ipairs(vrd_ids) do
        local chip_obj = self.vrd_chips[vrd_id]
        log:notice('[IndVrdManager] Upgrading chip %d/%d, VrdId=%d', idx, total_chips, vrd_id)

        -- 获取升级前版本
        local old_version = chip_obj:get_version()
        -- 记录升级开始日志
        log:maintenance(log.MLOG_INFO, log.FC__PUBLIC_OK, 'Upgrade VRD chip %s start', string.format('Vrd_%02d', vrd_id))

        local ret = chip_obj:upgrade(dir)
        if ret == defs.RET.OK then
            success_count = success_count + 1
        else
            log:error('[IndVrdManager] Chip VrdId=%d upgrade failed', vrd_id)
        end

        chip_logs[#chip_logs + 1] = {
            chip_obj = chip_obj,
            vrd_id = vrd_id,
            old_version = old_version,
            ret = ret
        }

        -- 更新进度
        if progress_callback then
            progress_callback(idx, 100)
        end
    end

    -- 成功一个就要生效
    if success_count >= 1 then
        self:set_vrd_valid_flag()
    end

    -- 记录升级日志（需在生效后读取新版本）
    for _, entry in ipairs(chip_logs) do
        record_chip_upgrade_log(self, entry.chip_obj, entry.vrd_id, entry.old_version, entry.ret)
    end

    if success_count < total_chips then
        log:error('[IndVrdManager] Upgrade finished with failure')
        return defs.RET.ERR
    end

    log:notice('[IndVrdManager] Upgrade completed successfully')
    return defs.RET.OK
end

function ind_vrd_manager:set_vrd_valid_flag()
    if not next(self.vrd_chips) then
        log:error('[IndVrdManager] No chips to activate')
        return defs.RET.ERR
    end

    local unique_validreg_paths = {} -- 记录生效寄存器，避免重复调用
    local activation_tasks = {} -- 实际的生效任务

    for _, chip_obj in pairs(self.vrd_chips) do
        local ok, source_info = pcall(function()
            return chip_obj.obj:get_property_source("ValidateReg")
        end)
        if not ok or not source_info[1] or not source_info[1].Path then  -- ValidateReg生效寄存器未配置
            goto continue
        end
        local validreg_path = source_info[1].Path

        -- 对于重读配置的生效寄存器则跳过
        if unique_validreg_paths[validreg_path] then
            log:notice('[IndVrdManager] Duplicate ValidateReg detected: %s (VrdId=%s), skip',
                        validreg_path, chip_obj.obj.VrdId)
            goto continue
        end

        -- 记录路径并添加任务
        unique_validreg_paths[validreg_path] = true
        table.insert(activation_tasks, {
            path = validreg_path,
            chip_obj = chip_obj
        })
        ::continue::
    end

    -- 无有效任务，直接返回成功
    if #activation_tasks == 0 then
        log:notice('[IndVrdManager] No valid VRD validation tasks found, skip activation')
        return defs.RET.OK
    end

    -- 执行生效任务
    local activated_result = defs.RET.OK
    for _, task in ipairs(activation_tasks) do
        local ret = task.chip_obj:valid_vrd()
        if ret ~= defs.RET.OK then  -- 多个生效寄存器场景，有1个未成功就返回失败
            log:error('[IndVrdManager] Falied to activate VRD via ValidateReg=%s (VrdId=%s)',
                        task.path, task.chip_obj.obj.VrdId)
            activated_result = defs.RET.ERR
        else
            log:notice('[IndVrdManager] Successfully activated VRD via ValidateReg=%s (VrdId=%s)',
                        task.path, task.chip_obj.obj.VrdId)
        end
    end
    return activated_result
end

--- 设置对象获取完成标志
--- @param complete boolean 是否完成
function ind_vrd_manager:set_objects_complete(complete)
    self.objects_complete = complete
    log:debug('[IndVrdManager] UID=%s objects_complete set to %s', self.UID, tostring(complete))
end

--- 获取对象获取完成标志
--- @return boolean 是否完成
function ind_vrd_manager:is_objects_complete()
    return self.objects_complete
end

return ind_vrd_manager