-- 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 skynet = require 'skynet'
local lu = require 'luaunit'
local sdbus = require 'sd_bus'
local utils = require 'mc.utils'
local test_common = require 'test_common.utils'
local test_data = require 'bma_nic'
local log = require 'mc.logging'
local mdb = require 'mc.mdb'
local context = require 'mc.context'
local worker = require 'worker.core'
local log_dump = require 'test_netcard.test_log_dump'
local test_manufacture = require 'test_manufacture.test_manufacture'
local test_ipmi = require 'test_ipmi.test_ipmi'
local app_base = require 'mc.app_base'
local json = require 'cjson'

require 'skynet.manager'
require 'network_adapter.json_types.NetworkAdapter'
require 'network_adapter.json_types.keplerSystemsNetworkAdapters'
require 'network_adapter.json_types.NetworkPort'
require 'network_adapter.json_types.Hardware'

require 'hwdiscovery.json_types.Connector'

-- RPC方法调用对象
local network_adapter_rpc
local network_adapter_card_rpc
local network_adapter_inventory_rpc
local host_agent_app = app_base.new('host_agent')
local cwd = utils.realpath('.')

log:set_log_module_name('network_adapter_test')
log:set_debug_log_type('local')
log:setLevel(log.INFO)

local function test_network_adapter_ipmi(bus)
    skynet.sleep(200)
    local test_ipmi_obj = test_ipmi.new(bus)
    test_ipmi_obj:test_GetIPMCConfiguration()
    test_ipmi_obj:test_SetIPMCConfiguration()
    test_ipmi_obj:test_GetNetworkAdapterChipModel()
end

local function test_mdb_get_network_adapter(bus)
    log:info('- test_mdb_get_network_adapter')
    local objs = mdb.get_sub_objects(bus, '/bmc/kepler/Systems/1/NetworkAdapters',
        'bmc.kepler.Systems.NetworkAdapter')
    local obj = objs:fold(function(obj)
        if obj.path:match('NetworkAdapter_1_010108') then
            return obj, false
        end
    end)
    lu.assertEquals(obj.SystemID, 1)
    lu.assertEquals(obj.SlotNumber, 1)
    lu.assertEquals(obj.Name, 'SC332')
    lu.assertEquals(obj.Model, 'Mellanox')
    lu.assertEquals(obj.Manufacturer, 'Mellanox')
end

local function test_mdb_get_inventory_network_adapter(bus)
    log:info('- test_mdb_get_inventory_network_adapter')
    local objs = mdb.get_sub_objects(bus, '/bmc/kepler/Systems/1/NetworkAdapters',
        'bmc.kepler.Inventory.Hardware')
    local obj = objs:fold(function(obj)
        if obj.path:match('NetworkAdapter_1_010108') then
            return obj, false
        end
    end)
    lu.assertEquals(obj.AssetName, 'SC332')
    lu.assertEquals(obj.AssetTag, 'N/A')
    lu.assertEquals(obj.AssetType, 'NetworkAdapter')
    lu.assertEquals(obj.FirmwareVersion, '')
    lu.assertEquals(obj.ManufactureDate, 'N/A')
    lu.assertEquals(obj.Manufacturer, 'Mellanox')
    lu.assertEquals(obj.Model, 'Mellanox')
    lu.assertEquals(obj.PCBVersion, '')
    lu.assertEquals(obj.PartNumber, 'N/A')
    lu.assertEquals(obj.SerialNumber, '1022862465555')
    lu.assertEquals(obj.Slot, '1')
    lu.assertEquals(obj.UUID, '00000000000080008000000000000000')
end

local function test_start_removing_device()
    log:info(
        '-------------------test start_removing_device start------------------------------------')
    local ok, _ = pcall(network_adapter_card_rpc.StartRemovingDevice, network_adapter_card_rpc,
        context.new(), 'chassisOCPCard1(SC332)')
    lu.assertEquals(ok, false)
    log:info('-------------------test start_removing_device end------------------------------------')
end

local function test_on_mdb_remove_object(bus)
    log:info(
        '-------------------test on_mdb_remove_object start------------------------------------')
    local num = network_adapter_rpc:GetNetworkAdapterNum(context.new())
    lu.assertEquals(num, 5) -- 有5张网卡

    local obj = mdb.get_object(bus, '/bmc/kepler/Connector/Connector_OCP_1_0101',
        'bmc.kepler.Connector')
    obj.Presence = 0
    local obj = mdb.get_object(bus, '/bmc/kepler/Connector/Connector_LOM_1_0101',
        'bmc.kepler.Connector')
    obj.Presence = 0
    skynet.sleep(300)

    num = network_adapter_rpc:GetNetworkAdapterNum(context.new())
    lu.assertEquals(num, 0) -- 两个connector presence重置为0，这时候应该没有任何卡

    log:info('-------------------test on_mdb_remove_object end------------------------------------')
end

local function call_host_agent(bus, ...)
    bus:call(host_agent_app.service_name, host_agent_app.app_path, host_agent_app.app_interface, ...)
end

local function mock_host_agent()
    local work = worker.new(0)
    work:start_module('mock_bma_dbus_resource')
    work:send('1', true)
    return work
end

local function ensure_vlan_4_ok(bus)
    for _ = 1, 3 do
        local vlan_path =
            '/bmc/kepler/Systems/1/NetworkAdapters/NetworkAdapter_2_010101/Ports/NetworkPort_0_010101/VLANs/4'
        local err = bus:pcall('bmc.kepler.network_adapter', vlan_path,
            'org.freedesktop.DBus.Properties', 'GetAll', 's', 'bmc.kepler.Systems.NetworkPort.VLAN')
        if not err then
            return true
        end
        skynet.sleep(100)
    end
    return false
end

local function test_bma_update(bus)
    local work = mock_host_agent()
    skynet.sleep(10)
    log:info('-------------test test_bma_update start-------------')

    call_host_agent(bus, 'update', 's', test_data.EthernetInterface)

    -- 修改旧的 ipv4 地址信息，再插入一个新 ipv4 地址
    local data = json.decode(test_data.EthernetInterface)
    data.IPv4Addresses[1].SubnetMask = '0.0.0.1'
    --
    data.IPv4Addresses[#data.IPv4Addresses + 1] = {
        Address = '127.0.0.0',
        AddressOrigin = 'Static',
        Gateway = {'196.128.0.0'},
        SubnetMask = '0.0.0.0'
    }
    call_host_agent(bus, 'update', 's', json.encode(data))

    -- 添加 VLAN 信息
    call_host_agent(bus, 'update', 's', test_data.VLanNetworkInterface)

    local ok = ensure_vlan_4_ok(bus)

    call_host_agent(bus, 'exit')

    work:stop()
    work:join()

    if not ok then
        log:error('ensure_vlan_4_ok failed')
    end
    log:info('-------------test test_bma_update end-------------')
end

local function prepare_test_data()
    log:info('---- prepare test data ----')
    local test_data_dir = skynet.getenv('TEST_DATA_DIR')
    os.execute('mkdir -p ' .. test_data_dir)

    -- 创建目录
    local dir_list = {'apps/network_adapter/mds', 'apps/hwdiscovery/mds', 'apps/ipmi_core/mds', 'csr', 'data', '/tmp/'}
    for _, path in pairs(dir_list) do
        os.execute('mkdir -p ' .. test_data_dir .. '/' .. path)
    end
    utils.copy_file('test/integration/test_data/root.sr', test_data_dir .. '/csr/root.sr')
    utils.copy_file('test/integration/test_data/14100513_EXU_01.sr',
        test_data_dir .. '/csr/14100513_EXU_01.sr')
    utils.copy_file('test/integration/test_data/14220246_VF_0.sr',
        test_data_dir .. '/csr/14220246_VF_0.sr')
    utils.copy_file('test/integration/test_data/14220247_15b31015_19e5d13b.sr',
        test_data_dir .. '/csr/14220247_15b31015_19e5d13b.sr')
    utils.copy_file('mds/schema.json', test_data_dir .. '/apps/network_adapter/mds/schema.json')
    utils.copy_file('mds/service.json', test_data_dir .. '/apps/network_adapter/mds/service.json')
    utils.copy_file('temp/opt/bmc/apps/hwdiscovery/mds/schema.json',
        test_data_dir .. '/apps/hwdiscovery/mds/schema.json')
    utils.copy_file('temp/opt/bmc/apps/hwdiscovery/mds/service.json',
        test_data_dir .. '/apps/hwdiscovery/mds/service.json')
    utils.copy_file('temp/opt/bmc/apps/ipmi_core/mds/schema.json',
        test_data_dir .. '/apps/ipmi_core/mds/schema.json')
    utils.copy_file('temp/test_data/apps/persistence/mmcblk0p8', test_data_dir .. 'mmcblk0p8')
end

local function clear_test_data(exit_test)
    log:info('- clear test data')
    local test_data_dir = cwd .. '/' .. skynet.getenv('TEST_DATA_DIR')
    if exit_test then
        skynet.timeout(0, function()
            skynet.sleep(20)
            skynet.abort()
            utils.remove_file(test_data_dir)
        end)
    else
        utils.remove_file(test_data_dir)
    end
end

-- 测试入口
local function main()
    log:info('================ test network_adapter start ================')
    local bus = sdbus.open_user(true)
    local CARD_1_PATH<const> = '/bmc/kepler/Systems/1/NetworkAdapters/NetworkAdapter_1_010108'
    local CARD_2_PATH<const> = '/bmc/kepler/Systems/1/NetworkAdapters/NetworkAdapter_2_010101'
    local CARD_INTERFACE<const> = 'bmc.kepler.Systems.NetworkAdapter'
    local INVENTORY_INTERFACE<const> = 'bmc.kepler.Inventory.Hardware'
    local count = 0

    local get_rpc_func = function()
        network_adapter_rpc = mdb.get_object(bus, '/bmc/kepler/Systems/1/NetworkAdapters',
            'bmc.kepler.Systems.NetworkAdapters')
        network_adapter_card_rpc = mdb.get_object(bus, CARD_1_PATH, CARD_INTERFACE)
        network_adapter_inventory_rpc = mdb.get_object(bus, CARD_1_PATH, INVENTORY_INTERFACE)
        local card_2 = mdb.get_object(bus, CARD_2_PATH, CARD_INTERFACE)
        assert(card_2)
    end

    while true do
        count = count + 1
        if count >= 30 then
            error('unable to get network_adapter object!')
        end

        local ok = pcall(get_rpc_func)
        if ok then
            break
        end

        skynet.sleep(300)
    end
    log:info('get mdb object complete in %d s', count)

    -- 初始化RPC方法调用对象
    test_network_adapter_ipmi(bus)

    -- 测试RPC调用
    local cases = require 'test_rpc.test_network_adapter_rpc'
    cases.test_entry(network_adapter_rpc)
    -- 测试光模块
    cases = require 'test_optical_module.test_optical_module'
    cases.test_entry(bus)
    local linkdown_msg_handler = require 'test_optical_module.test_optical_module_msg_handler'
    linkdown_msg_handler.test_optical_module_linkdown_msg_handler()

    test_mdb_get_network_adapter(bus)
    test_mdb_get_inventory_network_adapter(bus)
    test_bma_update(bus)
    test_start_removing_device()
    log_dump.test_log_dump_cb()
    log_dump.test_monitor_pmu_status()
    log_dump.test_init(bus)
    log_dump.test_get_log_dir()
    log_dump.test_create_dir()
    log_dump.test_delete_old_log_file()
    test_manufacture.test_all_dft_method(bus)
    test_on_mdb_remove_object(bus) -- 会从自发现删除网卡，必须放在最后

    skynet.call('network_adapter', 'lua', 'exit')
    log:info('============== test network_adapter complete ===============')
end

skynet.start(function()
    clear_test_data()
    prepare_test_data()
    test_common.dbus_launch()
    skynet.uniqueservice('sd_bus')
    skynet.uniqueservice('persistence/service/main')
    skynet.uniqueservice('maca/service/main')
    skynet.uniqueservice('ipmi_core/service/main')
    skynet.uniqueservice('main')
    skynet.uniqueservice('hwdiscovery/service/main')

    skynet.sleep(100)
    skynet.fork(function()
        local ok, err = pcall(main)
        clear_test_data(true)
        if not ok then
            log:info('It run failed reason = %s', err)
        end
    end)
end)
