-- 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 bios_enum = require 'domain.bios_firmware.defs'
local package_snapshot = require 'domain.bios_firmware.package.package_snapshot'
local package_builder = require 'domain.bios_firmware.package.package_builder'
local open_db = require 'bios.db'
local ipmb_channel = require 'domain.bios_firmware.package.channel.ipmb_channel'
local executors = require 'domain.bios_firmware.package.executors.upgrade_executor'
local imu_communicate = require 'domain.transport.imu_communicate'
local package = require 'domain.bios_firmware.package.package'
local component_version = require 'domain.mapping.component_version'
local bios_factory = require 'factory.bios_factory'
local bios_service = require 'service.bios_service'
local json = require 'cjson'
local component_upgrade = require 'domain.transport.component_upgrade'
local firmware = require 'interface.mdb.firmware_multihost'
local pfr_service = require 'service.pfr_service'
local upgrade_service = require 'service.upgrade_service'
local context = require 'pojo.context'
local bios_object = require 'pojo.bios_object_mutihost'
local msg = require 'bios.ipmi.ipmi_message'
local obj_def = require 'macros.object_def'
local prop_def = require "macros.property_def"
local object_service = require 'service.object_service'
local mdb_service = require 'mc.mdb.mdb_service'
local fructl_handler = require 'infrastructure.fructl'
local privilege_check = require 'infrastructure.privilege_check'

TestMultihost = {}

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 TestMultihost:test_bin_parser()
    local upgrade_component_executor = executors[bios_enum.UpgradeSteps.UpgradeComponent].new({})
    local ok = pcall(function()
        upgrade_component_executor:send_confirm({
            Global = {
                SystemId = 1,
                Period = bios_enum.PackagePeriod.Period3,
                UpgradeMode = bios_enum.UpgradeMode.Hot
            }
        })
    end)
    lu.assertEquals(ok, false)

    local imu_cmd = imu_communicate.get_instance()
    imu_cmd.query_firmware_process_status = function()
        return {Status = 0x00}
    end
    imu_cmd:query_firmware_process_status()
    local final_executor = executors[bios_enum.UpgradeSteps.WaitUpgradeFinish].new({})
    final_executor:execute({
        Global = {
            SystemId = 1
        }
    }, {
        execute = function()
            return
        end
    })
    lu.assertNotEquals(final_executor, nil)
end

function TestMultihost:test_package()
    local package_instance = package.new({}, bios_enum.PackageType.Normal)
    local upgrade_info = {
        db = construct_db(),
        upgrade_path = '',
        upgrade_mode = bios_enum.UpgradeMode.Cold
    }
    local snapshot = package_snapshot.new(construct_db(), {ActivatedStatus = 1}, 1)
    local ok = pcall(function()
        package_instance:_start(upgrade_info, snapshot)
    end)
    lu.assertEquals(ok, false)
end

local function get_bios_obj()
    return {
        get_system_id = function()
            return 1
        end,
        register_mdb_objects = function ()
        end,
        unregister_mdb_objects = function ()
        end,
        PcieCardBDF = '',
        PcieDiskBDF = '',
        OCPCardBDF = '',
        Slot = 1,
        Version = '10.66'
    }
end

function TestMultihost:test_package_snapshot()
    local snapshot = package_snapshot.new(construct_db(), {ActivatedStatus = 1}, 1)
    local ok = pcall(function()
        snapshot:activate(0x01)
    end)
    lu.assertEquals(ok, false)

    local bios_ser = bios_service.get_instance(bus, construct_db())
    bios_ser:add_object(get_bios_obj())
    bios_factory.register_bean('bios_service', bios_ser)
    local component_version_instance = component_version.new(1)
    local version = {
        PatchVersion = 'HP001'
    }
    component_version_instance.data = json.encode(version)
    bios_ser.update_patch_version = function()
        return
    end
    ok = pcall(function()
        component_version_instance:update_cfg_version()
    end)
    lu.assertEquals(ok, true)
end

function TestMultihost:test_a_mapping()
    local imu_cmd = imu_communicate.get_instance()
    pcall(imu_cmd.activate, imu_cmd, 0x01, 1)
    pcall(imu_cmd.write_firmware, imu_cmd, '', 1)
    pcall(imu_cmd.query_firmware_process_status, imu_cmd, 1)
    local compt_upgrade = component_upgrade.new()
    compt_upgrade.system_id = 1
    compt_upgrade.component_data = {
        get_type = function()
            return 1
        end,
        get_size = function()
            return 1
        end,
        get_crc = function()
            return 1
        end,
        get_id = function()
            return 1
        end,
        get_offset = function()
            return 1
        end,
        fetch_data = function()
            return ''
        end
    }
    pcall(compt_upgrade.write, compt_upgrade, '', 1)
    pcall(compt_upgrade.write_prepare, compt_upgrade)
    pcall(compt_upgrade.write_data, compt_upgrade)
    pcall(compt_upgrade.write_finish, compt_upgrade)
end

function TestMultihost:test_firmware()
    local firmware_instance = firmware.new({}, construct_db(), 1)
    pcall(firmware_instance.get_activate_mode, firmware_instance)
    pcall(firmware_instance.clear_activate_mode, firmware_instance)
end

function TestMultihost:test_pfr_service()
    local snapshot = package_snapshot.new(construct_db(), {ActivatedStatus = 1}, 1)
    local pfr_service_instance = pfr_service.new(construct_db())
    pfr_service_instance:cache_hpm_after_activate(snapshot)
    pfr_service_instance:can_effective(1)
    pfr_service_instance.can_effective = function()
        return true
    end
    pfr_service_instance:try_get_version(snapshot)
    pfr_service_instance:wait_effective(snapshot)
    pfr_service_instance:backup_hpm(2)
    pfr_service_instance:remove_tmp_hpm(2)
end

local function construct_ctx()
    local ctx = {}
    ctx.ChanType = 1
    ctx.get_initiator = function()
        return {}
    end

    return ctx
end

local function test_activate_components(upgrade_service_instance, snapshot)
    local bios_ser = bios_service.get_instance(bus, construct_db())
    bios_ser.is_multihost = function()
        return true
    end
    upgrade_service_instance:activate_components(construct_ctx(), {'IMU'})
    bios_ser.is_multihost = function()
        return false
    end

    local ok = pcall(function()
        upgrade_service_instance:activate_components(construct_ctx(), {'IMU'})
    end)
    lu.assertEquals(ok, false)

    ok = pcall(function()
        upgrade_service_instance:activate_component(construct_ctx(), "IMU", snapshot)
    end)
    lu.assertEquals(ok, false)

    bios_ser:set_prop('SystemStartupState', 254, 1)
    ok = pcall(function()
        upgrade_service_instance:activate_component(construct_ctx(), {"IMU"}, snapshot)
    end)
    lu.assertEquals(ok, false)
    bios_ser.get_componet_bitmap_list = function()
        return {
            [1] = {
                BitMap = 0x01,
                Name = 'IMU'
            }
        }
    end
    ok, res = pcall(function()
        upgrade_service_instance:activate_component(construct_ctx(), {"IMU"}, snapshot)
    end)
    lu.assertEquals(ok, false)
    snapshot.activate = function()
        return true
    end
    ok = pcall(function()
        upgrade_service_instance:activate_component(construct_ctx(), {"IMU"}, snapshot)
    end)
    lu.assertEquals(ok, true)
    ok = pcall(function()
        upgrade_service_instance:activate_multihost_components(construct_ctx(), {"IMU"})
    end)
    lu.assertEquals(ok, true)
    upgrade_service_instance.hpm_upgrade.upgrade_mode = bios_enum.UpgradeMode.Hot
    ok = pcall(function()
        upgrade_service_instance:checkout_before_activate(construct_ctx(), 1)
    end)
    lu.assertEquals(ok, false)
end

local function test_upgrade(upgrade_service_instance, snapshot)
    upgrade_service_instance.hpm_upgrade:hot_upgrade_info({
        system_id = 1,
        firmware_type = 1,
        cfg_path = '',
        hpm_path = '',
        context = construct_ctx()
    }, 1)

    local ok = pcall(function()
        upgrade_service_instance.hpm_upgrade:online_force_upgrade_info({
            system_id = 1,
            firmware_type = 1,
            cfg_path = '',
            hpm_path = '',
            context = construct_ctx()
        }, 1)
    end)
    lu.assertEquals(ok, false)

    upgrade_service_instance.hpm_upgrade:build_upgrade_mode({
        system_id = 1,
        firmware_type = 1,
        cfg_path = '',
        hpm_path = '',
        context = construct_ctx(),
        ActivateComponents = {"IMU"}
    }, 1)
end

local function test_patch(upgrade_service_instance, snapshot)
    upgrade_service_instance.hpm_upgrade.package =
        package_builder.new():build_with_snapshot(snapshot)
    upgrade_service_instance.hpm_upgrade:check_patch_version(1)
    upgrade_service_instance.hpm_upgrade:build_info({
        system_id = 1,
        firmware_type = 1,
        cfg_path = '',
        hpm_path = '',
        context = construct_ctx(),
        spi_rate = 10,
        Multihost = true
    }, 1)

    local ok = pcall(function()
        upgrade_service_instance.hpm_upgrade:_process_multihost(snapshot, {
            system_id = 1,
            firmware_type = 1,
            cfg_path = '',
            hpm_path = '',
            context = construct_ctx(),
            spi_rate = 10,
            Multihost = true
        })
    end)
    lu.assertEquals(ok, false)

    ok, res = pcall(function()
        upgrade_service_instance.hpm_upgrade:_process_multihost(snapshot, {
            system_id = 1,
            firmware_type = 1,
            cfg_path = '',
            hpm_path = '',
            context = construct_ctx(),
            spi_rate = 10,
            Multihost = true,
            ActivateComponents = {"IMU"}
        })
    end)
    lu.assertEquals(ok, false)
end

local function test_process(upgrade_service_instance, snapshot)
    local ok = pcall(function()
        upgrade_service_instance.hpm_upgrade:_process({
            system_id = 1,
            firmware_type = 1,
            cfg_path = '',
            hpm_path = '',
            context = construct_ctx(),
            spi_rate = 10,
            Multihost = false,
            Snapshots = {snapshot}
        })
    end)

    lu.assertEquals(ok, false)
    ok = pcall(function()
        upgrade_service_instance.hpm_upgrade:_process({
            system_id = 1,
            firmware_type = 1,
            cfg_path = '',
            hpm_path = '',
            context = construct_ctx(),
            spi_rate = 10,
            Multihost = true,
            ActivateComponents = {"IMU"},
            Snapshots = {snapshot}
        })
    end)
    lu.assertEquals(ok, false)
end

function TestMultihost:test_multihost_reset_bios()
    local snapshot = package_snapshot.new(construct_db(), {ActivatedStatus = 1}, 1)
    snapshot.SystemId = 1
    local upgrade_service_instance = upgrade_service.new(construct_db(), {})
    upgrade_service_instance:add_snapshot(1, snapshot)
    upgrade_service_instance.fructl_handler = fructl_handler
    fructl_handler.get_power_status = function (system_id)
        return 'OFF'
    end
    local get_service = bios_factory.get_service
    upgrade_service_instance.bios_factory = bios_factory
    bios_factory.get_service = function(service_name)
        local obj_util = {reset_bios = function(ctx, system_id)
            return 0
        end}
        return obj_util
    end
    local ok = pcall(function()
        upgrade_service_instance.hpm_upgrade:multihost_reset_bios({
            system_id = 1,
            firmware_type = 1,
            cfg_path = '',
            hpm_path = '',
            Snapshots = upgrade_service_instance.package_snapshot_collection
        })
    end)
    bios_factory.get_service = get_service
    lu.assertEquals(ok, true)
end

function TestMultihost:test_upgrade_force_service()
    -- 全檫升级测试
    local snapshot = package_snapshot.new(construct_db(), {ActivatedStatus = 1}, 1)
    local upgrade_service_instance = upgrade_service.new(construct_db(), {})
    local phrase = 'prepare'
    local cfg = {system_id = 1, para = {ForceUpgrade = "force"}}
    upgrade_service_instance.hpm_upgrade.upgrade = function(phrase, cfg, snapshots)
            
    end
    upgrade_service_instance.effective_upgrade = {}
    upgrade_service_instance:add_snapshot(1, snapshot)
    upgrade_service_instance:upgrade_hpm(phrase, cfg)
    upgrade_service_instance.hpm_upgrade:_set_state_machine(bios_enum.UpgradeEvent.PrepareStart)
    upgrade_service_instance.hpm_upgrade:_set_state_machine(bios_enum.UpgradeEvent.PrepareFinish)
    upgrade_service_instance.hpm_upgrade:_set_state_machine(bios_enum.UpgradeEvent.ProcessStart)
    upgrade_service_instance.hpm_upgrade:_set_state_machine(bios_enum.UpgradeEvent.ProcessFinish)
    upgrade_service_instance.hpm_upgrade:finish({
        system_id = 1,
        firmware_type = 1,
        cfg_path = '',
        hpm_path = '',
        context = construct_ctx()
    })
    test_activate_components(upgrade_service_instance, snapshot)
    test_patch(upgrade_service_instance, snapshot)
    test_process(upgrade_service_instance, snapshot)
end

function TestMultihost:test_upgrade_service()
    -- 原有测试逻辑。不走全檫升级
    local snapshot = package_snapshot.new(construct_db(), {ActivatedStatus = 1}, 1)
    local upgrade_service_instance = upgrade_service.new(construct_db(), {})
    local phrase = 'prepare'
    local cfg = {system_id = 1, para = {ForceUpgrade = "noforce"}}
    upgrade_service_instance:add_snapshot(1, snapshot)
    upgrade_service_instance:upgrade_hpm(phrase, cfg)
    upgrade_service_instance.hpm_upgrade:_set_state_machine(bios_enum.UpgradeEvent.PrepareStart)
    upgrade_service_instance.hpm_upgrade:_set_state_machine(bios_enum.UpgradeEvent.PrepareFinish)
    upgrade_service_instance.hpm_upgrade:_set_state_machine(bios_enum.UpgradeEvent.ProcessStart)
    upgrade_service_instance.hpm_upgrade:_set_state_machine(bios_enum.UpgradeEvent.ProcessFinish)
    upgrade_service_instance.hpm_upgrade:finish({
        system_id = 1,
        firmware_type = 1,
        cfg_path = '',
        hpm_path = '',
        context = construct_ctx()
    })
    test_activate_components(upgrade_service_instance, snapshot)
    test_patch(upgrade_service_instance, snapshot)
    test_process(upgrade_service_instance, snapshot)
end

function TestMultihost:test_cache_mem_silk_info()
    msg.read = function()
        local interfaces_and_properties = {}
        interfaces_and_properties[obj_def.MEMORY_INFO_INTERFACE] = true
        return "path", interfaces_and_properties
    end
    bios_object.memory_info_path = string.format('/bmc/kepler/Systems/%s/Memory', 1)
    bios_object.bus = {type = 'signal', path_namespace = '/bmc/kepler/Systems/1/Memory',
        interface = 'org.freedesktop.DBus.ObjectManager', member = 'InterfacesAdded'}

    bios_object.bus.match = function(self, obj, cb)
        cb(msg)
        return true
    end
    bios_object.queue = function()

    end

    local ok
    ok, _ = pcall(function()
        return bios_object:cache_mem_silk_info()
    end)
    lu.assertEquals(ok, true)
end

function TestMultihost:test_handle_memory_silk_config_add()
    local props = {}
    local mem_info_by_path = {}
    mem_info_by_path[obj_def.MEMORY_INFO_INTERFACE] = true
    bios_object.memory_info_path = string.format('/bmc/kepler/Systems/%s/Memory', 1)
    bios_object.cached_mem_info = {}
    bios_object.mem_info_by_path = mem_info_by_path
    props[obj_def.PROPERTY_MEM_CPU_ID] = {value = function ()
        return 1
    end}
    props[obj_def.PROPERTY_MEM_CHANNEL_ID] = {value = function ()
        return 1
    end}
    props[obj_def.PROPERTY_LOGICAL_CHANNEL_ID] = {value = function ()
        return 1
    end}
    props[obj_def.PROPERTY_MEM_DIMM_ID] = {value = function ()
        return 1
    end}
    props[obj_def.PROPERTY_MEM_NAME] = {value = function ()
        return "test"
    end}

    local ok
    ok, _ = pcall(function()
        return bios_object:handle_memory_silk_config_add(bios_object.memory_info_path, props)
    end)
    lu.assertEquals(ok, true)
end

function TestMultihost:test_handle_memory_silk_config_del()
    bios_object.cached_mem_info = {}
    local mem_info_by_path = {}
    mem_info_by_path[obj_def.MEMORY_INFO_INTERFACE] = true
    bios_object.mem_info_by_path = mem_info_by_path
    
    local ok
    ok, _ = pcall(function()
        return bios_object:handle_memory_silk_config_del(bios_object.memory_info_path)
    end)
    lu.assertEquals(ok, true)
end

function TestMultihost:test_get_memory_silk_config()
    local ok, _ = pcall(function()
        return bios_object:get_memory_silk_config()
    end)
    lu.assertEquals(ok, true)
    local mem_info_by_path = {}
    mem_info_by_path[obj_def.MEMORY_INFO_INTERFACE] = true
    bios_object.memory_info_path = string.format('/bmc/kepler/Systems/%s/Memory', 1)
    bios_object.cached_mem_info = {}
    bios_object.mem_info_by_path = mem_info_by_path
    local props = {}
    props[obj_def.PROPERTY_MEM_CPU_ID] = {value = function ()
        return 1
    end}
    props[obj_def.PROPERTY_MEM_CHANNEL_ID] = {value = function ()
        return 1
    end}
    props[obj_def.PROPERTY_LOGICAL_CHANNEL_ID] = {value = function ()
        return 1
    end}
    props[obj_def.PROPERTY_MEM_DIMM_ID] = {value = function ()
        return 1
    end}
    props[obj_def.PROPERTY_MEM_NAME] = {value = function ()
        return "test"
    end}
    props.path = "test"
    local get_service = bios_factory.get_service
    bios_factory.get_service = function()
        local obj_util = {get_object_list = function(name, path, int)
            return {
                props
            }
        end}
        return obj_util
    end
    local ok, _ = pcall(function()
        return bios_object:get_memory_silk_config()
    end)
    bios_factory.get_service = get_service
    lu.assertEquals(ok, true)
end

function TestMultihost:test_get_object_list()
    object_service.bus = {type = 'signal', path_namespace = '/bmc/kepler/Systems/1/Memory',
        interface = 'org.freedesktop.DBus.ObjectManager', member = 'InterfacesAdded'}
    mdb_service.get_sub_paths = function()
        rsp = {}
        rsp.SubPaths = {"test"}
        return rsp
    end
    object_service.get_object = function()
        return {}
    end
    object_service.bus.match = function(self, obj, cb)
        cb(msg)
        return true
    end
    local ok, _ = pcall(function()
        return object_service:get_object_list('object_service', "test", "test")
    end)
    lu.assertEquals(ok, true)
end

function TestMultihost:test_inner_upgrade()
    local snapshot = package_snapshot.new(construct_db(), {}, 1)
    snapshot.get_package_type = function ()
        return bios_enum.PackageType.Patch
    end
    local upgrade_service_instance = upgrade_service.new(construct_db(), {})
    upgrade_service_instance:add_snapshot(1, snapshot)
    local ok, _ = pcall(function()
        return upgrade_service_instance:inner_upgrade('')
    end)
    lu.assertEquals(ok, true)
end

function TestMultihost:test_check_sp_state()
    local get_service = bios_factory.get_service
    local ctx = {
        Privilege = '1',
        UserName = '<host sms>'
    }
    bios_factory.get_service = function ()
        local bios_ser = {}
        bios_ser.is_multihost = function()
            return true
        end
        return bios_ser
    end
    local _, ret = pcall(function()
        return privilege_check.check(ctx)
    end)
    lu.assertEquals(ret, true)
    bios_factory.get_service = function ()
        return nil
    end
    _, ret = pcall(function()
        return privilege_check.check(ctx)
    end)
    lu.assertEquals(ret, false)
    bios_factory.get_service = get_service
end


function TestMultihost:test_get_disk_silk_config()
    local ok, _ = pcall(function()
        return bios_service:get_disk_silk_config()
    end)
    lu.assertEquals(ok, true)
    local props = {}
    props[obj_def.PCIE_ADDR_INFO_CONTROLLER_TYPE] = {value = function ()
        return 2
    end}
    props[obj_def.PCIE_ADDR_INFO_SOCKET_ID] = {value = function ()
        return 1
    end}
    props[obj_def.PCIE_ADDR_INFO_DEVICE_PORT_ID] = {value = function ()
        return 1
    end}
    props[obj_def.PCIE_ADDR_INFO_SLOT_ID] = {value = function ()
        return 1
    end}
    props[obj_def.PCIE_ADDR_INFO_BUS] = {value = function ()
        return 1
    end}
    props[obj_def.PCIE_ADDR_INFO_DEVICE] = {value = function ()
        return 1
    end}
    props[obj_def.PCIE_ADDR_INFO_FUNCTION] = {value = function ()
        return 1
    end}
    local get_service = bios_factory.get_service
    bios_factory.get_service = function()
        local obj_util = {get_object_list = function(name, path, int)
            return {
                props
            }
        end}
        return obj_util
    end
    local ok, _ = pcall(function()
        return bios_service:get_disk_silk_config()
    end)
    bios_factory.get_service = get_service
    lu.assertEquals(ok, true)
end