-- 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: 独立vrd管理

local log = require 'mc.logging'
local singleton = require 'mc.singleton'
local class = require 'mc.class'
local signal = require 'mc.signal'
local vos = require 'utils.vos'
local utils_core = require 'utils.core'
local upgrade_subject = require 'upgrade_subject.upgrade_subject'
local cmn = require 'common'
local smc_vrd_upgrade = require 'independent_vrd.upgrade.smc_vrd_upgrade'
local pmbus_vrd_upgrade = require 'independent_vrd.upgrade.pmbus_vrd_upgrade'
local ind_vrd_manager = require 'independent_vrd.ind_vrd_manager'
local ini_parser = require 'mc.v2_persistence'
local defs = require 'independent_vrd.ind_vrd_defs'
local vrd_comm_method = require 'release_command.vrd_comm_method'

local ind_vrd_service = class()
ind_vrd_service.__index = ind_vrd_service

local function create_vrd_obj(object, pos)
    return {
        Id = "VRD_" .. object.BoardType .. "_" .. pos,
        BoardType = object.BoardType,
        RefChip = object.RefChip,
        LockChip = object.RefChip,
        UID = object.UID,
        SoftwareId = object.SoftwareId,
        Protocol = "SMC",
        MajorVersion = "",
        MinorVersion = "",
        Revision = "",
        SubCompList = {},
        VrdComponentID = defs.COMPONENT_ID,
        VrdComponentIDEx = defs.COMPONENT_ID_EX,
        object = object
    }
end

function ind_vrd_service:on_dump_cb(ctx, path)
    if next(self.smc_ind_vrd_collection) == nil then
        return
    end
    vrd_comm_method:on_dump_vrd_cb(ctx, path, self.smc_ind_vrd_collection, vrd_comm_method.DUMP_CHIP_TYPE.CPLD)
end

function ind_vrd_service:on_add_object(class_name, object, position)
    local switch = {
        ['VRDFirmware'] = function()
            local ok, chip_obj = pcall(require, string.format('independent_vrd.chip.%s_%s',
                string.lower(object.ChipType), string.lower(object.Protocol)))
            if not ok or not chip_obj then
                log:error('invalid vrd chip type: %s', object.ChipType)
                return
            end

            local obj = chip_obj.new(create_vrd_obj(object, position), position, self.vrd_info_changed)

            if object.Protocol == 'SMC' then
                -- SMC的继承vrd作为子固件管理方式
                obj:register_firmware_info(self.bus)
                cmn.skynet.fork(function ()
                    cmn.skynet.sleep(12000) -- 启动阶段等待2分钟后监听VRD异常
                    vrd_comm_method:listen_vrd_abnormal(obj)
                end)
                table.insert(self.smc_ind_vrd_collection, obj)
            elseif object.Protocol == 'PMBus' then
                -- PMBus VRD按position分组管理
                local uid = object.UID
                local vrd_id = object.VrdId

                -- 查找或创建VRD管理器
                if not self.pmbus_vrd_manager_collection[position] then
                    self.pmbus_vrd_manager_collection[position] = ind_vrd_manager.new(uid, object.BoardType,
                        object.SoftwareId, position)
                    log:notice('[VRDFirmware] Created VRD manager for position: %s, UID: %s', position, uid)
                end

                -- 将芯片对象加入管理器
                local manager = self.pmbus_vrd_manager_collection[position]
                manager:add_chip(vrd_id, obj)
            end
        end
    }

    if switch[class_name] then
        switch[class_name]()
        log:notice('[VRDFirmware] Add object, class: %s, position: %s, type: %s, protocol: %s',
            class_name, position, object.ChipType, object.Protocol)
    end
end

-- 对象添加完成回调函数
function ind_vrd_service:on_add_object_complete(position)
    log:notice('[VRDFirmware] on_add_object_complete, position: %s', position)

    -- 检查该position是否有PMBus VRD管理器
    if self.pmbus_vrd_manager_collection[position] then
        local manager = self.pmbus_vrd_manager_collection[position]
        manager:set_objects_complete(true)
        manager:register_firmware_info(self.bus)
    end
end

function ind_vrd_service:on_delete_object(class_name, object, position)
    local switch = {
        ['VRDFirmware'] = function()
            cmn.remove_ele_by_position(self.smc_ind_vrd_collection, position)

            -- PMBus VRD需要从管理器中移除
            if object.Protocol == 'PMBus' then
                if self.pmbus_vrd_manager_collection[position] then
                    local manager = self.pmbus_vrd_manager_collection[position]
                    manager.vrd_chips[object.VrdId] = nil

                    -- 如果管理器内没有芯片了，删除整个管理器
                    if not next(manager.vrd_chips) then
                        self.pmbus_vrd_manager_collection[position] = nil
                        log:notice('[VRDFirmware] Deleted VRD manager for position: %s, UID: %s',
                            position, manager.UID)
                    end
                end
            end
        end
    }

    if switch[class_name] then
        switch[class_name]()
        log:notice('[VRDFirmware] Delete object, class: %s, position: %s, type: %s',
            class_name, position, object.ChipType)
    end
end

function ind_vrd_service.new()
    return setmetatable({
        smc_ind_vrd_collection = {},
        pmbus_vrd_manager_collection = {} -- key为position, value为ind_vrd_manager对象
    }, ind_vrd_service)
end

-- VRD类型解析函数：根据配置文件判断协议类型
function ind_vrd_service:parse_firmware_type(parameters)
    local data = ini_parser.load_file(parameters.CfgPath)
    local firm = parameters.FirmwareIndex or 'Firmware1'
    local uid = data[firm].Uid
    for _, obj in pairs(self.smc_ind_vrd_collection) do
        if obj.mcu.UID == uid then
            return defs.FIRMWARE_TYPE.SMC_UPGRADE
        end
    end
    -- PMBus VRD通过遍历manager collection查找匹配的UID
    for _, manager in pairs(self.pmbus_vrd_manager_collection) do
        if manager.UID == uid then
            return defs.FIRMWARE_TYPE.PMBUS_UPGRADE
        end
    end
    log:error('parse firmware type not match uid: %s', uid)
end

-- 有效固件类型解析函数
local function parse_active_firmware_type(firmware_type)
    local fw_type = firmware_type == defs.FIRMWARE_TYPE.ACTIVE and defs.FIRMWARE_TYPE.UPGRADE or firmware_type
    if vos.get_file_accessible(defs.FW_VALID_FILE_DIR[fw_type]) then
        for _, file_name in pairs(utils_core.dir(defs.FW_VALID_FILE_DIR[fw_type])) do
            -- file_name be like: HOST_<sys_id>_Independent_Vrd*.hpm
            local type = file_name:match("HOST_%d+_(.+)%.hpm")
            if type and type == defs.FIRMWARE_TYPE.PMBUS_UPGRADE then
                return defs.FIRMWARE_TYPE.PMBUS_ACTIVE
            end
            if type and type == defs.FIRMWARE_TYPE.UPGRADE then
                return defs.FIRMWARE_TYPE.SMC_ACTIVE
            end
            log:error('get invalid file_name: %s', file_name)
        end
        return
    end
    log:error('[Ind_Vrd] cache upgrade file not exist')
end

function ind_vrd_service:init(bus, db)
    self.bus = bus
    self.vrd_info_changed = signal.new()
    self.smc_vrd_upgrade_obj = smc_vrd_upgrade.new(bus, db, self.smc_ind_vrd_collection)
    self.pmbus_vrd_upgrade_obj = pmbus_vrd_upgrade.new(bus, db, self.pmbus_vrd_manager_collection)

    -- 注册VRD固件解析器
    upgrade_subject.get_instance():register_firmware_parser(defs.FIRMWARE_TYPE.UPGRADE, function(cfg_path)
        return self:parse_firmware_type(cfg_path)
    end)

    upgrade_subject.get_instance():register_active_firmware_parser(defs.FIRMWARE_TYPE.ACTIVE,
        parse_active_firmware_type)
    -- 注册升级观察者
    upgrade_subject.get_instance():register_upgrade_observer(self.smc_vrd_upgrade_obj, defs.FIRMWARE_TYPE.SMC_UPGRADE)
    upgrade_subject.get_instance():register_active_observer(self.smc_vrd_upgrade_obj, defs.FIRMWARE_TYPE.SMC_ACTIVE)
    upgrade_subject.get_instance():register_upgrade_observer(self.pmbus_vrd_upgrade_obj,
        defs.FIRMWARE_TYPE.PMBUS_UPGRADE)
    upgrade_subject.get_instance():register_active_observer(self.pmbus_vrd_upgrade_obj,
        defs.FIRMWARE_TYPE.PMBUS_ACTIVE)
end

return singleton(ind_vrd_service)
