local lu = require 'luaunit'
local c_network_adapter_preload = require 'device.class.network_adapter_preload'
local c_network_adapter = require 'device.class.network_adapter'
local c_object_manage = require 'mc.orm.object_manage'
local comm_fun = require 'device.class.nic_mgmt.comm_fun'
local comm_defs = require 'device.class.nic_mgmt.comm_defs'
local skynet = require 'skynet'
local c_device_manager = require 'device.device_manager'
local card_mgmt = require 'device.class.nic_mgmt.card_mgmt'
local client = require 'network_adapter.client'
local mdb = require 'mc.mdb'
local log = require 'mc.logging'
local fructl = require 'infrastructure.fructl'

TEST_network_adapter_preload = {}

-- 辅助函数：创建模拟 bus（满足 init_obj 内部对 bus 的基本调用）
local function create_mock_bus()
    return {
        call = function()
            -- 返回最小的 GetObject 结果，让后续流程继续执行
            return { ['/test/path'] = { 'bmc.dev.PCIeDevice' } }
        end,
        match = function()
            -- 返回一个可调用的“取消订阅”占位
            return function() end
        end
    }
end

-- 辅助函数：创建模拟 mdb 对象（用于 get_device_name 中的 DeviceName）
local function create_mock_mdb_object()
    return {
        get_property = function(self, prop_name)
            if prop_name == 'DeviceName' then
                return 0, 'test_device_name'
            end
            return -1, nil
        end
    }
end

-- 辅助函数：简单日志捕获
local function setup_log_capture(log_calls)
    local orig_notice = log.notice
    local orig_error = log.error
    local orig_warning = log.warning

    log.notice = function(_, fmt, ...)
        table.insert(log_calls, string.format(fmt, ...))
    end
    log.error = function(_, fmt, ...)
        table.insert(log_calls, string.format(fmt, ...))
    end
    log.warning = function(_, fmt, ...)
        table.insert(log_calls, string.format(fmt, ...))
    end

    return function()
        log.notice = orig_notice
        log.error = orig_error
        log.warning = orig_warning
    end
end

-- 覆盖 c_network_adapter_preload:init 的分支

function TEST_network_adapter_preload:test_init_success()
    local log_calls = {}
    local restore_log = setup_log_capture(log_calls)

    -- 打桩 super.init，避免依赖真正 ORM 框架
    local old_super_init = c_network_adapter_preload.super.init
    c_network_adapter_preload.super.init = function() end

    local obj = {
        NodeId = 'node_1',
        PortCount = 4,
        Ref4PortConnector = { Presence = 0 },
    }

    c_network_adapter_preload.init(obj)

    -- Presence 应被置为 1
    lu.assertEquals(obj.Ref4PortConnector.Presence, 1)

    -- 日志至少包含一次“set Ref4PortConnector presence to 1 successfully”
    local found = false
    for _, msg in ipairs(log_calls) do
        if string.find(msg, 'set Ref4PortConnector presence to 1 successfully') then
            found = true
            break
        end
    end
    lu.assertTrue(found)

    -- 还原
    c_network_adapter_preload.super.init = old_super_init
    restore_log()
end

function TEST_network_adapter_preload:test_init_port_count_nil()
    local log_calls = {}
    local restore_log = setup_log_capture(log_calls)

    local old_super_init = c_network_adapter_preload.super.init
    c_network_adapter_preload.super.init = function() end

    -- PortCount 为空
    local obj = {
        NodeId = 'node_1',
    }

    c_network_adapter_preload.init(obj)

    -- 不应抛异常，且会打出 port count is nil 的错误日志
    local found = false
    for _, msg in ipairs(log_calls) do
        if string.find(msg, 'network adapter preload object init failed, port count is nil') then
            found = true
            break
        end
    end
    lu.assertTrue(found)

    c_network_adapter_preload.super.init = old_super_init
    restore_log()
end

function TEST_network_adapter_preload:test_init_ref_connector_missing()
    local log_calls = {}
    local restore_log = setup_log_capture(log_calls)

    local old_super_init = c_network_adapter_preload.super.init
    c_network_adapter_preload.super.init = function() end

    -- Ref4PortConnector 为空
    local obj = {
        NodeId = 'node_1',
        PortCount = 4,
    }

    c_network_adapter_preload.init(obj)

    local found = false
    for _, msg in ipairs(log_calls) do
        if string.find(msg, 'network adapter preload object init failed') and
           string.find(msg, 'is nil, node id') then
            found = true
            break
        end
    end
    lu.assertTrue(found)

    c_network_adapter_preload.super.init = old_super_init
    restore_log()
end

function TEST_network_adapter_preload:test_set_port_count_no_card_orm()
    local old_find = c_network_adapter.collection.find
    c_network_adapter.collection.find = function()
        return nil
    end

    local obj = { NodeId = 'node_1', PortCount = 4 }
    local ret = c_network_adapter_preload.set_port_count(obj, 8)
    lu.assertEquals(ret, false)
    lu.assertEquals(obj.PortCount, 4)

    c_network_adapter.collection.find = old_find
end

function TEST_network_adapter_preload:test_set_port_count_no_device_obj()
    local old_find = c_network_adapter.collection.find
    local old_get_instance = c_object_manage.get_instance
    local old_get_device_obj = comm_fun.get_device_obj_by_path_interface

    c_network_adapter.collection.find = function()
        return { device_path = '/test/path' }
    end
    c_object_manage.get_instance = function()
        return { bus = {} }
    end
    comm_fun.get_device_obj_by_path_interface = function()
        return nil
    end

    local obj = { NodeId = 'node_1', PortCount = 4 }
    local ret = c_network_adapter_preload.set_port_count(obj, 8)
    lu.assertEquals(ret, false)
    lu.assertEquals(obj.PortCount, 4)

    c_network_adapter.collection.find = old_find
    c_object_manage.get_instance = old_get_instance
    comm_fun.get_device_obj_by_path_interface = old_get_device_obj
end

function TEST_network_adapter_preload:test_set_port_count_call_method_failed()
    local old_find = c_network_adapter.collection.find
    local old_get_instance = c_object_manage.get_instance
    local old_get_device_obj = comm_fun.get_device_obj_by_path_interface

    c_network_adapter.collection.find = function()
        return { device_path = '/test/path' }
    end
    c_object_manage.get_instance = function()
        return { bus = {} }
    end
    comm_fun.get_device_obj_by_path_interface = function()
        return {
            call_method = function()
                return false
            end
        }
    end

    local obj = { NodeId = 'node_1', PortCount = 4 }
    local ret = c_network_adapter_preload.set_port_count(obj, 8)
    lu.assertEquals(ret, false)
    lu.assertEquals(obj.PortCount, 4)

    c_network_adapter.collection.find = old_find
    c_object_manage.get_instance = old_get_instance
    comm_fun.get_device_obj_by_path_interface = old_get_device_obj
end

function TEST_network_adapter_preload:test_set_port_count_success()
    local old_find = c_network_adapter.collection.find
    local old_get_instance = c_object_manage.get_instance
    local old_get_device_obj = comm_fun.get_device_obj_by_path_interface
    local old_sleep = skynet.sleep
    local old_fork_once = skynet.fork_once

    c_network_adapter.collection.find = function()
        return { device_path = '/test/path' }
    end
    c_object_manage.get_instance = function()
        return { bus = {} }
    end
    comm_fun.get_device_obj_by_path_interface = function()
        return {
            call_method = function()
                return true
            end
        }
    end
    skynet.sleep = function()
    end
    -- 使异步逻辑在测试中同步执行，便于验证 Presence 变化
    skynet.fork_once = function(fn)
        if fn then fn() end
    end

    -- 适配新逻辑：需要 old/new RefConnector 均存在
    local obj = {
        NodeId = 'node_1',
        PortCount = 4,
        Ref4PortConnector = { Presence = 1 },
        Ref8PortConnector = { Presence = 0 },
    }
    local ret = c_network_adapter_preload.set_port_count(obj, 8)
    lu.assertEquals(ret, true)
    lu.assertEquals(obj.PortCount, 8)
    -- fork_once 同步执行后，应完成 presence 切换
    lu.assertEquals(obj.Ref4PortConnector.Presence, 0)
    lu.assertEquals(obj.Ref8PortConnector.Presence, 1)

    c_network_adapter.collection.find = old_find
    c_object_manage.get_instance = old_get_instance
    comm_fun.get_device_obj_by_path_interface = old_get_device_obj
    skynet.sleep = old_sleep
    skynet.fork_once = old_fork_once
end

-- old_ref_connector_name 缺失分支
function TEST_network_adapter_preload:test_set_port_count_old_ref_connector_missing()
    local old_find = c_network_adapter.collection.find
    local old_get_instance = c_object_manage.get_instance
    local old_get_device_obj = comm_fun.get_device_obj_by_path_interface
    local old_error = log.error

    c_network_adapter.collection.find = function()
        return { device_path = '/test/path' }
    end
    c_object_manage.get_instance = function()
        return { bus = {} }
    end
    comm_fun.get_device_obj_by_path_interface = function()
        return {
            call_method = function()
                return true
            end
        }
    end
    -- 避免日志格式化占位符不匹配导致报错
    log.error = function() end

    local obj = {
        NodeId = 'node_1',
        PortCount = 4,
        -- 缺少 Ref4PortConnector
        Ref8PortConnector = { Presence = 0 },
    }

    local ret = c_network_adapter_preload.set_port_count(obj, 8)
    lu.assertEquals(ret, false)
    -- PortCount 不应被修改
    lu.assertEquals(obj.PortCount, 4)

    c_network_adapter.collection.find = old_find
    c_object_manage.get_instance = old_get_instance
    comm_fun.get_device_obj_by_path_interface = old_get_device_obj
    log.error = old_error
end

-- new_ref_connector_name 缺失分支
function TEST_network_adapter_preload:test_set_port_count_new_ref_connector_missing()
    local old_find = c_network_adapter.collection.find
    local old_get_instance = c_object_manage.get_instance
    local old_get_device_obj = comm_fun.get_device_obj_by_path_interface
    local old_error = log.error

    c_network_adapter.collection.find = function()
        return { device_path = '/test/path' }
    end
    c_object_manage.get_instance = function()
        return { bus = {} }
    end
    comm_fun.get_device_obj_by_path_interface = function()
        return {
            call_method = function()
                return true
            end
        }
    end
    log.error = function() end

    -- 缺少 Ref8PortConnector
    local obj = {
        NodeId = 'node_1',
        PortCount = 4,
        Ref4PortConnector = { Presence = 1 },
    }

    local ret = c_network_adapter_preload.set_port_count(obj, 8)
    lu.assertEquals(ret, false)
    -- PortCount 不应被修改
    lu.assertEquals(obj.PortCount, 4)
    -- 老 connector 不应被改为 0（因为在新 connector 缺失时提前返回了）
    lu.assertEquals(obj.Ref4PortConnector.Presence, 1)

    c_network_adapter.collection.find = old_find
    c_object_manage.get_instance = old_get_instance
    comm_fun.get_device_obj_by_path_interface = old_get_device_obj
    log.error = old_error
end

function TEST_network_adapter_preload:test_set_preload_port_count()
    local old_preload_find = c_network_adapter_preload.collection.find
    local obj = {NodeId = 'node_1'}
    local ctx = {
        get_initiator = function() return 'test' end
    }
    -- 1) preload_obj not found
    c_network_adapter_preload.collection.find = function() return nil end
    lu.assertError(function() c_device_manager:set_preload_port_count(obj, ctx, 2) end)
    -- 2) port_count not in allowable values
    c_network_adapter_preload.collection.find = function()
        return {
            PortCountAllowableValues = {1, 3},
            set_port_count = function() return true end
        }
    end
    lu.assertError(function() c_device_manager:set_preload_port_count(obj, ctx, 2) end)
    -- 3) set_port_count failed
    c_network_adapter_preload.collection.find = function()
        return {
            PortCountAllowableValues = {1, 2, 3},
            set_port_count = function() return false end
        }
    end
    lu.assertError(function() c_device_manager:set_preload_port_count(obj, ctx, 2) end)
    -- 4) success
    c_network_adapter_preload.collection.find = function()
        return {
            PortCountAllowableValues = {1, 2, 3},
            set_port_count = function() return true end
        }
    end
    local ok, err = pcall(function() c_device_manager:set_preload_port_count(obj, ctx, 2) end)
    lu.assertTrue(ok, err)
    c_network_adapter_preload.collection.find = old_preload_find
end

-- 覆盖 card_mgmt.lua 中 init_component_health 的失败路径：card_device_obj 获取失败
function TEST_network_adapter_preload:test_init_component_health_failed_via_init_obj()
    local log_calls = {}
    local restore_log = setup_log_capture(log_calls)

    -- 打桩 comm_fun：保证 create_card_orm_object 能成功，init_component_health 再失败
    local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
    local orig_get_object_name = comm_fun.get_object_name_by_device_path
    local orig_get_object_id = comm_fun.get_object_identifier_by_device_path

    comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
        -- 让 init_os_power_state / init_smbios_status / init_component_health 都走到
        -- “对象为空 -> 打日志并返回” 分支，避免后续依赖导致提前报错
        if interface == comm_defs.PCIE_CARD_DEVICE_INTERFACE
            or interface == comm_defs.NETWORK_ADAPTER_DEVICE_INTERFACE
            or interface == comm_defs.NETWORK_ADAPTER_OEM_INTERFACE then
            return nil
        end
        -- 其他接口返回占位对象，避免前面 create_card_orm_object 提前失败
        return {}
    end
    comm_fun.get_object_name_by_device_path = function()
        return 'test_obj'
    end
    comm_fun.get_object_identifier_by_device_path = function()
        return {1, 'mgr', 'chassis', '1'}
    end

    -- 打桩 mdb/c_network_adapter/c_factory，确保 create_card_orm_object 返回非空
    local orig_mdb_get_object = mdb.get_object
    local orig_mdb_register = mdb.register_interface
    mdb.get_object = function()
        return create_mock_mdb_object()
    end
    mdb.register_interface = function() end

    local c_factory = require 'mc.orm.factory'
    local orig_factory_get = c_factory.get_instance
    c_factory.get_instance = function()
        return { create_object = function() end }
    end

    local orig_create_mdb_object = c_network_adapter.create_mdb_object
    local orig_collection = c_network_adapter.collection
    c_network_adapter.create_mdb_object = function(obj) return obj end
    c_network_adapter.collection = {
        find = function()
            return { children = {}, ObjectName = 'test_obj' }
        end
    }

    -- 打桩 init_os_power_state 里依赖的 client 和 fructl
    local orig_on_fru_added = client.OnFruCtrlInterfacesAdded
    local orig_on_fru_changed = client.OnFruCtrlPropertiesChanged
    local orig_on_smbios_added = client.OnSmBiosInterfacesAdded
    local orig_on_smbios_changed = client.OnSmBiosPropertiesChanged
    local orig_foreach_smbios = client.ForeachSmBiosObjects
    local orig_on_comp_added = client.OnComponentInterfacesAdded
    local orig_on_comp_changed = client.OnComponentPropertiesChanged
    local orig_foreach_comp = client.ForeachComponentObjects
    local orig_get_power_status = fructl.get_power_status

    client.OnFruCtrlInterfacesAdded = function() end
    client.OnFruCtrlPropertiesChanged = function() end
    client.OnSmBiosInterfacesAdded = function() end
    client.OnSmBiosPropertiesChanged = function() end
    client.ForeachSmBiosObjects = function() end
    client.OnComponentInterfacesAdded = function() end
    client.OnComponentPropertiesChanged = function() end
    client.ForeachComponentObjects = function() end
    fructl.get_power_status = function() return 'ON' end

    local mgr = card_mgmt
    mgr.bus = create_mock_bus()
    mgr.objects = {}
    mgr.sig_slot = {}

    mgr:init_obj('/test/path')

    local found_error = false
    for _, msg in ipairs(log_calls) do
        if string.find(msg, 'init component health failed') then
            found_error = true
            break
        end
    end
    lu.assertTrue(found_error)

    -- 还原打桩
    comm_fun.get_device_obj_by_path_interface = orig_get_device_obj
    comm_fun.get_object_name_by_device_path = orig_get_object_name
    comm_fun.get_object_identifier_by_device_path = orig_get_object_id
    mdb.get_object = orig_mdb_get_object
    mdb.register_interface = orig_mdb_register
    c_factory.get_instance = orig_factory_get
    c_network_adapter.create_mdb_object = orig_create_mdb_object
    c_network_adapter.collection = orig_collection
    client.OnFruCtrlInterfacesAdded = orig_on_fru_added
    client.OnFruCtrlPropertiesChanged = orig_on_fru_changed
    client.OnSmBiosInterfacesAdded = orig_on_smbios_added
    client.OnSmBiosPropertiesChanged = orig_on_smbios_changed
    client.ForeachSmBiosObjects = orig_foreach_smbios
    client.OnComponentInterfacesAdded = orig_on_comp_added
    client.OnComponentPropertiesChanged = orig_on_comp_changed
    client.ForeachComponentObjects = orig_foreach_comp
    fructl.get_power_status = orig_get_power_status
    restore_log()
end

-- 覆盖 init_component_health 成功路径：Component 新增、属性变化和 Foreach 初始化
function TEST_network_adapter_preload:test_init_component_health_success_via_init_obj()
    local log_calls = {}
    local restore_log = setup_log_capture(log_calls)

    -- 打桩 comm_fun：PCIE_CARD_DEVICE_INTERFACE / PCIE_DEVICE_INTERFACE，
    -- 并保证 create_card_orm_object 前置依赖成功
    local orig_get_device_obj = comm_fun.get_device_obj_by_path_interface
    local orig_get_object_name = comm_fun.get_object_name_by_device_path
    local orig_get_object_id = comm_fun.get_object_identifier_by_device_path

    local card_device_obj = {}
    -- 用于 get_device_name：直接提供 DeviceName 字段
    local pcie_device_obj = { DeviceName = 'test_device_name' }

    comm_fun.get_device_obj_by_path_interface = function(bus, device_path, interface)
        if interface == comm_defs.PCIE_CARD_DEVICE_INTERFACE then
            return card_device_obj
        elseif interface == comm_defs.PCIE_DEVICE_INTERFACE then
            return pcie_device_obj
        end
        return {}
    end
    comm_fun.get_object_name_by_device_path = function()
        return 'test_obj'
    end
    comm_fun.get_object_identifier_by_device_path = function()
        return {1, 'mgr', 'chassis', '1'}
    end

    -- 打桩 mdb，用于 get_node_id_by_device_path 调用
    local orig_mdb_get_object = mdb.get_object
    local orig_mdb_register = mdb.register_interface
    mdb.get_object = function()
        return create_mock_mdb_object()
    end
    mdb.register_interface = function() end

    -- 打桩 client：记录注册的回调，并模拟 Foreach 行为；同时屏蔽与 OS 电源 / SMBIOS 相关的真实回调
    local orig_get_components = client.GetComponentObjects
    local orig_on_added = client.OnComponentInterfacesAdded
    local orig_on_changed = client.OnComponentPropertiesChanged
    local orig_foreach = client.ForeachComponentObjects
    local orig_on_fru_added = client.OnFruCtrlInterfacesAdded
    local orig_on_fru_changed = client.OnFruCtrlPropertiesChanged
    local orig_on_smbios_added = client.OnSmBiosInterfacesAdded
    local orig_on_smbios_changed = client.OnSmBiosPropertiesChanged
    local orig_foreach_smbios = client.ForeachSmBiosObjects
    local orig_get_power_status = fructl.get_power_status

    -- Health 为数字类型，初始值设为 3，便于区分各阶段更新
    local component_obj = { Name = 'test_device_name', Health = 3 }
    client.GetComponentObjects = function()
        return { ['/comp/path'] = component_obj }
    end

    local added_cb
    client.OnComponentInterfacesAdded = function(_, handler)
        added_cb = handler
    end

    local changed_cb
    client.OnComponentPropertiesChanged = function(_, handler)
        changed_cb = handler
    end

    local foreach_cb
    client.ForeachComponentObjects = function(_, handler)
        foreach_cb = handler
    end
    -- 这些回调用例中不会触发，直接打桩为空避免真实依赖
    client.OnFruCtrlInterfacesAdded = function() end
    client.OnFruCtrlPropertiesChanged = function() end
    client.OnSmBiosInterfacesAdded = function() end
    client.OnSmBiosPropertiesChanged = function() end
    client.ForeachSmBiosObjects = function() end
    fructl.get_power_status = function() return 'ON' end

    -- 打桩 factory / c_network_adapter，保证 create_card_orm_object 返回非空
    local c_factory = require 'mc.orm.factory'
    local orig_factory_get = c_factory.get_instance
    c_factory.get_instance = function()
        return { create_object = function() end }
    end

    local orig_create_mdb_object = c_network_adapter.create_mdb_object
    local orig_collection = c_network_adapter.collection
    c_network_adapter.create_mdb_object = function(obj) return obj end
    c_network_adapter.collection = {
        find = function()
            return { children = {}, ObjectName = 'test_obj' }
        end
    }

    -- 通过 init_obj 驱动 init_component_health 和相关逻辑
    local mgr = card_mgmt
    mgr.bus = create_mock_bus()
    mgr.objects = {}
    mgr.sig_slot = {}

    mgr:init_obj('/test/path')

    -- ComponentInterfacesAdded：Health 赋值为 1
    lu.assertNotNil(added_cb)
    added_cb(nil, '/comp/path', {
        Health = { value = function() return 1 end }
    })
    lu.assertEquals(card_device_obj.Health, 1)

    -- ComponentPropertiesChanged：无 Health 不修改
    lu.assertNotNil(changed_cb)
    changed_cb({}, '/comp/path', nil)
    lu.assertEquals(card_device_obj.Health, 1)

    -- ComponentPropertiesChanged：有 Health 更新为 2
    changed_cb({
        Health = { value = function() return 2 end }
    }, '/comp/path', nil)
    lu.assertEquals(card_device_obj.Health, 2)

    -- ForeachComponentObjects：最终设置为 Component.Health（3）
    lu.assertNotNil(foreach_cb)
    foreach_cb(component_obj)
    lu.assertEquals(card_device_obj.Health, 3)

    -- 还原打桩
    comm_fun.get_device_obj_by_path_interface = orig_get_device_obj
    comm_fun.get_object_name_by_device_path = orig_get_object_name
    comm_fun.get_object_identifier_by_device_path = orig_get_object_id
    mdb.get_object = orig_mdb_get_object
    mdb.register_interface = orig_mdb_register
    client.GetComponentObjects = orig_get_components
    client.OnComponentInterfacesAdded = orig_on_added
    client.OnComponentPropertiesChanged = orig_on_changed
    client.ForeachComponentObjects = orig_foreach
    client.OnFruCtrlInterfacesAdded = orig_on_fru_added
    client.OnFruCtrlPropertiesChanged = orig_on_fru_changed
    client.OnSmBiosInterfacesAdded = orig_on_smbios_added
    client.OnSmBiosPropertiesChanged = orig_on_smbios_changed
    client.ForeachSmBiosObjects = orig_foreach_smbios
    fructl.get_power_status = orig_get_power_status
    c_factory.get_instance = orig_factory_get
    c_network_adapter.create_mdb_object = orig_create_mdb_object
    c_network_adapter.collection = orig_collection
    restore_log()
end

function TEST_network_adapter_preload:test_get_log_link_status()
    local port = {
        OSLinkStatus = 'OSLinkStatus',
        LinkStatus = "Disconnected",
        Name = "Name"
    }
    
    local ret = c_network_adapter.get_log_link_status(port)
    lu.assertEquals(ret, 'OSLinkStatus')

    port = {
        OSLinkStatus = '',
        LinkStatus = "Disconnected",
        Name = "Name"
    }
    ret = c_network_adapter.get_log_link_status(port)
    lu.assertEquals(ret, 'NoLink')

    port = {
        OSLinkStatus = '',
        LinkStatus = "Connected",
        Name = "Name"
    }
    ret = c_network_adapter.get_log_link_status(port)
    lu.assertEquals(ret, 'N/A')

    port = {
        OSLinkStatus = '',
        LinkStatus = "Disconnected",
        Name = ""
    }
    ret = c_network_adapter.get_log_link_status(port)
    lu.assertEquals(ret, 'Down')

    port = {
        OSLinkStatus = '',
        LinkStatus = "Connected",
        Name = ""
    }
    ret = c_network_adapter.get_log_link_status(port)
    lu.assertEquals(ret, 'Up')

    port = {
        OSLinkStatus = '',
        LinkStatus = "",
        Name = ""
    }
    ret = c_network_adapter.get_log_link_status(port)
    lu.assertEquals(ret, 'N/A')
end
