-- 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 lu = require 'luaunit'
local file_sec = require 'utils.file'
local utils = require 'mc.utils'
local c_controller = require 'controller.controller_object'
local drive_collection = require 'drive.drive_collection'
local volume_collection = require 'volume.volume_collection'
local controller_collection = require 'controller.controller_collection'
local array_collection = require 'array.array_collection'
local volume_obj_info = require 'volume.volume_obj_info'
local ipmi_service = require 'ipmi_service'
local project_dir = os.getenv('PROJECT_DIR')

TEST_ipmi = {}

local cm = require 'common_def'
local rpc_service_controller = require 'rpc_services.rpc_service_controller'
local rpc_service_volume = require 'rpc_services.rpc_service_volume'

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

-- 测试获取指定逻辑盘对应的物理盘ID
function TEST_ipmi:test_get_logical_drive_pds()
    local req = {
        ManufacturerId = 0x0007DB,
        ControllerId = 0,
        VolumeId = 0
    }
    storage_app = {
        bus = 1
    }
    volume_collection.new(storage_app).volume_list = {
        ["0:0"] = {["RefDriveList"] = {[1] = "Drive_3", [2] = "Drive_5"}}
    }
    volume_collection.new(storage_app).get_ctrl_volume_by_id = function (...)
        return  {["RefDriveList"] = {"Drive_3", "Drive_5"}}
    end
    drive_collection.get_instance().get_drive = function (self, drive_name)
        return {["Id"] = tonumber(string.match(drive_name, "_(%d+)$"))}
    end
    rsp = ipmi_service.get_logical_drive_pds(req,{})
    -- 检查信息
    lu.assertEquals(rsp.CompletionCode, 0)
    lu.assertEquals(rsp.ManufactureId, 0x0007DB)
    lu.assertNotEquals(rsp.Data, nil)
    local result = {}
    local pos = 1
    while pos <= #rsp.Data do
        local value, new_pos = string.unpack('B', rsp.Data, pos)
        table.insert(result, value)
        pos = new_pos
    end
    lu.assertEquals(result, {3, 5})

    volume_collection.new(storage_app).get_ctrl_volume_by_id = function (...)
        return  nil
    end
    rsp = ipmi_service.get_logical_drive_pds(req,{})
    lu.assertEquals(rsp.CompletionCode, 0xcb)
end

function TEST_ipmi:test_get_cachecade_associated_lds_base()
    local req = {
        ManufacturerId = 0x0007DB,
        ControllerId = 0,
        VolumeId = 0
    }
    storage_app = {
        bus = 1
    }
    volume_collection.new(storage_app).get_ctrl_volume_by_id = function (...)
        return  {["AssociatedCacheCadeVolume"] = {1}}
    end
    rsp = ipmi_service.get_cachecade_associated_lds(req, {})
    -- 检查信息
    lu.assertEquals(rsp.CompletionCode, 0)
    lu.assertEquals(rsp.ManufactureId, 0x0007DB)
    lu.assertNotEquals(rsp.Data, nil)
    local result = {}
    local pos = 1
    while pos <= #rsp.Data do
        local value, new_pos = string.unpack('B', rsp.Data, pos)
        table.insert(result, value)
        pos = new_pos
    end
    lu.assertEquals(result, {1})

    volume_collection.new(storage_app).get_ctrl_volume_by_id = function (...)
        return  nil
    end
    rsp = ipmi_service.get_cachecade_associated_lds(req, {})
    lu.assertEquals(rsp.CompletionCode, 0xcb)
end

function TEST_ipmi:test_get_controller_fw_version()
    local req = {
        ManufacturerId = 0x0107DB,
        ControllerId = 0,
    }
    rsp = ipmi_service.get_controller_fw_version(req,{})
    lu.assertEquals(rsp.CompletionCode, 0xcc)

    req.ManufacturerId = 0x0007DB

    c_controller.collection = {
        ['find'] = function(...) return nil end ,
        ['fold'] = function(...) return nil end
    }
    controller_collection.new().get_by_controller_id = function (...)
        return  nil
    end
    rsp = ipmi_service.get_controller_fw_version(req,{})
    lu.assertEquals(rsp.CompletionCode, 0xcb)
end

function TEST_ipmi:test_get_array_pds()
    local req = {
        ManufacturerId = 0x0107DB,
        ArrayIDHigh = 0,
        ArrayIDLow =0
    }
    rsp = ipmi_service.get_array_pds(req,{})
    lu.assertEquals(rsp.CompletionCode, 0xcc)

    req.ManufacturerId = 0x0007DB
    storage_app = {
        bus = 1
    }
    array_collection.new(storage_app).get_ctrl_array_by_id = function (...)
        return  nil
    end
    rsp = ipmi_service.get_array_pds(req,{})
    lu.assertEquals(rsp.CompletionCode, 0xcb)
end

function TEST_ipmi:test_get_physical_drive_led_status()
    local req = {
        ManufacturerId = 0x0107DB,
        PhysicalDriveID = 0
    }
    rsp = ipmi_service.get_physical_drive_led_status(req,{})
    lu.assertEquals(rsp.CompletionCode, 0xcc)

    req.ManufacturerId = 0x0007DB
    storage_app = {
        bus = 1
    }
    drive_collection.new().get_drive_by_drive_id = function (...)
        return  nil
    end
    rsp = ipmi_service.get_physical_drive_led_status(req,{})
    lu.assertEquals(rsp.CompletionCode, 0xcb)
end

function TEST_ipmi:test_set_logical_drive_name()
    local req = {
        ManufacturerId = 0x0107DB,
        Data ={},
        WritingLength = 1,
        WritingOffset = 1
    }
    rsp = ipmi_service.set_logical_drive_name(req,{})
    lu.assertEquals(rsp.CompletionCode, 0xcc)

    -- ManufacturerId 正常 + 名称合法，覆盖成功路径
    local name = "LD_OK"
    local data = string.char(#name) .. name
    req = {
        ManufacturerId = cm.MANUFACTURE_ID,
        ControllerId = 1,
        VolumeId = 2,
        Data = data,
        WritingLength = #data,
        WritingOffset = 0,
    }

    -- stub rpc_service_volume.volume_operate，避免真实 RPC 依赖
    local old_volume_operate = rpc_service_volume.volume_operate
    rpc_service_volume.volume_operate = function(_, method, ctrl_id, vol_id, ctx, value)
        lu.assertEquals(method, "SetName")
        lu.assertEquals(ctrl_id, req.ControllerId)
        lu.assertEquals(vol_id, req.VolumeId)
        lu.assertEquals(value, name)
        return true
    end

    local rsp_ok = ipmi_service.set_logical_drive_name(req, dummy_ctx())
    -- 这里只要求调用链路打通，不强约束具体返回码
    lu.assertIsNumber(rsp_ok.CompletionCode)

    rpc_service_volume.volume_operate = old_volume_operate
end

-- 逻辑盘设置相关成功路径和典型失败路径
function TEST_ipmi:test_logical_drive_settings_success_and_errors()
    -- stub controller_collection / volume 列表，覆盖 check_ctrl_and_volume 成功路径
    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(_, id)
        return {
            Id = id,
            get_ctrl_volume_list = function()
                return {
                    { volume_id = 1 },
                    { volume_id = 2 },
                }
            end
        }
    end

    -- stub rpc_service_volume，强制 volume_operate 成功返回
    local old_volume_operate = rpc_service_volume.volume_operate
    rpc_service_volume.volume_operate = function(_, method, ctrl_id, vol_id, ctx, val)
        return true
    end

    -- 构造 1 字节数据的写策略请求，走 set_logical_drive_wp 成功路径
    local data = string.char(1)
    local req = {
        ManufacturerId = cm.MANUFACTURE_ID,
        ControllerId = 1,
        VolumeId = 1,
        Data = data,
        WritingLength = #data,
        WritingOffset = 0,
    }
    local rsp = ipmi_service.set_logic_drive_wp(req, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)

    -- 恢复 stub
    cc_inst.get_by_controller_id = old_get_by_id
    rpc_service_volume.volume_operate = old_volume_operate
end

function TEST_ipmi:test_create_logical_drive_on_new_array_and_delete()
    -- stub rpc_service_controller，覆盖 ctrl_operate 成功/失败两种路径
    local old_ctrl_operate = rpc_service_controller.ctrl_operate
    rpc_service_controller.ctrl_operate = function(_, op, ctrl_id, ctx, ...)
        if op == 'CreateVolumeInNewArray' then
            return cm.SM_CODE_SUCCESS
        elseif op == 'DeleteVolume' then
            return cm.SM_CODE_SUCCESS
        end
        return cm.SM_CODE_INVALID_RAID_LEVEL
    end

    -- 成功创建新阵列上的逻辑盘
    local name = "LD1"
    local raid_type = 1
    local capacity = 100
    local capacity_unit = 1
    local strip_size = 1
    local read_policy = 0
    local write_policy = 0
    local io_policy = 0
    local access_policy = 0
    local dcp = 0
    local init_mode = 0
    local span_num = 1
    local span_drive_num = 2
    local span_ids = string.char(1, 2)
    local data = string.char(#name) .. name ..
        string.char(raid_type) ..
        string.pack('I4', capacity) ..
        string.char(capacity_unit, strip_size, read_policy, write_policy,
            io_policy, access_policy, dcp, init_mode, span_num, span_drive_num) ..
        span_ids

    local req_new = {
        ManufacturerId = cm.MANUFACTURE_ID,
        ControllerId = 1,
        Data = data,
        WritingLength = #data,
        WritingOffset = 0,
    }
    local rsp = ipmi_service.create_logical_drive_on_new_array(req_new, dummy_ctx())
    lu.assertEquals(rsp.CompletionCode, 0x00)

    -- 删除逻辑盘成功路径
    local req_del = {
        ManufacturerId = cm.MANUFACTURE_ID,
        ControllerId = 1,
        VolumeId = 1,
    }
    local rsp_del = ipmi_service.delete_logical_drive(req_del, dummy_ctx())
    lu.assertEquals(rsp_del.CompletionCode, 0x00)

    rpc_service_controller.ctrl_operate = old_ctrl_operate
end

function TEST_ipmi:test_start_drive_collect_log()
    local req = {
        ManufacturerId = 0x0107DB,
    }
    rsp = ipmi_service.start_drive_collect_log(req,{})
    lu.assertEquals(rsp.CompletionCode, 0xcc)
end

function TEST_ipmi:test_get_log_auto_collect_config()
    local req = {
        ManufacturerId = 0x0107DB,
    }
    rsp = ipmi_service.get_log_auto_collect_config(req,{})
    lu.assertEquals(rsp.CompletionCode, 0xcc)
end

function TEST_ipmi:test_set_log_auto_collect_config()
    local req = {
        ManufacturerId = 0x0107DB,
    }
    rsp = ipmi_service.set_log_auto_collect_config(req,{})
    lu.assertEquals(rsp.CompletionCode, 0xcc)
end

function TEST_ipmi:test_set_pd_log_collect()
    local req = {
        ManufacturerId = 0x0107DB,
    }
    rsp = ipmi_service.set_pd_log_collect(req,{})
    lu.assertEquals(rsp.CompletionCode, 0xcc)
end

function TEST_ipmi:test_get_manufacturer()
    local req = {
        ManufacturerId = 0x0107DB,
    }
    rsp = ipmi_service.get_manufacturer(req,{})
    lu.assertEquals(rsp.CompletionCode, 0xcc)
end

function TEST_ipmi:test_set_raid_fault_status()
    local req = {
        ManufacturerId = 0x0007DB,
        DeviceStatus1 = 0,
        Bus = 1,
        Device = 2,
        Function = 3
    }
    c_controller.collection = {
        ['find'] = function(...) return {
            pcb_version = "N/A"
        } end ,
        ['fold'] = function(...) return nil end
    }
    controller_collection.new().get_all_controllers = function (...)
        return  {}
    end
    rsp = ipmi_service.set_raid_fault_status(req,{})
    lu.assertEquals(rsp.CompletionCode, 0xcc)
end

function TEST_ipmi:test_get_controller_info()
    local req = {
        ManufacturerId = 0x0007DB,
        ControllerId = 0,
        ReadingOffset = 0,
        ReadingLength = 0xF0
    }
    c_controller.collection = {
        ['find'] = function(...) return {
            pcb_version = "N/A"
        } end ,
        ['fold'] = function(...) return nil end
    }
    controller_collection.new().get_by_controller_id = function (...)
        return  {
            OOBSupport = 0,
            MemorySizeMiB = 0,
            CachePinnedState = 0,
            MaintainPDFailHistrory = 0,
            CopyBackState = 0,
            SmarterCopyBackState = 0,
            JBODState = 0,
            ControllerName = "test",
            SASAddr = 'abcd'
        }
    end
    local rsp = ipmi_service.get_controller_info(req,{})
    lu.assertEquals(rsp.CompletionCode, 0x00)
end
