-- 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 bs = require 'mc.bitstring'
local ipmi = require 'ipmi'
require 'ipmi'
local context = require 'mc.context'
local lu = require 'luaunit'
local comp_code = ipmi.types.Cc
local log = require 'mc.logging'
local chassis_service = require 'chassis_service'

local test_ipmi = {}

-- 测试发送ipmi命令
local function send_ipmi_cmd(bus, ipmi_req)
    ipmi_req.Payload = ipmi_req.Payload .. '\x00'
    local ipmi_package = bs.new('<<_,_:2,DestNetFn:6,_:3/unit:8,Cmd,Payload/string>>')
    local req = ipmi_package:pack(ipmi_req)
    -- ctx打桩
    local channel_type = require 'ipmi.enums'.ChannelType
    local cjson = require 'cjson'
    local ctx = cjson.encode({
        ChanType = channel_type.CT_ME:value(),
        Instance = 0,
        session = {
            user = {
                name = 'test'
            }
        }
    })
    local rsp = bus:call('bmc.kepler.ipmi_core', '/bmc/kepler/IpmiCore',
        'bmc.kepler.IpmiCore', 'Route', 'a{ss}ayay', require 'mc.context'.new(), req, ctx)
    return rsp
end

function test_ipmi.test_get_device(bus)
    -- Devices长度非法(0)
    local req = {DestNetFn = 0x30, Cmd = 0x92, Payload = '\xdb\x07\x00\x4E\x00\x00\x00\x00'}
    local resp = send_ipmi_cmd(bus, req)
    local completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, comp_code.ReqDataLenInvalid)

    -- DeviceNum不等于Devices长度
    req = {DestNetFn = 0x30, Cmd = 0x92, Payload = '\xdb\x07\x00\x4E\x01\x00\x00\x00\x01\x02'}
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, comp_code.InvalidFieldRequest)

    -- Devices字段非法
    req = {DestNetFn = 0x30, Cmd = 0x92, Payload = '\xdb\x07\x00\x4E\x02\x02\x00\x00\x00\x02'}
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, comp_code.InvalidFieldRequest)

    -- Devices字段合法
    req = {DestNetFn = 0x30, Cmd = 0x92, Payload = '\xdb\x07\x00\x4E\x02\x02\x00\x00\x01\x02'}
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, comp_code.Success)

    -- Devices字段合法
    req = {DestNetFn = 0x30, Cmd = 0x92, Payload = '\xdb\x07\x00\x4E\x02\x02\x00\x00\x01\x0D'}
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, comp_code.Success)

    -- 获取所有Devices字段
    req = {DestNetFn = 0x30, Cmd = 0x92, Payload = '\xdb\x07\x00\x4E\xff\x00\x00\x00\x00'}
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, comp_code.Success)
end

function test_ipmi.test_chassis_identify(bus)
    -- 参数数量过多
    local req = {DestNetFn = 0x00, Cmd = 0x04, Payload = '\x01\x01\x01'}
    local resp = send_ipmi_cmd(bus, req)
    local completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.ReqDataLenInvalid)
    -- 1个参数
    req = {DestNetFn = 0x00, Cmd = 0x04, Payload = '\x01'}
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.Success)
    -- 2个参数
    req = {DestNetFn = 0x00, Cmd = 0x04, Payload = '\x01\x01'}
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.Success)
end

function test_ipmi.test_get_chassis_capabilities(bus)
    local req = {DestNetFn = 0x00, Cmd = 0x00, Payload = ''}
    local resp = send_ipmi_cmd(bus, req)
    local completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.Success)
end

function test_ipmi.test_set_chassis_capabilities(bus)
    -- 参数数量合法
    local req = {DestNetFn = 0x00, Cmd = 0x05, Payload = '\x01\x02\x03\x04\x05\x06'}
    local resp = send_ipmi_cmd(bus, req)
    local completion_code = string.unpack('B', resp)
    local flag, fru, sdr, sel, sys, bridge
    lu.assertEquals(completion_code, ipmi.types.Cc.Success)
    req = {DestNetFn = 0x00, Cmd = 0x00, Payload = ''}
    resp = send_ipmi_cmd(bus, req)
    completion_code, flag, fru, sdr, sel, sys, bridge = string.unpack('BBBBBBB', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.Success)
    lu.assertEquals(flag, 0x01)
    lu.assertEquals(fru, 0x02)
    lu.assertEquals(sdr, 0x03)
    lu.assertEquals(sel, 0x04)
    lu.assertEquals(sys, 0x05)
    lu.assertEquals(bridge, 0x06)
    -- 参数数量过多
    req = {DestNetFn = 0x00, Cmd = 0x05, Payload = '\x01\x02\x03\x04\x05\x06\x07'}
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.ReqDataLenInvalid)
    -- 参数数量过少
    req = {DestNetFn = 0x00, Cmd = 0x05, Payload = '\x01\x02\x03\x04\x05'}
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.ReqDataLenInvalid)
end

function test_ipmi.test_get_superpod_label_info(bus)
    local req = {DestNetFn = 0x30, Cmd = 0x94, Payload = '\xdb\x07\x00\x6A\x04\x00\x00\x00\x08'}
    local resp = send_ipmi_cmd(bus, req)
    local completion_code, manufacture_id, end_flag, data = string.unpack('BI3BI8', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.Success)
    lu.assertEquals(manufacture_id, 0x0007db)
    lu.assertEquals(end_flag, 0)
    lu.assertEquals(data, 0x0180FFFFFFFFFFFF)
end
 
 
function test_ipmi.test_set_superpod_label_info(bus)
    -- 设置三个属性都为1
    local req = {
        DestNetFn = 0x30,
        Cmd = 0x94,
        Payload = '\xdb\x07\x00\x6B\x04\x00\x00\x00\x00\x09\x00\x01\x00\x00\x00\x01\x00\x01\x00'
    }
    local resp = send_ipmi_cmd(bus, req)
    local completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.Success)
    -- 长度异常
    req = {
        DestNetFn = 0x30,
        Cmd = 0x94,
        Payload = '\xdb\x07\x00\x6B\x04\x00\x00\x00\x00\x0A\x00\x01\x00\x00\x00\x01\x00\x01\x00\x00'
    }
    local resp = send_ipmi_cmd(bus, req)
    local completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.CommandDisabled)
    -- 设置SuperPodId为2
    req = {DestNetFn = 0x30, Cmd = 0x94, Payload = '\xdb\x07\x00\x6B\x04\x00\x00\x00\x00\x05\x01\x02\x00\x00\x00'}
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.Success)
    -- 设置ServerIndex为2
    req = {DestNetFn = 0x30, Cmd = 0x94, Payload = '\xdb\x07\x00\x6B\x04\x00\x00\x00\x00\x03\x02\x02\x00'}
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.Success)
    -- 设置SuperPodSize为2
    req = {DestNetFn = 0x30, Cmd = 0x94, Payload = '\xdb\x07\x00\x6B\x04\x00\x00\x00\x00\x03\x03\x02\x00'}
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.Success)
    -- 长度异常
    req = {DestNetFn = 0x30, Cmd = 0x94, Payload = '\xdb\x07\x00\x6B\x04\x00\x00\x00\x00\x04\x03\x00\xff\x00'}
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.CommandDisabled)
    -- 设置SuperPodSize为0xffff
    req = {DestNetFn = 0x30, Cmd = 0x94, Payload = '\xdb\x07\x00\x6B\x04\x00\x00\x00\x00\x03\x03\xff\xff'}
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.ParmOutOfRange)
end
 
function test_ipmi.test_get_superpod_label_Enabled(bus)
    local req = {DestNetFn = 0x30, Cmd = 0x94, Payload = '\xdb\x07\x00\x6A\x05\x00\x00\x00\x01'}
    local resp = send_ipmi_cmd(bus, req)
    local completion_code, manufacture_id, end_flag, data = string.unpack('BI3BB', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.Success)
    lu.assertEquals(manufacture_id, 0x0007db)
    lu.assertEquals(end_flag, 0)
    lu.assertEquals(data, 0x01)
end

function test_ipmi.test_set_superpod_label_enabled(bus)
    -- 设置属性为true
    local req = {
        DestNetFn = 0x30,
        Cmd = 0x94,
        Payload = '\xdb\x07\x00\x6B\x05\x00\x00\x00\x00\x02\x00\x01'
    }
    local resp = send_ipmi_cmd(bus, req)
    local completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.Success)
    -- 设置属性为false
    local req = {
        DestNetFn = 0x30,
        Cmd = 0x94,
        Payload = '\xdb\x07\x00\x6B\x05\x00\x00\x00\x00\x02\x00\x00'
    }
    local resp = send_ipmi_cmd(bus, req)
    local completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.Success)
    -- 设置属性位超出限制
    local req = {
        DestNetFn = 0x30,
        Cmd = 0x94,
        Payload = '\xdb\x07\x00\x6B\x05\x00\x00\x00\x00\x02\x00\x02'
    }
    local resp = send_ipmi_cmd(bus, req)
    local completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.ParmOutOfRange)
end

function test_ipmi.test_set_panel_button_enabled(bus)

    -- ctx打桩
    local channel_type = require 'ipmi.enums'.ChannelType

    local ctx = {
        ChanType = channel_type.CT_ME:value(),
        Instance = 0,
        SystemId = 2,
        session = {
            user = {
                name = 'test'
            }
        }
    }
    local req = {
        State = 0x01
    }

    -- 测试SystemId != 1 应该返回错误码 CommandNotAvailable
    
    local rsp = chassis_service:ipmi_set_panel_button_enabled(req, ctx)
    lu.assertEquals(rsp.CompletionCode, ipmi.types.Cc.DataNotAvailable)

    local tmp_fru_ctrl_object = chassis_service.get_fru_ctrl_object

    chassis_service.get_fru_ctrl_object = function()
        return nil
    end    
    ctx.SystemId = 1
    rsp = chassis_service:ipmi_set_panel_button_enabled(req, ctx)
    chassis_service.get_fru_ctrl_object = tmp_fru_ctrl_object
    lu.assertEquals(rsp.CompletionCode, ipmi.types.Cc.DataNotAvailable)

    -- 测试启用面板按钮屏蔽 (State = 0x01)
    req = {
        DestNetFn = 0x00,
        Cmd = 0x0a,
        Payload = '\x01'
    }
    local resp = send_ipmi_cmd(bus, req)
    local completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.Success)
    
    -- 测试禁用面板按钮屏蔽 (State = 0x00)
    req = {
        DestNetFn = 0x00,
        Cmd = 0x0a,
        Payload = '\x00'
    }
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.Success)
    
    -- 测试参数长度异常 (空payload)
    req = {
        DestNetFn = 0x00,
        Cmd = 0x0a,
        Payload = ''
    }
    resp = send_ipmi_cmd(bus, req)
    completion_code = string.unpack('B', resp)
    lu.assertEquals(completion_code, ipmi.types.Cc.ReqDataLenInvalid)
end

function test_ipmi.test_get_chassis_status(bus)
    -- 测试获取机箱状态 (Get Chassis Status)
    local req = {
        DestNetFn = 0x00,
        Cmd = 0x01,
        Payload = ''
    }
    local resp = send_ipmi_cmd(bus, req)
    
    -- 检查响应长度，应该包含5个字节：CompletionCode + CurrentPowerState 
    -- LastPowerEvent + MiscChassisState + FrontPanelButton
    lu.assertGreaterThan(#resp, 4, "Response should contain at least 5 bytes")
    
    -- 解析响应数据
    local completion_code, current_power_state, last_power_event, 
      misc_chassis_state, front_panel_button = string.unpack('BBBBB', resp)
    log:info('Chassis Status - Power: %d, LastEvent: %d, Misc: %d, Button: %d', 
        current_power_state, last_power_event, misc_chassis_state, front_panel_button)
    
    -- 检查完成码
    lu.assertEquals(completion_code, ipmi.types.Cc.Success, "Completion code should be Success")
    
    -- 检查电源状态 (CurrentPowerState)
    lu.assertIsNumber(current_power_state, "CurrentPowerState should be a number")
    lu.assertGreaterThanOrEqual(current_power_state, 0, "CurrentPowerState should be >= 0")
    lu.assertLessThanOrEqual(current_power_state, 255, "CurrentPowerState should be <= 255")
    
    -- 检查最后电源事件 (LastPowerEvent)
    lu.assertIsNumber(last_power_event, "LastPowerEvent should be a number")
    lu.assertGreaterThanOrEqual(last_power_event, 0, "LastPowerEvent should be >= 0")
    lu.assertLessThanOrEqual(last_power_event, 255, "LastPowerEvent should be <= 255")
    
    -- 检查其他机箱状态 (MiscChassisState)
    lu.assertIsNumber(misc_chassis_state, "MiscChassisState should be a number")
    lu.assertGreaterThanOrEqual(misc_chassis_state, 0, "MiscChassisState should be >= 0")
    lu.assertLessThanOrEqual(misc_chassis_state, 255, "MiscChassisState should be <= 255")
    
    -- 检查前面板按钮状态 (FrontPanelButton)
    lu.assertIsNumber(front_panel_button, "FrontPanelButton should be a number")
    lu.assertGreaterThanOrEqual(front_panel_button, 0, "FrontPanelButton should be >= 0")
    lu.assertLessThanOrEqual(front_panel_button, 255, "FrontPanelButton should be <= 255")
    
    log:info('Chassis Status - Power: %d, LastEvent: %d, Misc: %d, Button: %d', 
        current_power_state, last_power_event, misc_chassis_state, front_panel_button)
end


function test_ipmi.test_superpod_label_enabled(bus)
    local service = 'bmc.kepler.chassis'
    local path = '/bmc/kepler/Chassis/1/SuperPodLabel'
    local intf = 'bmc.kepler.Chassis.SuperPodLabel'
    local ret = pcall(bus.call, bus, service, path, intf, 'SetSuperPodEnabled', 
                'a{ss}b', require 'mc.context'.new(), true)
    assert(ret)
end

function test_ipmi.test_superpod_size(bus)
    local service = 'bmc.kepler.chassis'
    local path = '/bmc/kepler/Chassis/1/SuperPodLabel'
    local intf = 'bmc.kepler.Chassis.SuperPodLabel'
    local ret = pcall(bus.call, bus, service, path, intf, 'SetSuperPodSize', 
                'a{ss}q', require 'mc.context'.new(), 1)
    assert(ret)
end

function test_ipmi.test_server_index(bus)
    local service = 'bmc.kepler.chassis'
    local path = '/bmc/kepler/Chassis/1/SuperPodLabel'
    local intf = 'bmc.kepler.Chassis.SuperPodLabel'
    local ret = pcall(bus.call, bus, service, path, intf, 'SetServerIndex', 
                'a{ss}q', require 'mc.context'.new(), 1)
    assert(ret)
end

function test_ipmi.test_superpod_id(bus)
    local service = 'bmc.kepler.chassis'
    local path = '/bmc/kepler/Chassis/1/SuperPodLabel'
    local intf = 'bmc.kepler.Chassis.SuperPodLabel'
    local ret = pcall(bus.call, bus, service, path, intf, 'SetSuperPodId', 
                'a{ss}u', require 'mc.context'.new(), 1)
    assert(ret)
end

function test_ipmi.main(bus)
    log:info('================ test device capabilities start ================')
    test_ipmi.test_get_superpod_label_info(bus)
    test_ipmi.test_get_superpod_label_Enabled(bus)
    test_ipmi.test_set_superpod_label_info(bus)
    test_ipmi.test_set_superpod_label_enabled(bus)
    test_ipmi.test_superpod_label_enabled(bus)
    test_ipmi.test_server_index(bus)
    test_ipmi.test_superpod_id(bus)
    test_ipmi.test_superpod_size(bus)
    test_ipmi.test_get_device(bus)
    test_ipmi.test_chassis_identify(bus)
    test_ipmi.test_get_chassis_capabilities(bus)
    test_ipmi.test_set_chassis_capabilities(bus)
    test_ipmi.test_set_panel_button_enabled(bus)
    test_ipmi.test_get_chassis_status(bus)
    log:info('================ test device capabilities end ================')
end

return test_ipmi