-- Copyright (c) 2024 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 singleton = require 'mc.singleton'
local log = require 'mc.logging'
local client = require 'general_hardware.client'
local utils = require 'dpu_service.utils'
local parser_cfg = require 'mcu.upgrade.parser_cfg'
local ini_parse = require 'mc.v2_persistence'

local upgrade_subject = {}
upgrade_subject.__index = upgrade_subject

local DUP_MCU_TYPE = 'DPUMcu'
local HPC_VDR_TYPE = 'HPCVrd'

local function is_dpu_firmware(cfg_path)
    if not utils.get_firmware_uid(cfg_path, 'Firmware1', 'UID') then
        log:debug("This is not a dpu mcu, can not get UID")
        return false
    end
    return true
end

local function build_firm_type_map_key(parameters)
    local key = tostring(parameters.ComponentID) .. '_' .. tostring(parameters.ComponentIDEx)
    return key
end

function upgrade_subject:get_maped_firm_type(firmware_type, firm_key, parameters)
    if self.firmware_parsers[firmware_type] then
        return self.firmware_parsers[firmware_type](parameters) or firmware_type
    end
    if firmware_type ~=  'Mcu' and firmware_type ~= 'Vrd' then
        return firmware_type
    end
    if not self.firmware_map[firm_key] then
        return firmware_type
    end
    return self.firmware_map[firm_key]
end

function upgrade_subject:cpld_test_firmware_type(firmware_type, path)
    local ok, data = pcall(ini_parse.load_file, path)
    if not ok then
        return nil
    end
    local test_case = data.EquipmentTest and data.EquipmentTest.TestCase or nil
    if test_case == 'cpld' then
        self.firmware_parsers[firmware_type] = function()
            return 'cpld_test'
        end
    end
    return nil
end

-- 判断特殊升级固件
function upgrade_subject:build_firmware_type_map(firmware_type, cfg_path, parameters)
    if firmware_type == 'Mcu' and is_dpu_firmware(cfg_path) then
        local firm_key = build_firm_type_map_key(parameters)
        self.firmware_map[firm_key] = DUP_MCU_TYPE
        return DUP_MCU_TYPE
    end
    if firmware_type == 'Vrd' and parser_cfg.is_hpc_vrd_pkg(cfg_path) then
        local firm_key = build_firm_type_map_key(parameters)
        self.firmware_map[firm_key] = HPC_VDR_TYPE
        return HPC_VDR_TYPE
    end
    self:cpld_test_firmware_type(firmware_type, cfg_path)
    if self.firmware_parsers[firmware_type] then
        return self.firmware_parsers[firmware_type](parameters) or firmware_type
    end

    return firmware_type
end

function upgrade_subject:build_valid_firmware_type_map(firmware_type)
    if self.active_firmware_parsers[firmware_type] then
        return self.active_firmware_parsers[firmware_type](firmware_type) or firmware_type
    end
    return firmware_type
end

function upgrade_subject:register_upgrade_observer(observer, firmware_type)
    log:notice('register upgrade observer firmware_type:%s', firmware_type)
    if self.upgrade_observers[firmware_type] then
        log:error('Already exists the same upgrade firmware_type:[%s]', firmware_type)
        return
    end
    self.upgrade_observers[firmware_type] = observer
end

function upgrade_subject:register_active_observer(observer, firmware_type)
    log:notice('register active observer firmware_type:%s', firmware_type)
    if self.active_observers[firmware_type] then
        log:error('Already exists the same active firmware_type:[%s]', firmware_type)
        return
    end
    self.active_observers[firmware_type] = observer
end

function upgrade_subject:on_upgrade_prepare(context, system_id, firmware_type, cfg_path, hpm_path, parameters)
    local transed_firm = self:build_firmware_type_map(firmware_type, cfg_path, parameters)
    log:notice("[on_upgrade_prepare] firmware_type:%s cur_firmware_type:%s", firmware_type, transed_firm)

    local observer_service = self.upgrade_observers[transed_firm]
    if not observer_service then
        log:error('[on_upgrade_prepare] does not exists the observer, firmware_type:%s', transed_firm)
        return
    end

    log:notice("[on_upgrade_prepare] start upgrade %s", firmware_type)
    local ok, err = pcall(function ()
        return observer_service:on_upgrade_prepare(context, system_id, firmware_type, cfg_path, hpm_path, parameters)
    end)
    if not ok then
        log:error("upgrade prepare failed, error:%s", err)
    end
    log:notice("[on_upgrade_prepare] end upgrade %s", firmware_type)
end

function upgrade_subject:on_upgrade_process(context, system_id, firmware_type, file_path, parameters)
    local firm_key = build_firm_type_map_key(parameters)
    local firm_type = self:get_maped_firm_type(firmware_type, firm_key, parameters)
    local observer_service = self.upgrade_observers[firm_type]
    if not observer_service then
        log:error('[on_upgrade_process] does not exists the observer, firmware_type:%s', firm_type)
        return
    end

    log:notice("[on_upgrade_process] start upgrade %s", firmware_type)
    local ok, err = pcall(function ()
        return observer_service:on_upgrade_process(context, system_id, firmware_type, file_path, parameters)
    end)
    if not ok then
        log:error("upgrade process failed, error:%s", err)
    end
    log:notice("[on_upgrade_process] end upgrade %s", firmware_type)
end

function upgrade_subject:on_upgrade_finish(context, system_id, firmware_type, parameters)
    local firm_key = build_firm_type_map_key(parameters)
    local firm_type = self:get_maped_firm_type(firmware_type, firm_key, parameters)
    local observer_service = self.upgrade_observers[firm_type]
    if not observer_service then
        log:error('[on_upgrade_finish] does not exists the observer, firmware_type:%s', firm_type)
        return
    end

    log:notice("[on_upgrade_finish] start upgrade %s", firmware_type)
    local ok, err = pcall(function ()
        return observer_service:on_upgrade_finish(context, system_id, firmware_type, parameters)
    end)
    if not ok then
        log:error("upgrade finish failed, error:%s", err)
    end
    log:notice("[on_upgrade_finish] end upgrade %s", firmware_type)
end

function upgrade_subject:on_active_process(context, system_id, firmware_type)
    local firm_type = self:build_valid_firmware_type_map(firmware_type)
    local observer_service = self.active_observers[firm_type]
    if not observer_service then
        log:error('[on_active_process] does not exists the observer, firmware_type:%s', firm_type)
        return
    end

    log:notice("[on_active_process] start upgrade %s", firm_type)
    local ok, err = pcall(function ()
        return observer_service:on_active_process(context, system_id, firm_type)
    end)
    if not ok then
        log:error("active process failed, error:%s", err)
    end
    log:notice("[on_active_process] end upgrade %s", firm_type)
end

function upgrade_subject:register_firmware_parser(firmware_type, parser_func)
    log:notice('register firmware parser for firmware_type:%s', firmware_type)
    if self.firmware_parsers[firmware_type] then
        log:error('Already exists the same firmware parser for firmware_type:[%s]', firmware_type)
        return
    end
    self.firmware_parsers[firmware_type] = parser_func
end

function upgrade_subject:register_active_firmware_parser(firmware_type, parser_func)
    log:notice('register valid firmware parser for firmware_type:%s', firmware_type)
    if self.active_firmware_parsers[firmware_type] then
        log:error('Already exists the same valid firmware parser for firmware_type:[%s]', firmware_type)
        return
    end
    self.active_firmware_parsers[firmware_type] = parser_func
end

function upgrade_subject.new()
    return setmetatable({}, upgrade_subject)
end

function upgrade_subject:init()
    self.upgrade_observers = {}
    self.active_observers = {}
    self.firmware_map = {}
    self.firmware_parsers = {}
    self.active_firmware_parsers = {}

    -- 监听升级信号
    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)
end

return singleton(upgrade_subject)