-- Copyright (c) 2025 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 storageconfig = require('storageconfig.storageconfig_object')
local controller_collection = require('controller.controller_collection')
local controller = require('controller.controller_object')
local common_def = require('common_def')

local lu = require('luaunit')

TEST_Storage_Config = {}

function TEST_Storage_Config:test_change_power_state()
    local obj = {
        start_ctrl_loaded_check = function() end,
    }
    storageconfig.change_power_state(obj, 1)
    lu.assertEquals(obj.power_on, 1)

    storageconfig.change_power_state(obj, 0)
    lu.assertEquals(obj.power_on, 0)
    lu.assertEquals(obj.all_ctrl_loaded, false)
end

function TEST_Storage_Config:test_controller_collection_init()
    local obj = {
        controller_list = {},
        on_add_controller = {
            emit = function() end,
        },
        link_interface = function() end,
        on_os_state_changed = {
            on = function() end,
        },
    }
    local controller_obj = {
        Id = 0,
    }
    local origin_add_on = controller.on_add_object.on
    local origin_fold = controller.collection.fold
    local origin_del_on = controller.on_delete_object.on
    controller.on_add_object.on = function(c, cb)
        cb(controller_obj)
    end
    controller.on_delete_object.on = function(c, cb)
        cb(controller_obj)
    end
    controller.collection.fold = function(c, cb)
        cb(nil, controller_obj)
    end
    controller_collection.init(obj)
    lu.assertEquals(next(obj.controller_list), nil)

    controller.on_add_object.on = origin_add_on
    controller.collection.fold = origin_fold
    controller.on_delete_object.on = origin_del_on
end

function TEST_Storage_Config:test_controller_collection_link_interface()
    local collection_obj = {
        intf_map = {},
    }
    local controller_obj = {
        [common_def.CONTROLLER_INTRERFACE[1]] = 1,
    }

    controller_collection.link_interface(collection_obj, controller_obj, true)
    lu.assertEquals(collection_obj.intf_map[1], controller_obj)
end

-- 覆盖 init 中 on_update_flag 回调:self.obj 为 nil 分支 (line 124)
function TEST_Storage_Config:test_init_on_update_flag_no_obj()
    local signal = require('mc.signal')
    local c_storageconfig_object = require('storageconfig.storageconfig_object')
    local obj = {
        obj = nil,
        storageconfig_info = {},
        power_on = 1,
        controller_mask = 1,
        on_update_flag = signal.new(),
        start_ctrl_loaded_check = function() end,
    }
    setmetatable(obj, { __index = c_storageconfig_object })
    -- init 中会注册 c_storageconfig_object.on_add_object / on_delete_object 的回调
    c_storageconfig_object.init(obj)
    -- 触发 on_update_flag 回调,self.obj 为 nil 时进入 line 124 分支
    obj.on_update_flag:emit(0)
    lu.assertTrue(obj.need_sync)
end

-- 覆盖 start_drive_slot_allocation_monitor 中 bus 为 nil 的分支 (line 176-177)
function TEST_Storage_Config:test_start_drive_slot_allocation_monitor_no_bus()
    local c_tasks = require('mc.orm.tasks')
    local orig_get_instance = c_tasks.get_instance
    local mock_task = {
        loop = function(self, cb)
            cb(self)
            return self
        end,
        stop = function() end,
        set_timeout_ms = function(self, ms)
            return self
        end,
    }
    c_tasks.get_instance = function()
        return {
            new_task = function(self, name)
                return mock_task
            end,
        }
    end

    local obj = {
        drive_slot_task = nil,
        bus = nil,
        drive_slot_allocation_status = 255,
    }
    local c_storageconfig_object = require('storageconfig.storageconfig_object')
    c_storageconfig_object.start_drive_slot_allocation_monitor(obj)
    lu.assertEquals(obj.drive_slot_allocation_status, 255)

    c_tasks.get_instance = orig_get_instance
end

-- 覆盖 start_drive_slot_allocation_monitor 中 drive_slot_task 已存在的分支 (line 190-191)
function TEST_Storage_Config:test_start_drive_slot_allocation_monitor_already_running()
    local obj = {
        drive_slot_task = {},
    }
    local c_storageconfig_object = require('storageconfig.storageconfig_object')
    c_storageconfig_object.start_drive_slot_allocation_monitor(obj)
    lu.assertNotNil(obj.drive_slot_task)
end

-- 覆盖 start_drive_slot_allocation_monitor 中状态获取成功的分支 (line 197-202)
function TEST_Storage_Config:test_start_drive_slot_allocation_monitor_status_ok()
    local mdb = require('mc.mdb')
    local orig_get_object = mdb.get_object
    mdb.get_object = function(bus, path, intf)
        return { DriveSlotAllocationStatus = 1 }
    end

    local c_tasks = require('mc.orm.tasks')
    local orig_get_instance = c_tasks.get_instance
    c_tasks.get_instance = function()
        return {
            new_task = function(self, name)
                return {
                    loop = function(self, cb)
                        cb(self)
                        return self
                    end,
                    stop = function() end,
                    set_timeout_ms = function(self, ms)
                        return self
                    end,
                }
            end,
        }
    end

    local obj = {
        drive_slot_task = nil,
        bus = {},
        drive_slot_allocation_status = 255,
    }
    local c_storageconfig_object = require('storageconfig.storageconfig_object')
    c_storageconfig_object.start_drive_slot_allocation_monitor(obj)
    lu.assertEquals(obj.drive_slot_allocation_status, 1)

    mdb.get_object = orig_get_object
    c_tasks.get_instance = orig_get_instance
end

-- 覆盖 start_drive_slot_allocation_monitor 中超时的分支 (line 206-209)
function TEST_Storage_Config:test_start_drive_slot_allocation_monitor_timeout()
    local mdb = require('mc.mdb')
    local orig_get_object = mdb.get_object
    mdb.get_object = function(bus, path, intf)
        return { DriveSlotAllocationStatus = 255 }
    end

    local c_tasks = require('mc.orm.tasks')
    local orig_get_instance = c_tasks.get_instance
    local call_count = 0
    local mock_task = {
        loop = function(self, cb)
            call_count = call_count + 1
            cb(self)
            if call_count >= 100 then
                return self
            end
            return self
        end,
        stop = function() end,
        set_timeout_ms = function(self, ms)
            return self
        end,
    }
    c_tasks.get_instance = function()
        return {
            new_task = function(self, name)
                return mock_task
            end,
        }
    end

    local obj = {
        drive_slot_task = nil,
        bus = {},
        drive_slot_allocation_status = 255,
    }
    local c_storageconfig_object = require('storageconfig.storageconfig_object')
    c_storageconfig_object.start_drive_slot_allocation_monitor(obj)
    lu.assertEquals(obj.drive_slot_allocation_status, 255)

    mdb.get_object = orig_get_object
    c_tasks.get_instance = orig_get_instance
end

-- 覆盖 change_smbios_status (line 70-72)
function TEST_Storage_Config:test_change_smbios_status()
    local obj = {}
    storageconfig.change_smbios_status(obj, 1)
    lu.assertEquals(obj.smbios_status, 1)

    storageconfig.change_smbios_status(obj, 0)
    lu.assertEquals(obj.smbios_status, 0)
end

-- 覆盖 update_object_info need_sync=true 分支 (line 223-227)
function TEST_Storage_Config:test_update_object_info_need_sync()
    local obj = {
        need_sync = true,
        obj = { StorageConfigReady = 0 },
        storageconfig_info = { StorageConfigReady = 1 },
    }
    storageconfig.update_object_info(obj)
    lu.assertEquals(obj.obj.StorageConfigReady, 1)
end

-- 覆盖 update_object_info need_sync=false 分支 (line 224-226)
function TEST_Storage_Config:test_update_object_info_no_sync()
    local obj = {
        need_sync = false,
        obj = { StorageConfigReady = 0 },
        storageconfig_info = { StorageConfigReady = 1 },
    }
    storageconfig.update_object_info(obj)
    lu.assertEquals(obj.obj.StorageConfigReady, 0)
end

-- 覆盖 ctrl_count_changed (line 240-244)
function TEST_Storage_Config:test_ctrl_count_changed()
    local obj = { ctrl_count = 1, last_ctrl_count = 0 }
    lu.assertTrue(storageconfig.ctrl_count_changed(obj))
    lu.assertEquals(obj.last_ctrl_count, 1)

    -- 数量未变化
    lu.assertFalse(storageconfig.ctrl_count_changed(obj))
end

-- 覆盖 add_or_del_controller 各分支 (line 272-293)
function TEST_Storage_Config:test_add_or_del_controller()
    -- 添加不支持带外管理的卡 (OOBSupport ~= 1)
    local obj = {
        ctrl_count = 0,
        ctrl_index_list = {},
        ctrl_no_oob = {},
        ctrl_3004 = {},
        ctrl_no_3004 = {},
        start_ctrl_loaded_check = function() end,
    }
    local ctrl_no_oob = { Id = 0, OOBSupport = 0 }
    storageconfig.add_or_del_controller(obj, ctrl_no_oob, true)
    lu.assertEquals(obj.ctrl_count, 1)
    lu.assertEquals(obj.ctrl_no_oob[0], true)

    -- 添加 3004 卡 (OOBSupport == 1, TypeId == LSI_3004_WITH_IMR)
    local ctrl_3004 = { Id = 1, OOBSupport = 1, TypeId = common_def.LSI_3004_WITH_IMR }
    storageconfig.add_or_del_controller(obj, ctrl_3004, true)
    lu.assertEquals(obj.ctrl_count, 2)
    lu.assertEquals(obj.ctrl_3004[1], true)

    -- 添加非 3004 但支持带外管理的卡 (OOBSupport == 1, TypeId ~= LSI_3004_WITH_IMR)
    local ctrl_no_3004 = { Id = 2, OOBSupport = 1, TypeId = 99 }
    storageconfig.add_or_del_controller(obj, ctrl_no_3004, true)
    lu.assertEquals(obj.ctrl_count, 3)
    lu.assertEquals(obj.ctrl_no_3004[2], true)

    -- 删除控制器
    storageconfig.add_or_del_controller(obj, ctrl_no_oob, false)
    lu.assertEquals(obj.ctrl_count, 2)
    lu.assertEquals(obj.ctrl_no_oob[0], false)
end

-- 覆盖 support_oob 所有分支 (line 296-316)
function TEST_Storage_Config:test_support_oob()
    -- ctrl_no_oob 存在且 ctrl_no_3004 也存在 → false
    local obj = { ctrl_no_oob = { [0] = true }, ctrl_3004 = {}, ctrl_no_3004 = { [1] = true } }
    lu.assertFalse(storageconfig.support_oob(obj))

    -- ctrl_no_oob 存在且只有 ctrl_3004 → true
    obj = { ctrl_no_oob = { [0] = true }, ctrl_3004 = { [1] = true }, ctrl_no_3004 = {} }
    lu.assertTrue(storageconfig.support_oob(obj))

    -- ctrl_no_oob 存在且其他都为空 → false
    obj = { ctrl_no_oob = { [0] = true }, ctrl_3004 = {}, ctrl_no_3004 = {} }
    lu.assertFalse(storageconfig.support_oob(obj))

    -- 不存在 ctrl_no_oob 但存在 ctrl_no_3004 → true
    obj = { ctrl_no_oob = {}, ctrl_3004 = {}, ctrl_no_3004 = { [1] = true } }
    lu.assertTrue(storageconfig.support_oob(obj))

    -- 不存在 ctrl_no_oob 但存在 ctrl_3004 → true
    obj = { ctrl_no_oob = {}, ctrl_3004 = { [1] = true }, ctrl_no_3004 = {} }
    lu.assertTrue(storageconfig.support_oob(obj))

    -- 全部为空 → false
    obj = { ctrl_no_oob = {}, ctrl_3004 = {}, ctrl_no_3004 = {} }
    lu.assertFalse(storageconfig.support_oob(obj))
end

-- 覆盖 get_set_configuration_ready_flag 各分支 (line 319-332)
function TEST_Storage_Config:test_get_set_configuration_ready_flag()
    -- INVALID_U8 且 mask == 0 → false
    local obj = { configuration_ready_mask = 0 }
    lu.assertFalse(storageconfig.get_set_configuration_ready_flag(obj, common_def.INVALID_U8))

    -- INVALID_U8 且 mask ~= 0 → true
    obj.configuration_ready_mask = 5 -- 二进制 101
    lu.assertTrue(storageconfig.get_set_configuration_ready_flag(obj, common_def.INVALID_U8))

    -- ctrl_id >= SML_MAX_RAID_CONTROLLER → false
    lu.assertFalse(storageconfig.get_set_configuration_ready_flag(obj, common_def.SML_MAX_RAID_CONTROLLER))

    -- ctrl_id=0, mask=5 (101) → bit0 已设置 → true
    lu.assertTrue(storageconfig.get_set_configuration_ready_flag(obj, 0))

    -- ctrl_id=1, mask=5 (101) → bit1 未设置 → false
    lu.assertFalse(storageconfig.get_set_configuration_ready_flag(obj, 1))

    -- ctrl_id=2, mask=5 (101) → bit2 已设置 → true
    lu.assertTrue(storageconfig.get_set_configuration_ready_flag(obj, 2))
end

-- 覆盖 generate_abnormal_volume_exsits 各分支 (line 334-339)
function TEST_Storage_Config:test_generate_abnormal_volume_exsits()
    -- obj 存在, assert=true
    local obj = { obj = { VolumesStateAbnormal = 0 } }
    storageconfig.generate_abnormal_volume_exsits(obj, true)
    lu.assertEquals(obj.obj.VolumesStateAbnormal, 1)

    -- obj 存在, assert=false
    storageconfig.generate_abnormal_volume_exsits(obj, false)
    lu.assertEquals(obj.obj.VolumesStateAbnormal, 0)

    -- obj 为 nil
    obj = { obj = nil }
    storageconfig.generate_abnormal_volume_exsits(obj, true)
    lu.assertNil(obj.obj)
end

-- 覆盖 init on_update_flag 回调: power_on==0 → NOT_READY (line 146-148)
function TEST_Storage_Config:test_init_on_update_flag_power_off()
    local signal = require('mc.signal')
    local obj = {
        obj = { StorageConfigReady = 99 },
        storageconfig_info = {},
        power_on = 0,
        controller_mask = 1,
        on_update_flag = signal.new(),
        drive_slot_task = {},
    }
    setmetatable(obj, { __index = storageconfig })
    storageconfig.init(obj)
    obj.on_update_flag:emit(common_def.STORAGE_MGNT_STATE.STORAGE_MGNT_READY)
    lu.assertEquals(obj.obj.StorageConfigReady, common_def.STORAGE_MGNT_STATE.STORAGE_MGNT_NOT_READY)
end

-- 覆盖 init on_update_flag 回调: controller_mask==0 → NO_CTRLS (line 150-152)
function TEST_Storage_Config:test_init_on_update_flag_no_controllers()
    local signal = require('mc.signal')
    local obj = {
        obj = { StorageConfigReady = 99 },
        storageconfig_info = {},
        power_on = 1,
        controller_mask = 0,
        on_update_flag = signal.new(),
        drive_slot_task = {},
    }
    setmetatable(obj, { __index = storageconfig })
    storageconfig.init(obj)
    obj.on_update_flag:emit(common_def.STORAGE_MGNT_STATE.STORAGE_MGNT_READY)
    lu.assertEquals(obj.obj.StorageConfigReady, common_def.STORAGE_MGNT_STATE.STORAGE_MGNT_NO_CTRLS)
end

-- 覆盖 init on_update_flag 回调: value==OOB_NOT_SUPPORT (line 154-158)
function TEST_Storage_Config:test_init_on_update_flag_oob_not_support()
    local signal = require('mc.signal')
    local obj = {
        obj = { StorageConfigReady = 99 },
        storageconfig_info = {},
        power_on = 1,
        controller_mask = 1,
        configuration_ready_mask = 1,
        on_update_flag = signal.new(),
        drive_slot_task = {},
    }
    setmetatable(obj, { __index = storageconfig })
    storageconfig.init(obj)
    obj.on_update_flag:emit(common_def.STORAGE_MGNT_STATE.STORAGE_MGNT_OOB_NOT_SUPPORT)
    lu.assertEquals(obj.obj.StorageConfigReady, common_def.STORAGE_MGNT_STATE.STORAGE_MGNT_OOB_NOT_SUPPORT)
end

-- 覆盖 init on_update_flag 回调: configuration_ready_mask ~= controller_mask → CTRL_ABNOMAL (line 160-165)
function TEST_Storage_Config:test_init_on_update_flag_ctrl_abnormal()
    local signal = require('mc.signal')
    local obj = {
        obj = { StorageConfigReady = 99 },
        storageconfig_info = {},
        power_on = 1,
        controller_mask = 3, -- 二进制 11
        configuration_ready_mask = 1, -- 二进制 01, 不相等
        on_update_flag = signal.new(),
        drive_slot_task = {},
    }
    setmetatable(obj, { __index = storageconfig })
    storageconfig.init(obj)
    obj.on_update_flag:emit(common_def.STORAGE_MGNT_STATE.STORAGE_MGNT_READY)
    lu.assertEquals(obj.obj.StorageConfigReady, common_def.STORAGE_MGNT_STATE.STORAGE_MGNT_CTRL_ABNOMAL)
end

-- 覆盖 init on_update_flag 回调: 正常 ready 分支 (line 168-170)
function TEST_Storage_Config:test_init_on_update_flag_ready()
    local signal = require('mc.signal')
    local obj = {
        obj = { StorageConfigReady = 99 },
        storageconfig_info = {},
        power_on = 1,
        controller_mask = 1,
        configuration_ready_mask = 1,
        on_update_flag = signal.new(),
        drive_slot_task = {},
    }
    setmetatable(obj, { __index = storageconfig })
    storageconfig.init(obj)
    obj.on_update_flag:emit(common_def.STORAGE_MGNT_STATE.STORAGE_MGNT_READY)
    lu.assertEquals(obj.obj.StorageConfigReady, common_def.STORAGE_MGNT_STATE.STORAGE_MGNT_READY)
end