-- 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.

local class = require 'mc.class'
local log = require 'mc.logging'
local mdb = require 'mc.mdb'
local skynet = require 'skynet'
local context = require 'mc.context'
local client = require 'bios.client'
local prop_def = require 'macros.property_def'

local KRUN_PATH<const> = '/bmc/kepler/UpdateService/FirmwareInventory/Krun'
local KRUN_INTERFACE<const> = 'bmc.kepler.UpdateService.FirmwareInfo'

local krun_firmware = class()

local function get_firmware_path(path, id)
    if id == prop_def.DEFAULT_SYSTEM_ID then
        return path
    end
    return path .. id
end

function krun_firmware:ctor(bus, db, id, uid, related_systems)
    self.bus = bus
    self.db = db
    self.id = id
    self.uid = uid
    self.related_systems = related_systems
    self.krun_path = get_firmware_path(KRUN_PATH, id)
end

function krun_firmware:init()
    -- 注册KRUN固件信息
    self:register_krun_info()
end

function krun_firmware:get_krun_fw_info()
    local db_id = 'Krun'
    if self.id ~= prop_def.DEFAULT_SYSTEM_ID then
        db_id = 'Krun' .. self.id
    end
    local krun_info = self.db:select(self.db.KrunFwInfoTable)
        :where(self.db.KrunFwInfoTable.Id:eq(db_id)):first()
    if krun_info then
        return krun_info
    end
    krun_info = self.db.KrunFwInfoTable({
        Id = db_id,
        Name = 'Krun',
        Version = '0.00',
        BuildNum = '',
        ReleaseDate = '',
        LowestSupportedVersion = '',
        SoftwareId = 'Krun-Krun',
        Manufacturer = 'Huawei',
        Updateable = true,
        Location = 'U75',
        Capability = '5',
        State = 'Enabled',
        ActiveMode = '',
        ActiveModeSupported = true,
        ActiveModeEnabled = true})
    krun_info:save()
    log:notice('[bios]create krun firmware table, id: %s', self.id)
    return krun_info
end

function krun_firmware:register_krun_info()
    -- 查询Krun固件信息
    local krun_info = self:get_krun_fw_info()
    if not krun_info then
        log:error('get Krun info failed')
        return
    end
    local related_items = ''
    for _, sys_id in pairs(self.related_systems) do
        if related_items ~= '' then
            related_items = related_items .. ','
        end
        related_items = related_items .. '/redfish/v1/Systems/' .. sys_id .. '/Bios'
    end
    -- 注册Krun固件信息到资源树
    skynet.fork_once(function()
        local retries = 0
        local ok, rsp
        repeat
            ok, rsp = client:PFirmwareInventoryFirmwareInventoryAdd(context.new(),
                {Id = krun_info.Id, Name = krun_info.Name, Version = krun_info.Version, BuildNum = krun_info.BuildNum,
                ReleaseDate = krun_info.ReleaseDate, LowestSupportedVersion = krun_info.LowestSupportedVersion,
                SoftwareId = krun_info.SoftwareId, Manufacturer = krun_info.Manufacturer,
                Location = krun_info.Location, State = 'Enabled', Severity = 'Informational', StageSupported = 'true',
                RelatedItem = related_items, ComponentID = '2', SubComponentID = '132', FirmwareType = 'Krun', TargetUIDs = self.uid},
                krun_info.Updateable, krun_info.Capability, 20) -- krun固件包需预留20MB的tmp空间
            if not ok then
                retries = retries + 1
                skynet.sleep(100)
            end
        until ok or retries > 120

        if retries > 120 then
            log:error('[BIOSUpgrade] register Krun_%s to firmware inventory fail, message:%s', self.id, rsp)
            return
        end
        log:notice('[BIOSUpgrade] register Krun_%s to firmware inventory success', self.id)
    end)
end

function krun_firmware:update_krun_version(version)
    local obj = mdb.get_cached_object(self.bus, self.krun_path, KRUN_INTERFACE)
    obj.Version = version
    local db_id = 'Krun'
    if self.id ~= prop_def.DEFAULT_SYSTEM_ID then
        db_id = 'Krun' .. self.id
    end
    local fw_table = self.db.KrunFwInfoTable({Id = db_id})
    if fw_table and fw_table.Version then
        fw_table.Version = version
        fw_table:save()
        log:notice('[bios] update krun version(%s) to db success, id: %s', version, self.id)
    end
end

return krun_firmware
