-- 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 t_skynet = require 'skynet'

TestIndVrdService = {}

function TestIndVrdService:setUp()
    self.mock_signal = {
        new = function()
            return {
                on = function() end
            }
        end
    }

    self.mock_upgrade_subject = {
        get_instance = function()
            return {
                register_upgrade_observer = function() end,
                register_active_observer = function() end
            }
        end
    }

    self.mock_vrd_upgrade = {
        new = function(bus, db, collection)
            return {
                test = "upgrade_obj"
            }
        end
    }

    self.mock_chip_obj = {
        new = function(params, position, signal)
            return {
                register_firmware_info = function() end,
                test_params = params,
                test_position = position
            }
        end
    }

    package.loaded['mc.signal'] = self.mock_signal
    package.loaded['upgrade_subject.upgrade_subject'] = self.mock_upgrade_subject
    package.loaded['independent_vrd.upgrade.vrd_upgrade'] = self.mock_vrd_upgrade
    package.loaded['independent_vrd.chip.mp2882_smc'] = self.mock_chip_obj
    package.loaded['independent_vrd.chip.mp2882_pmbus'] = self.mock_chip_obj
    package.loaded['independent_vrd.chip.test_chip'] = self.mock_chip_obj

    package.loaded['common'] = {
        remove_ele_by_position = function(collection, position)
        end,
        skynet = {
            fork = function (cb, ...)
                if ok then
                    t_skynet.fork(cb, ...)
                end
            end,
            fork_once = function (cb, ...)
                if ok then
                    t_skynet.fork_once(cb, ...)
                end
            end,
            sleep = function (timeout)
                if ok then
                    t_skynet.sleep(timeout)
                end
            end
        }
    }

    package.loaded['independent_vrd.ind_vrd_service'] = nil

    -- 加载服务
    local ind_vrd_service = require 'independent_vrd.ind_vrd_service'
    self.service = ind_vrd_service.new()
end

function TestIndVrdService:test_on_add_object_vrd_firmware_valid_chip()
    local mock_object = {
        ChipType = "MP2882",
        BoardType = "TestBoard",
        RefChip = "test_ref",
        UID = "test-uid",
        Protocol = "SMC",
        SoftwareId = "test-sw-id"
    }

    local register_called = false
    local inserted_obj = nil

    self.mock_chip_obj.new = function(params, position, signal)
        return {
            register_firmware_info = function(bus)
                register_called = true
            end,
            test_params = params,
            test_position = position
        }
    end

    local original_insert = table.insert
    table.insert = function(t, obj)
        inserted_obj = obj
        original_insert(t, obj)
    end

    self.service:on_add_object("VRDFirmware", mock_object, 1)

    table.insert = original_insert
    table.insert = original_insert

    lu.assertTrue(register_called)
    lu.assertNotNil(inserted_obj)
    lu.assertEquals(#self.service.smc_ind_vrd_collection, 1)
end

function TestIndVrdService:test_on_add_object_invalid_chip_type()
    local mock_object = {
        ChipType = "INVALID_CHIP",
        BoardType = "TestBoard",
        RefChip = "test_ref",
        UID = "test-uid",
        Protocol = "SMC",
        SoftwareId = "test-sw-id"
    }

    package.loaded['independent_vrd.chip.invalid_chip'] = nil

    self.service:on_add_object("VRDFirmware", mock_object, 1)

    lu.assertEquals(#self.service.smc_ind_vrd_collection, 0)
end

function TestIndVrdService:test_on_add_object_non_vrd_firmware()
    -- 测试class_name不是VRDFirmware的情况
    local mock_object = {
        ChipType = "MP2882",
        BoardType = "TestBoard",
        Protocol = "SMC"
    }

    self.service:on_add_object("OTHER_CLASS", mock_object, 1)

    lu.assertEquals(#self.service.smc_ind_vrd_collection, 0)
end

function TestIndVrdService:test_on_delete_object_vrd_firmware()
    local remove_called = false
    local removed_position = nil

    package.loaded['common'].remove_ele_by_position = function(collection, position)
        remove_called = true
        removed_position = position
    end

    local mock_object = {
        ChipType = "MP2882",
        BoardType = "TestBoard"
    }

    self.service:on_delete_object("VRDFirmware", mock_object, 5)

    lu.assertTrue(remove_called)
    lu.assertEquals(removed_position, 5)
end

function TestIndVrdService:test_on_delete_object_non_vrd_firmware()
    local remove_called = false

    package.loaded['common'].remove_ele_by_position = function(collection, position)
        remove_called = true
    end

    local mock_object = {
        ChipType = "MP2882",
        BoardType = "TestBoard"
    }

    self.service:on_delete_object("OTHER_CLASS", mock_object, 5)

    lu.assertFalse(remove_called)
end

function TestIndVrdService:test_on_add_object_pmbus_new_manager()
    -- Mock ind_vrd_manager
    local manager_created = false
    local chip_added = false
    local firmware_registered = false

    local mock_manager = {
        add_chip = function(self, vrd_id, chip_obj)
            chip_added = true
            return true
        end,
        register_firmware_info = function(self, bus)
            firmware_registered = true
        end,
        set_objects_complete = function(self, complete) end
    }

    package.loaded['independent_vrd.ind_vrd_manager'] = {
        new = function(uid, board_type, software_id)
            manager_created = true
            return mock_manager
        end
    }

    package.loaded['independent_vrd.ind_vrd_service'] = nil
    local ind_vrd_service = require 'independent_vrd.ind_vrd_service'
    self.service = ind_vrd_service.new()
    self.service.bus = {} -- Mock bus object

    local mock_object = {
        ChipType = "MP2882",
        BoardType = "TestBoard",
        RefChip = "test_ref",
        UID = "pmbus-uid-001",
        VrdId = 1,
        Protocol = "PMBus",
        SoftwareId = "test-sw-id"
    }

    self.service:on_add_object("VRDFirmware", mock_object, 1)
    self.service:on_add_object_complete(1)

    lu.assertTrue(manager_created)
    lu.assertTrue(chip_added)
    lu.assertTrue(firmware_registered)
end

function TestIndVrdService:test_on_add_object_pmbus_existing_manager()
    -- Mock ind_vrd_manager
    local chip_added = false
    local firmware_registered = false

    local mock_manager = {
        UID = "pmbus-uid-001",
        add_chip = function(self, vrd_id, chip_obj)
            chip_added = true
            return true
        end,
        register_firmware_info = function(self, bus)
            firmware_registered = true
        end,
        set_objects_complete = function(self, complete) end
    }

    package.loaded['independent_vrd.ind_vrd_manager'] = {
        new = function(uid, board_type, software_id)
            return mock_manager
        end
    }

    package.loaded['independent_vrd.ind_vrd_service'] = nil
    local ind_vrd_service = require 'independent_vrd.ind_vrd_service'
    self.service = ind_vrd_service.new()
    self.service.bus = {} -- Mock bus object

    -- Pre-populate manager with position as key
    self.service.pmbus_vrd_manager_collection[1] = mock_manager

    local mock_object = {
        ChipType = "MP2882",
        BoardType = "TestBoard",
        RefChip = "test_ref",
        UID = "pmbus-uid-001",
        VrdId = 2,
        Protocol = "PMBus",
        SoftwareId = "test-sw-id"
    }

    self.service:on_add_object("VRDFirmware", mock_object, 1)
    self.service:on_add_object_complete(1)

    lu.assertTrue(chip_added)
    lu.assertTrue(firmware_registered)
end

function TestIndVrdService:test_on_delete_object_pmbus_chip()
    -- Mock ind_vrd_manager
    local mock_manager = {
        UID = "pmbus-uid-001",
        vrd_chips = {
            [1] = { test = "chip1" },
            [2] = { test = "chip2" }
        }
    }

    package.loaded['independent_vrd.ind_vrd_manager'] = {
        new = function(uid, board_type, software_id)
            return mock_manager
        end
    }

    package.loaded['independent_vrd.ind_vrd_service'] = nil
    local ind_vrd_service = require 'independent_vrd.ind_vrd_service'
    self.service = ind_vrd_service.new()

    self.service.pmbus_vrd_manager_collection[1] = mock_manager

    local mock_object = {
        ChipType = "MP2882",
        BoardType = "TestBoard",
        UID = "pmbus-uid-001",
        VrdId = 1,
        Protocol = "PMBus"
    }

    self.service:on_delete_object("VRDFirmware", mock_object, 1)

    lu.assertNil(mock_manager.vrd_chips[1])
    lu.assertNotNil(mock_manager.vrd_chips[2])
    lu.assertNotNil(self.service.pmbus_vrd_manager_collection[1])
end

function TestIndVrdService:test_on_delete_object_pmbus_last_chip()
    -- Mock ind_vrd_manager
    local mock_manager = {
        UID = "pmbus-uid-001",
        vrd_chips = {
            [1] = { test = "chip1" }
        }
    }

    package.loaded['independent_vrd.ind_vrd_manager'] = {
        new = function(uid, board_type, software_id)
            return mock_manager
        end
    }

    package.loaded['independent_vrd.ind_vrd_service'] = nil
    local ind_vrd_service = require 'independent_vrd.ind_vrd_service'
    self.service = ind_vrd_service.new()

    self.service.pmbus_vrd_manager_collection[1] = mock_manager

    local mock_object = {
        ChipType = "MP2882",
        BoardType = "TestBoard",
        UID = "pmbus-uid-001",
        VrdId = 1,
        Protocol = "PMBus"
    }

    self.service:on_delete_object("VRDFirmware", mock_object, 1)

    lu.assertNil(self.service.pmbus_vrd_manager_collection[1])
end

function TestIndVrdService:test_parse_firmware_type_smc()
    -- Mock ini parser
    package.loaded['mc.v2_persistence'] = {
        load_file = function(path)
            return {
                Firmware1 = {
                    Uid = 'test-smc-uid'
                }
            }
        end
    }

    package.loaded['independent_vrd.ind_vrd_service'] = nil
    local ind_vrd_service = require 'independent_vrd.ind_vrd_service'
    self.service = ind_vrd_service.new()

    -- Add SMC VRD to collection
    table.insert(self.service.smc_ind_vrd_collection, {
        mcu = {
            UID = 'test-smc-uid'
        }
    })

    local result = self.service:parse_firmware_type('/test/cfg.ini')

    lu.assertEquals('Independent_Vrd_Smc', result)
end

function TestIndVrdService:test_parse_firmware_type_pmbus()
    -- Mock ini parser
    package.loaded['mc.v2_persistence'] = {
        load_file = function(path)
            return {
                Firmware1 = {
                    Uid = 'test-pmbus-uid'
                }
            }
        end
    }

    package.loaded['independent_vrd.ind_vrd_manager'] = {
        new = function(uid, board_type, software_id)
            return {
                UID = uid
            }
        end
    }

    package.loaded['independent_vrd.ind_vrd_service'] = nil
    local ind_vrd_service = require 'independent_vrd.ind_vrd_service'
    self.service = ind_vrd_service.new()

    -- Add PMBus manager to collection with position as key
    self.service.pmbus_vrd_manager_collection[1] = {
        UID = 'test-pmbus-uid'
    }

    local result = self.service:parse_firmware_type('/test/cfg.ini')

    lu.assertEquals('Independent_Vrd_PMBus', result)
end

function TestIndVrdService:test_init()
    -- Mock smc_vrd_upgrade
    package.loaded['independent_vrd.upgrade.smc_vrd_upgrade'] = {
        new = function(bus, db, collection)
            return { test = "smc_upgrade" }
        end
    }

    -- Mock pmbus_vrd_upgrade
    package.loaded['independent_vrd.upgrade.pmbus_vrd_upgrade'] = {
        new = function(bus, db, collection)
            return { test = "pmbus_upgrade" }
        end
    }

    local firmware_parser_called = false
    local active_parser_called = false
    local upgrade_observer_count = 0
    local active_observer_count = 0

    self.mock_upgrade_subject.get_instance = function()
        return {
            register_firmware_parser = function(self, fw_type, parser_fn)
                firmware_parser_called = true
            end,
            register_active_firmware_parser = function(self, fw_type, parser_fn)
                active_parser_called = true
            end,
            register_upgrade_observer = function(self, observer, fw_type)
                upgrade_observer_count = upgrade_observer_count + 1
            end,
            register_active_observer = function(self, observer, fw_type)
                active_observer_count = active_observer_count + 1
            end
        }
    end

    package.loaded['upgrade_subject.upgrade_subject'] = self.mock_upgrade_subject
    package.loaded['independent_vrd.ind_vrd_service'] = nil

    local ind_vrd_service = require 'independent_vrd.ind_vrd_service'
    self.service = ind_vrd_service.new()

    local mock_bus = {}
    local mock_db = {}

    self.service:init(mock_bus, mock_db)

    lu.assertTrue(firmware_parser_called)
    lu.assertTrue(active_parser_called)
    lu.assertEquals(2, upgrade_observer_count)
    lu.assertEquals(2, active_observer_count)
    lu.assertNotNil(self.service.smc_vrd_upgrade_obj)
    lu.assertNotNil(self.service.pmbus_vrd_upgrade_obj)
end

function TestIndVrdService:test_dump_nil()
    local ind_vrd_service = require 'independent_vrd.ind_vrd_service'
    self.service = ind_vrd_service.new()
    self.service.smc_ind_vrd_collection = {}
    local ok, err = pcall(self.service.on_dump_cb, self.service, nil, nil)
    lu.assertEquals(ok, true)
    lu.assertEquals(err, nil)
end

function TestIndVrdService:test_dump()
    local ind_vrd_service = require 'independent_vrd.ind_vrd_service'
    self.service = ind_vrd_service.new()
    self.service.smc_ind_vrd_collection = {
        {get_vrd_dump_info = function()
            return
        end,
        get_vrd_log = function()
            return {1}
        end,
        get_id = function()
            return 1
        end,
        mcu = {Id = 1}}
    }
    local ok, err = pcall(self.service.on_dump_cb, self.service, nil, '/data')
    lu.assertEquals(ok, true)
    lu.assertEquals(err, nil)
end

function TestIndVrdService:tearDown()
    package.loaded['mc.signal'] = nil
    package.loaded['upgrade_subject.upgrade_subject'] = nil
    package.loaded['independent_vrd.upgrade.vrd_upgrade'] = nil
    package.loaded['independent_vrd.upgrade.smc_vrd_upgrade'] = nil
    package.loaded['independent_vrd.upgrade.pmbus_vrd_upgrade'] = nil
    package.loaded['independent_vrd.chip.mp2882_smc'] = nil
    package.loaded['independent_vrd.chip.test_chip'] = nil
    package.loaded['independent_vrd.ind_vrd_manager'] = nil
    package.loaded['mc.v2_persistence'] = nil
    package.loaded['common'] = nil
    package.loaded['independent_vrd.ind_vrd_service'] = nil
end
