-- 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 lu = require 'luaunit'
local gold_package = require 'domain.bios_firmware.gold.gold_package'
local bin_builder = require 'libmgmt_protocol.bios.bin_parser.bin_parser_builder'
local bios_enum = require 'domain.bios_firmware.defs'
local package_snapshot = require 'domain.bios_firmware.package.package_snapshot'
local package_validator = require 'domain.bios_firmware.package.package_validator'
local package_cfg = require 'domain.bios_firmware.package.package_cfg'
local component = require 'libmgmt_protocol.bios.domain.bios_firmware.package.component.component'
local component_collection = require 'libmgmt_protocol.bios.domain.bios_firmware.package.component.component_collection'
local package_builder = require 'domain.bios_firmware.package.package_builder'
local upgrade_scenarios = require 'domain.bios_firmware.package.upgrade_scenarios'
local open_db = require 'bios.db'
local ipmb_channel = require 'domain.bios_firmware.package.channel.ipmb_channel'
local upgrade_config_builder = require 'domain.bios_firmware.package.upgrade_config_builder'
local executors = require 'domain.bios_firmware.package.executors.upgrade_executor'

TestPackage= {}

function TestPackage:test_gold_package()
    local package = gold_package.new(1)
    local ok = pcall(function()
        package:cache_hpm()
    end)
    lu.assertEquals(ok, false)
    lu.assertEquals(package:get_prop('FwVerifyResult'), bios_enum.PfrVerify.Success)
    lu.assertEquals(package:get_prop('BackupFwVerifyResult'), bios_enum.PfrVerify.Success)

    ok = pcall(function()
        package:recover_from_gold()
    end)
    lu.assertEquals(ok, true)

    local confirm_power_off_mock = package._confirm_power_off
    package._confirm_power_off = function ()
    end
    ok = pcall(function()
        package:recover_from_gold()
    end)
    lu.assertEquals(ok, true)

    ok = pcall(function()
        package:verify_and_recover()
    end)
    lu.assertEquals(ok, true)

    local verify_mock = package.verify
    package.verify = function ()
        error("error")
    end
    ok = pcall(function()
        package:verify_and_recover()
    end)
    lu.assertEquals(ok, true)

    package._confirm_power_off = confirm_power_off_mock
    package.verify = verify_mock
end

function TestPackage:test_bin_parser()
    local bin_parser = bin_builder.build(bios_enum.PackagePeriod.Period2)
    lu.assertEquals(bin_parser.name, 'BinParserPeriod2')
    bin_parser =  bin_builder.build(bios_enum.PackagePeriod.Period3)
    lu.assertEquals(bin_parser.name, 'BinParserPeriod3')
end

local function construct_db()
    local ok, datas = pcall(require, 'bios.datas')
    if not ok then
        -- 如果没有datas配置，证明当前组件不需要datas，仅打开数据库
        datas = nil
    end
    local db = open_db(':memory:', datas)
    return db
end

function TestPackage:test_package_snapshot()
    local snapshot = package_snapshot.new(construct_db(), {ActivatedStatus = 1}, 1)
    lu.assertEquals(snapshot.PackageType, bios_enum.PackageType.Normal)
    lu.assertEquals(snapshot.Period, bios_enum.PackagePeriod.Period2)
    lu.assertEquals(snapshot.UpgradeFinishFlag, true)

    lu.assertEquals(snapshot:is_complete(), true)
    lu.assertEquals(snapshot:is_cache(), false)
    lu.assertEquals(snapshot:get_period(), bios_enum.PackagePeriod.Period2)
    lu.assertEquals(snapshot:get_package_type(), bios_enum.PackageType.Normal)
end

local function validate_package(mode, package_type, result)
    local ctx = {
        UpgradeMode = mode,
        PackageType = package_type
    }
    local ok = pcall(function()
        local validator = package_validator.new()
        validator:validate(ctx)
    end)
    lu.assertEquals(ok, result)
end

function TestPackage:test_package_validator()
    -- 正常场景
    validate_package(bios_enum.UpgradeMode.Cold, bios_enum.PackageType.Normal, true)
    validate_package(bios_enum.UpgradeMode.Force, bios_enum.PackageType.Normal, true)
    validate_package(bios_enum.UpgradeMode.PowerOffEffective, bios_enum.PackageType.Normal, true)
    validate_package(bios_enum.UpgradeMode.PowerOffEffective, bios_enum.PackageType.Patch, true)

    -- 异常场景
    validate_package(bios_enum.UpgradeMode.Hot, bios_enum.PackageType.Patch, false)
    validate_package(bios_enum.UpgradeMode.Cold, bios_enum.PackageType.Patch, false)
    validate_package(bios_enum.UpgradeMode.Hot, bios_enum.PackageType.Normal, false)
    validate_package(bios_enum.UpgradeMode.Force, bios_enum.PackageType.Patch, false)
end

function TestPackage:test_package_cfg()
    local ok = pcall(function()
        package_cfg.new()
    end)
    lu.assertEquals(ok, false)
end

local function component_upgrade(offset, length, ctx, result)
    local compt = component.new(offset, length, 0)
    local channel = ipmb_channel.new()
    local channels = {
        [1] = channel
    }
    compt:set_channels(channels)
    local ok = pcall(function()
        compt:upgrade(ctx)
    end)
    lu.assertEquals(ok, result)
end

function TestPackage:test_component()
    component_upgrade(0, 10, {}, false)
    component_upgrade(0, 10, {PackageSize = 100}, false)
    component_upgrade(0, 10, {PackageSize = 5}, false)
end

function TestPackage:test_component_collection()
    local compt = component.new(0, 0, 0)
    local channel = ipmb_channel.new()
    local channels = {
        [1] = channel
    }
    local collection = component_collection.new()
    collection:add_component(compt)
    collection:set_channels(channels)
    local ok = pcall(function()
        collection:upgrade({PackageSize = 100})
    end)
    lu.assertEquals(ok, false)
end

function TestPackage:test_package_builder()
    local builder = package_builder.new()
    local ok, pkage
    ok = pcall(function()
        builder:build()
    end)
    lu.assertEquals(ok, false)

    local snapshot = package_snapshot.new(construct_db(), {ActivatedStatus = 1}, 1)
    ok, pkage = pcall(function()
        return builder:build_with_snapshot(snapshot)
    end)
    lu.assertEquals(ok, true)
    lu.assertEquals(pkage.package_type, bios_enum.PackageType.Normal)
    lu.assertEquals(pkage.period, bios_enum.PackagePeriod.Period2)
end

function TestPackage:test_executor()
    local final_executor = executors[bios_enum.UpgradeSteps.WaitUpgradeFinish].new()
    lu.assertNotEquals(final_executor, nil)
end

function TestPackage:test_upgrade_scenarios()
    local scenarios
    local ok, err = pcall(function()
        local package_info = {
            period = self.period,
            package_type = self.package_type
        }
        local cfg_builder = upgrade_config_builder.new({}, package_info)
        local upgrade_cfg = cfg_builder:build()
        scenarios = upgrade_scenarios.new(upgrade_cfg)
    end)
    if not ok then
        error(string.format('[bios] build scenarios fail, %s', err))
    end
    -- executor执行链的上下文对象
    local ctx = {
        -- 全局上下文：不可更改
        Global = {
            UpgradeDBTable = construct_db(),
            UpgradeMode = bios_enum.UpgradeMode.Cold,
            Period = bios_enum.PackagePeriod.Period2,
            PackageType = bios_enum.PackageType.Normal,
            PackageCfg = {}
        },
        -- 链间上下文
        Interchain = {
            UpgradePath = '',
            -- 标识是否需要传包升级
            UpgradeFlag = true
        },
    }
    pcall(function()
        scenarios:execute(ctx)
    end)
end