-- Copyright (c) 2026 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.
--         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 common_interface = require 'common_interface'
-- 引入被依赖的模块用于Mock
local client = require 'general_hardware.client'
local log = require 'mc.logging'

-- 重置单例内部状态（用于测试隔离）
local function reset_state()
    common_interface.callbacks = {}
    common_interface.flags = {}
    common_interface.ipv4_addr = {}
    common_interface.ipv6_addr = {}
    common_interface.mac = {}
    common_interface.sn = {}
    common_interface.registered_bmc_properties_listening = false
    common_interface.bus = nil
end

local original_client = {}
local original_log = {}
local original_skynet = nil
local function mock_setup()
    -- 备份client所有被调用的方法
    original_client.OnIpv4PropertiesChanged = client.OnIpv4PropertiesChanged
    original_client.OnIpv6PropertiesChanged = client.OnIpv6PropertiesChanged
    original_client.OnEthernetInterfacesPropertiesChanged = client.OnEthernetInterfacesPropertiesChanged
    original_client.OnIpv4InterfacesAdded = client.OnIpv4InterfacesAdded
    original_client.OnIpv6InterfacesAdded = client.OnIpv6InterfacesAdded
    original_client.OnEthernetInterfacesInterfacesAdded = client.OnEthernetInterfacesInterfacesAdded
    original_client.ForeachIpv4Objects = client.ForeachIpv4Objects
    original_client.ForeachIpv6Objects = client.ForeachIpv6Objects
    original_client.ForeachEthernetInterfacesObjects = client.ForeachEthernetInterfacesObjects
    original_client.ForeachOverviewObjects = client.ForeachOverviewObjects
    original_client.GetFruDataProductObject = client.GetFruDataProductObject
    original_client.OnProductPropertiesChanged = client.OnProductPropertiesChanged
    -- 备份log方法（避免测试打印冗余日志）
    original_log.info = log.info
    original_log.notice = log.notice
    original_log.error = log.error
    original_log.error_easy = log.error_easy
    -- Mock log为空函数
    log.info = function() end
    log.notice = function() end
    log.error = function() end
    log.error_easy = function() end
    -- 备份并Mock skynet.sleep（避免测试真实睡眠）
    original_skynet = require 'skynet'
    package.loaded.skynet = { sleep = function() end }
end

-- 恢复原始Mock方法
local function mock_teardown()
    -- 恢复client
    for k, v in pairs(original_client) do
        client[k] = v
    end
    -- 恢复log
    for k, v in pairs(original_log) do
        log[k] = v
    end
    -- 恢复skynet
    package.loaded.skynet = original_skynet
    original_skynet = nil
    -- 恢复setup_eth_listener原始方法
    common_interface.setup_eth_listener = original_setup_eth_listener
end

-- 提前备份setup_eth_listener
local original_setup_eth_listener = common_interface.setup_eth_listener

TestCommonInterface = {}

-- 重写setUp：每次用例前重置状态+初始化Mock
function TestCommonInterface:setUp()
    reset_state()
    mock_setup()
end

-- 重写tearDown：每次用例后恢复Mock
function TestCommonInterface:tearDown()
    mock_teardown()
end

function TestCommonInterface:test_register_callback()
    local cb1 = function() end
    local cb2 = function() end

    common_interface:register_callback(cb1, common_interface, "ipv4", "ipv4_registered")
    lu.assertEquals(#common_interface.callbacks.ipv4, 1)
    lu.assertTrue(common_interface.flags.ipv4_registered)

    common_interface:register_callback(cb2, common_interface, "ipv4", "ipv4_registered")
    lu.assertEquals(#common_interface.callbacks.ipv4, 1)

    common_interface:register_callback(cb2, common_interface, "ipv4")
    lu.assertEquals(#common_interface.callbacks.ipv4, 2)
end

function TestCommonInterface:test_notify_callbacks()
    local call_log = {}
    local cb = function(self_obj, id, val)
        table.insert(call_log, {id = id, val = val})
    end

    common_interface:register_callback(cb, common_interface, "sn")
    common_interface:_notify_callbacks("sn", 1, "TEST123")
    lu.assertEquals(#call_log, 1)
    lu.assertEquals(call_log[1].id, 1)
    lu.assertEquals(call_log[1].val, "TEST123")

    common_interface:_notify_callbacks("nonexist")
    lu.assertEquals(#call_log, 1)
end

function TestCommonInterface:test_getters()
    common_interface.ipv4_addr[1] = "192.168.1.1"
    common_interface.ipv6_addr[2] = "2001:db8::1"
    common_interface.mac[1] = "aa:bb:cc:dd:ee:ff"
    common_interface.sn[1] = "SN123"

    lu.assertEquals(common_interface:get_ipv4_addr(1), "192.168.1.1")
    lu.assertEquals(common_interface:get_ipv6_addr(2), "2001:db8::1")
    lu.assertEquals(common_interface:get_mac(1), "aa:bb:cc:dd:ee:ff")
    lu.assertEquals(common_interface:get_sn(), "SN123")
    lu.assertEquals(common_interface:get_ipv4_addr(999), nil)

    common_interface.sn = {}
    lu.assertEquals(common_interface:get_sn(), nil)
end

function TestCommonInterface:test_sleep_ms()
    lu.assertEquals(math.ceil(0 / 10), 0)
    lu.assertEquals(math.ceil(5 / 10), 1)
    lu.assertEquals(math.ceil(6000 / 10), 600)
end

function TestCommonInterface:test_setup_eth_listener()
    local origin_foreach_ipv4 = client.ForeachIpv4Objects
    local origin_on_ipv4_prop = client.OnIpv4PropertiesChanged
    local origin_on_ipv4_add = client.OnIpv4InterfacesAdded
    local origin_foreach_mac = client.ForeachEthernetInterfacesObjects
    local origin_on_mac_prop = client.OnEthernetInterfacesPropertiesChanged
    local origin_on_mac_add = client.OnEthernetInterfacesInterfacesAdded
    local origin_foreach_ipv6 = client.ForeachIpv6Objects
    local origin_on_ipv6_prop = client.OnIpv6PropertiesChanged
    local origin_on_ipv6_add = client.OnIpv6InterfacesAdded

    common_interface.ipv4_addr = {}
    common_interface.mac = {}
    common_interface.ipv6_addr = {}

    client.OnIpv4PropertiesChanged = function(self, cb) end
    client.OnIpv4InterfacesAdded = function(self, cb) end
    client.OnEthernetInterfacesPropertiesChanged = function(self, cb) end
    client.OnEthernetInterfacesInterfacesAdded = function(self, cb) end
    client.OnIpv6PropertiesChanged = function(self, cb) end
    client.OnIpv6InterfacesAdded = function(self, cb) end

    -- ==================== 测试1：IPv4监听器注册+初始数据遍历/赋值 ====================
    local ipv4_foreach_called = false
    client.ForeachIpv4Objects = function(self, cb)
        ipv4_foreach_called = true
        -- 合法对象：带Managers/数字，原代码正常索引赋值
        cb({
            path = '/bmc/kepler/Managers/1/EthernetInterfaces/Ipv4',
            IpAddr = "10.0.0.1"
        })
        -- 非法对象：无有效索引，原代码忽略
        cb({ path = '/invalid/path', IpAddr = "10.0.0.2" })
    end
    -- 调用IPv4监听器初始化
    common_interface.setup_eth_listener(common_interface, 'ipv4')
    lu.assertTrue(ipv4_foreach_called)
    lu.assertEquals(common_interface.ipv4_addr[1], "10.0.0.1")
    lu.assertNil(common_interface.ipv4_addr[99]) -- 非法地址无索引，确保被忽略

    -- ==================== 测试2：MAC监听器注册+初始数据遍历/赋值 ====================
    local mac_foreach_called = false
    client.ForeachEthernetInterfacesObjects = function(self, cb)
        mac_foreach_called = true
        cb({
            path = '/bmc/kepler/Managers/1/EthernetInterfaces',
            Mac = "00:11:22:33:44:55"
        })
    end
    -- 调用MAC监听器初始化
    common_interface.setup_eth_listener(common_interface, 'mac')
    lu.assertTrue(mac_foreach_called)
    lu.assertEquals(common_interface.mac[1], "00:11:22:33:44:55")

    -- ==================== 测试3：IPv6监听器注册+初始数据遍历/赋值 ====================
    local ipv6_foreach_called = false
    client.ForeachIpv6Objects = function(self, cb)
        ipv6_foreach_called = true
        cb({
            path = '/bmc/kepler/Managers/1/EthernetInterfaces/Ipv6',
            IpAddr = "2001:db8::2"
        })
    end
    -- 调用IPv6监听器初始化（本次屏蔽了OnIpv6PropertiesChanged，无底层报错）
    common_interface.setup_eth_listener(common_interface, 'ipv6')
    lu.assertTrue(ipv6_foreach_called)
    lu.assertEquals(common_interface.ipv6_addr[1], "2001:db8::2")

    client.ForeachIpv4Objects = origin_foreach_ipv4
    client.OnIpv4PropertiesChanged = origin_on_ipv4_prop
    client.OnIpv4InterfacesAdded = origin_on_ipv4_add
    client.ForeachEthernetInterfacesObjects = origin_foreach_mac
    client.OnEthernetInterfacesPropertiesChanged = origin_on_mac_prop
    client.OnEthernetInterfacesInterfacesAdded = origin_on_mac_add
    client.ForeachIpv6Objects = origin_foreach_ipv6
    client.OnIpv6PropertiesChanged = origin_on_ipv6_prop
    client.OnIpv6InterfacesAdded = origin_on_ipv6_add
end

function TestCommonInterface:test_listen_and_update_sn()
    local DEFAULT_SYSTEM_ID = 1
    local MAX_RETRY_TIMES = 10
    local FRU0ID = 0

    local origin_foreach_overview = client.ForeachOverviewObjects
    local origin_get_fru_product = client.GetFruDataProductObject
    local origin_on_product_changed = client.OnProductPropertiesChanged

    -- ==================== 场景1：首次获取FRU ID+SN成功 ====================
    common_interface.sn = {}
    local overview_called = 0
    local on_product_called = false
    local fru_params_scene1 = nil
    -- 场景1独立Mock：ForeachOverviewObjects
    client.ForeachOverviewObjects = function(self, cb)
        overview_called = overview_called + 1
        cb({ FruId = FRU0ID, extra_params = { ObjectName = 100 } })
    end
    -- 首次SN直接返回字符串（适配原代码漏写:value()的逻辑）
    client.GetFruDataProductObject = function(self, params)
        fru_params_scene1 = params
        return { ProductSerialNumber = "SN_SUCCESS" }
    end
    client.OnProductPropertiesChanged = function(self, cb, params)
        on_product_called = true
        cb({ ProductSerialNumber = { value = function() return "SN_UPDATE" end } })
        cb({ ProductSerialNumber = "" })
    end
    -- 执行方法
    common_interface:listen_and_update_sn()
    -- 场景1断言
    lu.assertEquals(overview_called, 1)
    lu.assertEquals(fru_params_scene1.Id, 100)
    lu.assertEquals(fru_params_scene1.SystemId, DEFAULT_SYSTEM_ID)
    lu.assertEquals(common_interface.sn[DEFAULT_SYSTEM_ID], "SN_UPDATE")
    lu.assertTrue(on_product_called)

    -- ==================== 场景2：FRU ID获取失败，重试后成功（第3次） ====================
    common_interface.sn = {}
    overview_called = 0
    local retry_times = 0
    local fru_params_scene2 = nil
    -- 场景2独立Mock：ForeachOverviewObjects
    client.ForeachOverviewObjects = function(self, cb)
        overview_called = overview_called + 1
        retry_times = retry_times + 1
        if retry_times <= 2 then
            cb({ FruId = 1 }) -- 前2次失败
        else
            cb({ FruId = FRU0ID, extra_params = { ObjectName = 200 } }) -- 第3次成功
        end
    end
    client.GetFruDataProductObject = function(self, params)
        fru_params_scene2 = params
        return { ProductSerialNumber = "SN_RETRY_SUCCESS" }
    end
    client.OnProductPropertiesChanged = function(self, cb, params)
    end
    -- 执行方法
    common_interface:listen_and_update_sn()
    -- 场景2断言（现在能匹配字符串值）
    lu.assertEquals(overview_called, 3)
    lu.assertEquals(fru_params_scene2.Id, 200)
    lu.assertEquals(fru_params_scene2.SystemId, DEFAULT_SYSTEM_ID)
    lu.assertEquals(common_interface.sn[DEFAULT_SYSTEM_ID], "SN_RETRY_SUCCESS")

    -- ==================== 场景3：达到最大重试次数，FRU ID始终获取失败 ====================
    common_interface.sn = {}
    overview_called = 0
    -- 场景3独立Mock
    client.ForeachOverviewObjects = function(self, cb)
        overview_called = overview_called + 1
        cb({ FruId = 1 }) -- 持续失败
    end
    client.GetFruDataProductObject = function(self, params) return {} end
    client.OnProductPropertiesChanged = function(self, cb, params) end
    -- 执行方法
    common_interface:listen_and_update_sn()
    -- 场景3断言
    lu.assertEquals(overview_called, MAX_RETRY_TIMES)
    lu.assertNil(common_interface.sn[DEFAULT_SYSTEM_ID])

    -- ==================== 场景4：FRU ID成功，SN为空值，重试后仍失败 ====================
    common_interface.sn = {}
    overview_called = 0
    retry_times = 0
    -- 场景4独立Mock
    client.ForeachOverviewObjects = function(self, cb)
        overview_called = overview_called + 1
        cb({ FruId = FRU0ID, extra_params = { ObjectName = 300 } })
    end
    client.GetFruDataProductObject = function(self, params)
        retry_times = retry_times + 1
        return { ProductSerialNumber = "" } -- SN为空触发重试
    end
    client.OnProductPropertiesChanged = function(self, cb, params) end
    -- 执行方法
    common_interface:listen_and_update_sn()
    -- 场景4断言
    lu.assertEquals(retry_times, MAX_RETRY_TIMES)
    lu.assertNil(common_interface.sn[DEFAULT_SYSTEM_ID])

    -- ==================== 场景5：GetFruDataProductObject执行抛出异常 ====================
    common_interface.sn = {}
    overview_called = 0
    -- 场景5独立Mock
    client.ForeachOverviewObjects = function(self, cb)
        overview_called = 1
        cb({ FruId = FRU0ID, extra_params = { ObjectName = 400 } })
    end
    client.GetFruDataProductObject = function(self, params)
        error("get fru data product object failed")
    end
    client.OnProductPropertiesChanged = function(self, cb, params) end
    -- 执行方法
    common_interface:listen_and_update_sn()
    -- 场景5断言
    lu.assertEquals(overview_called, 1)
    lu.assertNil(common_interface.sn[DEFAULT_SYSTEM_ID])

    client.ForeachOverviewObjects = origin_foreach_overview
    client.GetFruDataProductObject = origin_get_fru_product
    client.OnProductPropertiesChanged = origin_on_product_changed
end

function TestCommonInterface:test_ctor()
    -- 单例重新初始化（传入bus参数）
    local new_if = require 'common_interface'.new("test_bus")
    lu.assertEquals(new_if.bus, "test_bus")
    lu.assertEquals(type(new_if.callbacks), "table")
    lu.assertEquals(type(new_if.flags), "table")
    lu.assertEquals(type(new_if.ipv4_addr), "table")
    lu.assertEquals(type(new_if.ipv6_addr), "table")
    lu.assertEquals(type(new_if.mac), "table")
    lu.assertEquals(type(new_if.sn), "table")
    lu.assertFalse(new_if.registered_bmc_properties_listening)
    -- 重置单例状态
    reset_state()
end