-- 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 c_controller = require 'controller.controller_object'
local common_def = require 'common_def'
local ctx = require 'mc.context'
local method_misc = require 'method_misc'
local sml = require 'sml'

TEST_Controller_Slow_Drive = {}

-- Setup test environment
function TEST_Controller_Slow_Drive:setUp()
    -- Save original functions
    self.original_ctx_get_context = ctx.get_context_or_default
    self.original_test_controller_vendor = method_misc.test_controller_vendor
    self.original_ctrl_option = common_def.CTRL_OPTION
    self.original_sml_ctrl_operation = sml and sml.ctrl_operation
    
    -- Replace with mock functions
    ctx.get_context_or_default = function() return "mock_context" end
    method_misc.test_controller_vendor = function(self, type_id, vendor) 
        return vendor == common_def.VENDER_HUAWEI 
    end
    
    -- Mock CTRL_OPTION
    common_def.CTRL_OPTION = {
        drive_detection = {
            pack = function()
            end
        }
    }
    
    -- Track sml.ctrl_operation calls
    self.ctrl_operate_calls = {}
    
    -- Mock sml.ctrl_operation
    if sml then
        sml.ctrl_operation = function(ctrl_id, op_code, params)
            table.insert(self.ctrl_operate_calls, {ctrl_id = ctrl_id, op_code = op_code, params = params})
            return common_def.RET_OK
        end
    end

    -- Create mock signal object
    self.mock_signal = {
        on = function(self, callback)
            -- Store callback for testing if needed
            self._callback = callback
        end
    }
end

-- Teardown test environment
function TEST_Controller_Slow_Drive:tearDown()
    -- Restore original functions
    ctx.get_context_or_default = self.original_ctx_get_context
    method_misc.test_controller_vendor = self.original_test_controller_vendor
    common_def.CTRL_OPTION = self.original_ctrl_option
    
    -- Restore sml.ctrl_operation
    if sml and self.original_sml_ctrl_operation then
        sml.ctrl_operation = self.original_sml_ctrl_operation
    end
    
    -- Clear mock data
    self.ctrl_operate_calls = {}
end

-- Test non-Huawei controller
function TEST_Controller_Slow_Drive:test_non_huawei_controller()
    -- Mock method_misc to return false for non-Huawei
    method_misc.test_controller_vendor = function(self, type_id, vendor) 
        return false 
    end
    
    local controller_obj = {
        TypeId = 99,  -- Non-Huawei controller
        SlowDriveDetectionMode = 0
    }
    
    -- This should not throw an error
    c_controller.property_change_listen_func(controller_obj)
    lu.assertTrue(true)
end

-- Test non-SlowDriveDetectionMode property
function TEST_Controller_Slow_Drive:test_non_slow_drive_property()
    local controller_obj = {
        TypeId = common_def.HI1880_SP186_M_16i,  -- Huawei controller
        SlowDriveDetectionMode = 0,
        on_property_changed = self.mock_signal,
        get_mdb_object = function(self, path)
            return {
                property_before_change = {
                    on = function(self, callback)
                        -- Test with non-SlowDriveDetectionMode property
                        local result = callback('SomeOtherProperty', 123, 'sender')
                        lu.assertEquals(result, true)
                    end
                }
            }
        end
    }
    
    c_controller.property_change_listen_func(controller_obj)
end

-- Test SlowDriveDetectionMode with INVALID_U8 (os off)
function TEST_Controller_Slow_Drive:test_slow_drive_invalid_u8()
    local controller_obj = {
        TypeId = common_def.HI1880_SP186_M_16i,  -- Huawei controller
        SlowDriveDetectionMode = 0,
        on_property_changed = self.mock_signal,
        get_mdb_object = function(self, path)
            return {
                property_before_change = {
                    on = function(self, callback)
                        -- Test with INVALID_U8
                        local result = callback('SlowDriveDetectionMode', common_def.INVALID_U8, 'sender')
                        lu.assertEquals(result, true)
                    end
                }
            }
        end
    }
    
    c_controller.property_change_listen_func(controller_obj)
end

-- Test SlowDriveDetectionMode update from INVALID_U8
function TEST_Controller_Slow_Drive:test_slow_drive_update_from_invalid()
    local controller_obj = {
        TypeId = common_def.HI1880_SP186_M_16i,  -- Huawei controller
        SlowDriveDetectionMode = common_def.INVALID_U8,
        on_property_changed = self.mock_signal,
        get_mdb_object = function(self, path)
            return {
                property_before_change = {
                    on = function(self, callback)
                        -- Test update from INVALID_U8
                        local result = callback('SlowDriveDetectionMode', 1, 'sender')
                        lu.assertEquals(result, true)
                    end
                }
            }
        end
    }
    
    c_controller.property_change_listen_func(controller_obj)
end

-- Test invalid SlowDriveDetectionMode value
function TEST_Controller_Slow_Drive:test_invalid_slow_drive_value()
    local controller_obj = {
        TypeId = common_def.HI1880_SP186_M_16i,  -- Huawei controller
        SlowDriveDetectionMode = 0,
        on_property_changed = self.mock_signal,
        get_mdb_object = function(self, path)
            return {
                property_before_change = {
                    on = function(self, callback)
                        -- Test with invalid value
                        local result = callback('SlowDriveDetectionMode', 999, 'sender')
                        lu.assertEquals(result, false)
                    end
                }
            }
        end
    }
    
    c_controller.property_change_listen_func(controller_obj)
end

-- Test valid SlowDriveDetectionMode transition
function TEST_Controller_Slow_Drive:test_valid_slow_drive_transition()
    local SLOW_DISK_MODES = common_def.SLOW_DISK_DETECTION_MODE
    
    local controller_obj = {
        TypeId = common_def.HI1880_SP186_M_16i,  -- Huawei controller
        SlowDriveDetectionMode = SLOW_DISK_MODES.DISABLE,
        Id = 1,
        OOBSupport = 1,
        is_working = false,
        on_property_changed = self.mock_signal,
        get_mdb_object = function(self, path)
            return {
                property_before_change = {
                    on = function(self, callback)
                        -- Test valid transition
                        local result = callback('SlowDriveDetectionMode', SLOW_DISK_MODES.ENABLE_RAW_DRIVE, 'sender')
                        lu.assertEquals(result, true)
                    end
                }
            }
        end
    }
    
    c_controller.property_change_listen_func(controller_obj)
    
    -- Should have 1 call: enable raw drives
    lu.assertEquals(#self.ctrl_operate_calls, 1)
end

-- Test error handling in ctrl_operate
function TEST_Controller_Slow_Drive:test_ctrl_operate_error()
    local SLOW_DISK_MODES = common_def.SLOW_DISK_DETECTION_MODE
    
    -- Make sml.ctrl_operation throw an error
    local original_sml_ctrl_operation = sml.ctrl_operation
    sml.ctrl_operation = function() error("mock error") end
    
    local controller_obj = {
        TypeId = common_def.HI1880_SP186_M_16i,  -- Huawei controller
        SlowDriveDetectionMode = SLOW_DISK_MODES.DISABLE,
        Id = 1,
        OOBSupport = 1,
        is_working = false,
        on_property_changed = self.mock_signal,
        get_mdb_object = function(self, path)
            return {
                property_before_change = {
                    on = function(self, callback)
                        -- This should handle the error gracefully
                        local success, _ = pcall(callback, 'SlowDriveDetectionMode', SLOW_DISK_MODES.ENABLE_RAW_DRIVE, 'sender')
                        lu.assertFalse(success)
                    end
                }
            }
        end
    }
    
    c_controller.property_change_listen_func(controller_obj)
    
    -- Restore sml.ctrl_operation
    sml.ctrl_operation = original_sml_ctrl_operation
end

-- Test all valid mode transitions
function TEST_Controller_Slow_Drive:test_all_valid_mode_transitions()
    -- Clear previous calls
    self.ctrl_operate_calls = {}
    local SLOW_DISK_MODES = common_def.SLOW_DISK_DETECTION_MODE
    
    -- Test all valid mode transitions
    local transitions = {
        {from = SLOW_DISK_MODES.DISABLE, to = SLOW_DISK_MODES.ENABLE_RAW_DRIVE},
        {from = SLOW_DISK_MODES.DISABLE, to = SLOW_DISK_MODES.ENABLE_MEMBER_DRIVE},
        {from = SLOW_DISK_MODES.DISABLE, to = SLOW_DISK_MODES.ENABLE_RAW_AND_MEMBER_DRIVE},
        {from = SLOW_DISK_MODES.ENABLE_RAW_DRIVE, to = SLOW_DISK_MODES.DISABLE},
        {from = SLOW_DISK_MODES.ENABLE_RAW_DRIVE, to = SLOW_DISK_MODES.ENABLE_MEMBER_DRIVE},
        {from = SLOW_DISK_MODES.ENABLE_RAW_DRIVE, to = SLOW_DISK_MODES.ENABLE_RAW_AND_MEMBER_DRIVE},
        {from = SLOW_DISK_MODES.ENABLE_MEMBER_DRIVE, to = SLOW_DISK_MODES.DISABLE},
        {from = SLOW_DISK_MODES.ENABLE_MEMBER_DRIVE, to = SLOW_DISK_MODES.ENABLE_RAW_DRIVE},
        {from = SLOW_DISK_MODES.ENABLE_MEMBER_DRIVE, to = SLOW_DISK_MODES.ENABLE_RAW_AND_MEMBER_DRIVE},
        {from = SLOW_DISK_MODES.ENABLE_RAW_AND_MEMBER_DRIVE, to = SLOW_DISK_MODES.DISABLE},
        {from = SLOW_DISK_MODES.ENABLE_RAW_AND_MEMBER_DRIVE, to = SLOW_DISK_MODES.ENABLE_RAW_DRIVE},
        {from = SLOW_DISK_MODES.ENABLE_RAW_AND_MEMBER_DRIVE, to = SLOW_DISK_MODES.ENABLE_MEMBER_DRIVE}
    }
    for _, transition in ipairs(transitions) do
        local controller_obj = {
            TypeId = common_def.HI1880_SP186_M_16i,  -- Huawei controller
            SlowDriveDetectionMode = transition.from,
            Id = 1,
            OOBSupport = 1,
            is_working = false,
            on_property_changed = self.mock_signal,
            get_mdb_object = function(self, path)
                return {
                    property_before_change = {
                        on = function(self, callback)
                            -- Test valid transition
                            local result = callback('SlowDriveDetectionMode', transition.to, 'sender')
                            lu.assertEquals(result, true)
                        end
                    }
                }
            end
        }
        
        c_controller.property_change_listen_func(controller_obj)
    end
end

-- Test additional valid mode transition scenarios
function TEST_Controller_Slow_Drive:test_mode_transition_edge_cases()
    local SLOW_DISK_MODES = common_def.SLOW_DISK_DETECTION_MODE
    
    -- Test same mode transition (should no-op)
    local controller_obj = {
        TypeId = common_def.HI1880_SP186_M_16i,  -- Huawei controller
        SlowDriveDetectionMode = SLOW_DISK_MODES.ENABLE_RAW_DRIVE,
        Id = 1,
        OOBSupport = 1,
        is_working = false,
        on_property_changed = self.mock_signal,
        get_mdb_object = function(self, path)
            return {
                property_before_change = {
                    on = function(self, callback)
                        -- Test same mode transition
                        local result = callback('SlowDriveDetectionMode', SLOW_DISK_MODES.ENABLE_RAW_DRIVE, 'sender')
                        lu.assertEquals(result, true)
                    end
                }
            }
        end
    }
    
    c_controller.property_change_listen_func(controller_obj)
    
    -- Should have no calls since mode is the same
    lu.assertEquals(#self.ctrl_operate_calls, 0)
end

function TEST_Controller_Slow_Drive:test_property_change()
    local method_misc = require 'method_misc'
    local old_update = method_misc.update_firmware_info
    local check_res = ""
    method_misc.update_firmware_info = function(self, value)
        check_res = value.FirmwareVersion
    end

    local controller_obj = {
        TypeId = common_def.HI1880_SP186_M_16i,  -- Huawei controller
        FirmwareVersion = '2.0.0.6',
        on_property_changed = {
            on = function(self, callback)
                callback('FirmwareVersion', '2.0.0.6')
            end
        },
        get_mdb_object = function(self, path)
            return {
                property_before_change = {
                    on = function(self, callback)
                        return true
                    end
                }
            }
        end,
        firmware_register_status = true
    }
    c_controller.property_change_listen_func(controller_obj)
    lu.assertEquals(check_res, '2.0.0.6')
    method_misc.update_firmware_info = old_update
end

return TEST_Controller_Slow_Drive
