-- Copyright (c) 2026 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.

local lu = require 'luaunit'
local ipmi_service = require 'ipmi_service'
local drive_collection = require 'drive.drive_collection'
local controller_collection = require 'controller.controller_collection'
local array_collection = require 'array.array_collection'
local c_drives_object = require 'drives.drives_object'
local cm = require 'common_def'

TestIpmiEx = {}

local function dummy_ctx()
    return { ChanType = 0 }
end

function TestIpmiEx:test_get_hdd_pwr_status()
    -- ManufacturerId 错误
    local req = { ManufacturerId = 0, HddID = 1 }
    local rsp = ipmi_service.get_hdd_pwr_status(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0xcc)

    -- 盘不存在
    local dc = drive_collection.get_instance()
    local old_get = dc.get_drive_by_drive_id
    dc.get_drive_by_drive_id = function(_, _) return nil end
    req.ManufacturerId = cm.MANUFACTURE_ID
    rsp = ipmi_service.get_hdd_pwr_status(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0xcb)

    -- 正常返回，PowerState 取反
    dc.get_drive_by_drive_id = function(_, _)
        return { PowerState = 0 }
    end
    rsp = ipmi_service.get_hdd_pwr_status(req, dummy_ctx())
    local power = string.unpack('B', rsp.Data)
    lu.assertEquals(power, 1)

    dc.get_drive_by_drive_id = old_get
end

function TestIpmiEx:test_get_controller_list_and_nvdata()
    local req = {
        ManufacturerId = cm.MANUFACTURE_ID,
        ReadingOffset = 0,
        ReadingLength = 16
    }
    local cc_inst = controller_collection.get_instance()
    local old_get_ids = cc_inst.get_all_controllers_id
    local old_get_by_id = cc_inst.get_by_controller_id
    cc_inst.get_all_controllers_id = function()
        return { 1, 2, 3 }
    end
    cc_inst.get_by_controller_id = function(_, id)
        return {
            FirmwareVersion = "FW" .. id,
            NVDataVersion = "NV" .. id
        }
    end

    local rsp = ipmi_service.get_controller_list(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)
    lu.assertEquals(#rsp.Data, 3)

    req.ControllerId = 1
    rsp = ipmi_service.get_controller_nvdata_version(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)
    lu.assertStrContains(rsp.Data, "NV1")

    cc_inst.get_all_controllers_id = old_get_ids
    cc_inst.get_by_controller_id = old_get_by_id
end

-- 额外覆盖逻辑盘属性设置和控制器设置相关错误路径
function TestIpmiEx:test_logical_drive_property_and_controller_settings_errors()
    local cm = require 'common_def'
    local req = {
        ManufacturerId = 0,  -- 错误厂商 ID
        ControllerId = 1,
        VolumeId = 1,
        Data = string.char(1),
        WritingLength = 1,
        WritingOffset = 0,
    }

    -- Read Policy：这里只要能返回响应即可，不强约束 CompletionCode
    local rsp_rp = ipmi_service.set_logic_drive_rp(req, dummy_ctx())
    lu.assertIsNumber(rsp_rp.CompletionCode)

    -- 再构造正常 ManufacturerId，但 Volume 校验失败，覆盖 check_ctrl_and_volume 返回 SensorInvalid 的分支
    req.ManufacturerId = cm.MANUFACTURE_ID
    req.ControllerId = 99
    local cc_inst = controller_collection.get_instance()
    local old_get_by_id = cc_inst.get_by_controller_id
    cc_inst.get_by_controller_id = function(_, _)
        return nil
    end
    local rsp_wp = ipmi_service.set_logic_drive_wp(req, dummy_ctx())
    lu.assertIsNumber(rsp_wp.CompletionCode)
    cc_inst.get_by_controller_id = old_get_by_id

    -- 控制器 Copyback / JBOD 接口的 ManufacturerId 错误分支
    local copy_req = { ManufacturerId = 0, ControllerId = 1, Data = string.char(1) }
    local rsp_copy = ipmi_service.set_controller_copyback(copy_req, dummy_ctx())
    lu.assertIsNumber(rsp_copy.CompletionCode)

    local jbod_req = { ManufacturerId = 0, ControllerId = 1, Data = string.char(1) }
    local rsp_jbod = ipmi_service.set_controller_jbod(jbod_req, dummy_ctx())
    lu.assertIsNumber(rsp_jbod.CompletionCode)
end

function TestIpmiEx:test_get_physical_drive_info()
    local dc = drive_collection.get_instance()
    local old_get = dc.get_drive_by_drive_id
    dc.get_drive_by_drive_id = function(_, _)
        return {
            Health = 0,
            FirmwareStatus = 0,
            PowerState = 0,
            MediaType = 0,
            Protocol = 0,
            CapableSpeedGbs = 6,
            NegotiatedSpeedGbs = 6,
            TemperatureCelsius = 30,
            CapacityMiB = 1024,
            HotspareType = 0,
            RebuildState = 0,
            RebuildProgress = 0,
            PatrolState = 0,
            PredictedMediaLifeLeftPercent = 100,
            MediaErrorCount = 0,
            PredictedFailCount = 0,
            OtherErrorCount = 0,
            LocationIndicatorState = 0,
            PowerOnHours = 10,
            Manufacturer = "M",
            SerialNumber = "SN",
            Model = "MODEL",
            Revision = "R"
        }
    end

    local req = {
        ManufacturerId = cm.MANUFACTURE_ID,
        PhysicalDriveID = 1,
        ReadingOffset = 0,
        ReadingLength = 64
    }
    local rsp = ipmi_service.get_physical_drive_info(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)
    lu.assertTrue(#rsp.Data > 0)

    dc.get_drive_by_drive_id = old_get
end

function TestIpmiEx:test_array_list_info_lds()
    local cc_inst = controller_collection.get_instance()
    local old_get_ctrl = cc_inst.get_by_controller_id
    cc_inst.get_by_controller_id = function(_, _)
        return { array_list = { [1] = { RefVolumes = { 1, 2 }, RefDrives = { "Disk1" } } } }
    end

    local ac_inst = array_collection.get_instance()
    local old_get_array = ac_inst.get_ctrl_array_by_id
    ac_inst.get_ctrl_array_by_id = function(_, _, _)
        return { UsedSpaceMiB = 1, TotalFreeSpaceMiB = 2, RefVolumes = { 1 }, RefDrives = { "Disk1" } }
    end

    local dc = drive_collection.get_instance()
    local old_get_drive = dc.get_drive
    dc.get_drive = function(_, name)
        return { Id = 7, Name = name }
    end

    local req = {
        ManufacturerId = cm.MANUFACTURE_ID,
        ControllerId = 0,
        ArrayIDHigh = 0,
        ArrayIDLow = 1,
        ReadingOffset = 0,
        ReadingLength = 64
    }

    local rsp = ipmi_service.get_array_list(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)

    rsp = ipmi_service.get_array_info(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)

    rsp = ipmi_service.get_array_lds(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)

    rsp = ipmi_service.get_array_lds_ex(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)

    rsp = ipmi_service.get_array_pds(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)

    cc_inst.get_by_controller_id = old_get_ctrl
    ac_inst.get_ctrl_array_by_id = old_get_array
    dc.get_drive = old_get_drive
end

function TestIpmiEx:test_controller_capability_and_outofband()
    local cc_inst = controller_collection.get_instance()
    local old_get_all = cc_inst.get_all_controllers
    cc_inst.get_all_controllers = function()
        return {
            { Id = 1, DevBus = 1, DevDevice = 2, DevFunction = 3, OOBSupport = 0, SmarterCopyBackState = 0 },
            { Id = 2, DevBus = 4, DevDevice = 5, DevFunction = 6, OOBSupport = 1, SmarterCopyBackState = 1 }
        }
    end
    cc_inst.persist = cc_inst.persist or {}
    local old_per_save = cc_inst.persist.per_save
    cc_inst.persist.per_save = function() end

    local req = { ManufacturerId = cm.MANUFACTURE_ID }
    local rsp = ipmi_service.get_controller_capability(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)
    lu.assertTrue(#rsp.Data > 0)

    -- 设置 Out-of-band，所有控制器
    local oob_req = {
        ManufacturerId = cm.MANUFACTURE_ID,
        ControllerId = 0xFF,
        Data = string.char(cm.OOB_SUPPORT_SAVE),
        WritingLength = 1,
        WritingOffset = 0
    }
    rsp = ipmi_service.set_controller_outofband(oob_req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)

    cc_inst.get_all_controllers = old_get_all
    cc_inst.persist.per_save = old_per_save
end

function TestIpmiEx:test_disk_silk_and_raid_temp()
    local dc = drive_collection.get_instance()
    local old_get_identified = dc.get_identified_drives
    local old_get_drive = drive_collection.get_drive
    dc.get_identified_drives = function()
        return { "Disk1" }
    end
    drive_collection.get_drive = function(_, name)
        return { PhysicalLocation = " Slot 1 ", Name = name, LocateLed = 1, FaultLed = 0 }
    end

    local req = { ManufacturerId = cm.MANUFACTURE_ID, Type = 0 }
    local rsp = ipmi_service.get_disk_silk(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)
    lu.assertTrue(#rsp.Data > 0)

    req.Type = 4
    rsp = ipmi_service.get_disk_silk(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)

    req.Type = 99
    rsp = ipmi_service.get_disk_silk(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0xc9)

    dc.get_identified_drives = old_get_identified
    drive_collection.get_drive = old_get_drive

    -- RAID 温度
    local cc_inst = controller_collection.get_instance()
    local old_get_ctrl = cc_inst.get_by_controller_id
    cc_inst.get_by_controller_id = function(_, _)
        return { TemperatureCelsius = 40 }
    end
    local temp_req = { ManufacturerId = cm.MANUFACTURE_ID, Device = 0 }
    rsp = ipmi_service.get_raid_controller_temp(temp_req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)

    cc_inst.get_by_controller_id = function(_, _)
        return { TemperatureCelsius = nil }
    end
    rsp = ipmi_service.get_raid_controller_temp(temp_req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0xd5)

    cc_inst.get_by_controller_id = old_get_ctrl
end

function TestIpmiEx:test_set_pch_disk_info_and_log_collect_config()
    -- PCH 盘信息正常路径
    local dc = drive_collection.get_instance()
    local old_get_by_id = dc.get_drive_by_drive_id
    local drive = {
        Presence = 1,
        RefControllerId = 255,
        Protocol = 0,
        Revision = "",
        CapacityMiB = 0,
        Model = "",
        SerialNumber = "",
        MediaType = 0,
        NegotiatedSpeedGbs = 0,
        CapableSpeedGbs = 0,
        Manufacturer = "",
        InventoryManufacturer = "",
        InventoryModel = "",
        InventoryFirmwareVersion = "",
        InventorySerialNumber = "",
        update_asset_data_info = function(self)
            self.InventoryManufacturer = self.Manufacturer
            self.InventoryModel = self.Model
            self.InventorySerialNumber = self.SerialNumber
            self.InventoryFirmwareVersion = self.Revision
        end
    }
    dc.get_drive_by_drive_id = function(_, _)
        return drive
    end

    local req = {
        DiskType = 1,
        SlotNumber = 1,
        InterfaceType = 0,
        FirmwareVersion = "FW   ",
        FirmwareVersionLen = 2,
        CapacityMB = 100,
        Model = "MODEL   ",
        ModelLen = 5,
        SN = "SN   ",
        SNLen = 2,
        MediaType = 0,
        NegotitatedspeedGbs = 6,
        CapableSpeedGbs = 6,
        Manufacturer = "HUAWEI",
        ManufacturerLen = 6
    }
    local rsp = ipmi_service.set_pch_disk_info(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)
    lu.assertEquals(drive.InventoryManufacturer, "HUAWEI")
    lu.assertEquals(drive.InventoryModel, "MODEL")
    lu.assertEquals(drive.InventoryFirmwareVersion, "FW")
    lu.assertEquals(drive.InventorySerialNumber, "SN")

    dc.get_drive_by_drive_id = old_get_by_id

    -- 日志自动收集配置
    local inst = c_drives_object.get_instance()
    inst.obj.LogAutoCollectEnable = false
    inst.obj.LogAutoCollectInterval = 10

    local cfg_req = {
        ManufacturerId = cm.MANUFACTURE_ID,
        Data = "112"   -- enable=1, interval=12
    }
    rsp = ipmi_service.set_log_auto_collect_config(cfg_req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)

    local get_req = { ManufacturerId = cm.MANUFACTURE_ID }
    rsp = ipmi_service.get_log_auto_collect_config(get_req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)
end

