-- 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 P

local lu = require 'luaunit'
local c_network_adapter = require 'device.class.network_adapter'
local c_object_manage = require 'mc.orm.object_manage'
local c_tasks = require 'mc.orm.tasks'
local c_network_port = require 'device.class.network_port'
local c_network_adapter_db = require 'network_adapter.db'
local hook_tasks = require 'test_common.hook_tasks'
local test_utils = require 'test_utils'
local create_mds_object = test_utils.create_mds_object
local c_optical_module = require 'device.class.optical_module'
local hardware_config = require 'hardware_config.HyperCard'
local client = require 'network_adapter.client'
local skynet = require 'skynet'

TEST_oem_smbus_network_adapter_hypercard = {}

local bmc_chip_data = {
    '\x80', '\x0b', '\x1f', '\x53', '\x01', '\xff', '\x20', '\x00', '\xf0', '\x84',
    '\x30', '\xce', '\x02', '\x23', '\x5c', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x03', '\x03', '\x03', '\xff', '\xff', '\xff', '\xff',
    '\xff', '\xff', '\x6e', '\x76', '\x86', '\x52', '\x0c', '\x49', '\x49', '\x49',
    '\xff', '\xff', '\xff', '\xff', '\x5a', '\x31', '\xff', '\xff', '\x2d', '\x2b',
    '\x39', '\xfe', '\x10', '\x27', '\x26', '\xfe', '\xfe', '\x2a', '\xff', '\xff',
    '\xff', '\xff', '\xff', '\xff', '\x11', '\x22', '\x53', '\xff', '\xff', '\xff',
    '\xff', '\xff', '\x05', '\x24', '\x41', '\x60', '\xff', '\xff', '\xff', '\xff',
    '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
    '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xfe', '\xff', '\xff'
}

local cpld_chip_data = {
    '\x8a', '\x01', '\x1f', '\x53', '\x01', '\x51', '\x0c', '\x55', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x5a', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x88', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x02', '\x00', '\x00'
}

local bmc_chip = {
    WriteRead = true,
    Read = function(self, ctx, read_offset, read_len)
        local rsp_data = {}
        read_offset = read_offset + 1
        read_len = read_len - 1

        for i = 0, read_len do
            rsp_data[#rsp_data + 1] = bmc_chip_data[read_offset + i]
        end

        return table.concat(rsp_data)
    end,
    Write = function(self, ctx, write_offset, data)
        write_offset = write_offset + 1

        for i, v in ipairs(data) do
            bmc_chip_data[write_offset + i - 1] = string.char(v)
        end
        return true
    end
}

local cpld_chip = {
    WriteRead = true,
    Read = function(self, ctx, read_offset, read_len)
        local rsp_data = {}
        read_offset = read_offset + 1
        read_len = read_len - 1

        for i = 0, read_len do
            rsp_data[#rsp_data + 1] = cpld_chip_data[read_offset + i]
        end

        return table.concat(rsp_data)
    end,
    Write = function(self, ctx, write_offset, data)
        write_offset = write_offset + 1

        for i, v in ipairs(data) do
            cpld_chip_data[write_offset + i - 1] = string.char(v)
        end
        return true
    end
}

    local function deepCopy(origin, seen)
        seen = seen or {}

        if seen[origin] then
            return seen[origin]
        end

        local copy
        if type(origin) == 'table' then
            copy = {}
            seen[origin] = copy

            for orig_key, orig_value in pairs(origin) do
                copy[deepCopy(orig_key, seen)] = deepCopy(orig_value, seen)
            end

            setmetatable(copy, deepCopy(getmetatable(origin), seen))
        else
            copy = origin
        end

        return copy
    end

    local adapter

function TEST_oem_smbus_network_adapter_hypercard:setUp()
    self.position = '0001010104456'
    self.uuid = 'testuid'
    self.port_count = 6

    adapter = require 'device.class.network_adapter'
    adapter.BoardID = 32786
    adapter.DeviceLocator = 'some locator'
    adapter.Name = "testname"
    adapter.Position = '0001010104456'
    adapter.Model = "HyperCard4.0"
    adapter.NetworkPortCount = 6
    adapter.SupportedLLDP = true
    adapter.RefChip = bmc_chip
    adapter.RefCPLDChip = cpld_chip
end

function TEST_oem_smbus_network_adapter_hypercard:CreateVLANs()
end

function TEST_oem_smbus_network_adapter_hypercard:InitRefChip(input)
    input.oem_smbus_config_obj = hardware_config.oem_smbus.new(input.RefChip, input.RefCPLDChip, self.position)
    input.oem_smbus_config_obj.bmc_chip = bmc_chip
    input.oem_smbus_config_obj.cpld_chip = cpld_chip
    input.oem_smbus_config_obj.position = self.position
    hardware_config.bmc_chip = bmc_chip
    hardware_config.cpld_chip = cpld_chip
    hardware_config.position = self.position
end

function TEST_oem_smbus_network_adapter_hypercard:tearDown()
    adapter = nil
end

function TEST_oem_smbus_network_adapter_hypercard:flush()
    c_tasks.get_instance():run_all_task()
    self.database.db:flush()
end

function TEST_oem_smbus_network_adapter_hypercard:test_can_set_port_lldp()
    skynet.sleep(3000)
    self:InitRefChip(adapter)
    local port_origin = require 'device.class.network_port'
    local port0 = deepCopy(port_origin)
    port0.PortID = 0

    -- 通过外部设置lldp，实际修改寄存器内部值，再次读取校验值是否发生变更
    adapter.oem_smbus_config_obj:SetPortLLDPEnable(port0, true)
    local lldp_set_byte = adapter.RefCPLDChip:Read(nil, 129, 1)
    local lldp_byte = string.byte(lldp_set_byte, 1)
    local lldp_num = tonumber(string.format("%d", lldp_byte))
    lu.assertEquals(lldp_num, 1)

    local port1 = deepCopy(port_origin)
    port1.PortID = 1
    adapter.oem_smbus_config_obj:SetPortLLDPEnable(port1, true)
    lldp_set_byte = adapter.RefCPLDChip:Read(nil, 129, 1)
    lldp_byte = string.byte(lldp_set_byte, 1)
    lldp_num = tonumber(string.format("%d", lldp_byte))
    lu.assertEquals(lldp_num, 3)

    -- 测试属性监听逻辑，调用redfish_set_lldp_capability而非SetPortLLDPEnable
    local port2 = deepCopy(port_origin)
    port2.PortID = 2
    local mock_get_parent = port2.get_parent
    port2.get_parent = function ()
        return {
            SupportedLLDP = true
        }
    end
    port2.oem_smbus_config_obj = adapter.oem_smbus_config_obj
    port2:redfish_set_lldp_capability(true)
    lldp_set_byte = adapter.RefCPLDChip:Read(nil, 129, 1)
    lldp_byte = string.byte(lldp_set_byte, 1)
    lldp_num = tonumber(string.format("%d", lldp_byte))
    lu.assertEquals(lldp_num, 7)

    -- 测试属性监听逻辑，调用redfish_set_lldp_capability而非SetPortLLDPEnable
    local port3 = deepCopy(port_origin)
    port3.PortID = 3
    local mock_get_parent_3 = port3.get_parent
    port3.get_parent = function ()
        return {
            SupportedLLDP = true
        }
    end
    port3.oem_smbus_config_obj = adapter.oem_smbus_config_obj
    port3:redfish_set_lldp_capability(true)
    lldp_set_byte = adapter.RefCPLDChip:Read(nil, 129, 1)
    lldp_byte = string.byte(lldp_set_byte, 1)
    lldp_num = tonumber(string.format("%d", lldp_byte))
    lu.assertEquals(lldp_num, 15)

    port0.PortID = 0
    adapter.oem_smbus_config_obj:SetPortLLDPEnable(port0, false)
    lldp_set_byte = adapter.RefCPLDChip:Read(nil, 129, 1)
    lldp_byte = string.byte(lldp_set_byte, 1)
    lldp_num = tonumber(string.format("%d", lldp_byte))
    lu.assertEquals(lldp_num, 14)
    port2.get_parent = mock_get_parent
    port3.get_parent = mock_get_parent_3
end

local function create_port(port_origin, op_origin, port_id)
    local port = deepCopy(port_origin)
    local op = deepCopy(op_origin)
    port.PortID = port_id
    port.t_port_prop_without_bma = {}
    op.PortID = port_id
    port.get_optical_module = function ()
        return op
    end
    return port, op
end

function TEST_oem_smbus_network_adapter_hypercard:test_can_get_ports_info_power_on()
    skynet.sleep(3000)
    self:InitRefChip(adapter)
    local port_origin = require 'device.class.network_port'
    local op_origin = require 'device.class.optical_module'
    local port0, op0 = create_port(port_origin, op_origin, 0)
    local port1, op1 = create_port(port_origin, op_origin, 1)
    local port2, op2 = create_port(port_origin, op_origin, 2)
    local port3, op3 = create_port(port_origin, op_origin, 3)

    local ports = {port0, port1, port2, port3}
    local mock_GetProductObjects = client.GetProductObjects
    client.GetProductObjects = function(client)
        return {
            {
                extra_params = {
                    Position = self.position
                },
                ProductCustomInfo = '84:30:CE:02:23:5C42Panshi_ASIC_DPU-16-32GB-240GB'
            }
        }
    end
    adapter:update_oem_smbus_port_properties_power_on(ports)
    lu.assertEquals(port0.LLDPEnabled, false)
    lu.assertEquals(port0.SpeedMbps, 10000)
    lu.assertEquals(port0.FullDuplex, false)
    lu.assertEquals(port0.AutoSpeedNegotiation, true)
    lu.assertEquals(port0.LinkStatus, 'Connected')
    lu.assertEquals(port0.MACAddress, '84:30:CE:02:23:5C')
    -- 模拟上报无效数据，温度为10，不参与最值计算
    lu.assertEquals(op0.ReadingCelsius, 254)
    lu.assertEquals(op0.Presence, 0)

    lu.assertEquals(port1.LLDPEnabled, false)
    -- 模拟上报无效数据，速率为0
    lu.assertEquals(port1.SpeedMbps, 25000)
    lu.assertEquals(port1.FullDuplex, true)
    lu.assertEquals(port1.AutoSpeedNegotiation, true)
    -- 模拟上报无效数据，网口状态为N/A
    lu.assertEquals(port1.LinkStatus, 'Disconnected')
    lu.assertEquals(port1.MACAddress, '84:30:CE:02:23:5D')
    -- 模拟上报无效数据，温度为10，不参与最值计算
    lu.assertEquals(op1.ReadingCelsius, 16)
    lu.assertEquals(op1.Presence, 1)

    lu.assertEquals(port2.LLDPEnabled, false)
    lu.assertEquals(port2.SpeedMbps, 40000)
    lu.assertEquals(port2.FullDuplex, false)
    lu.assertEquals(port2.AutoSpeedNegotiation, false)
    lu.assertEquals(port2.LinkStatus, 'Connected')
    lu.assertEquals(port2.MACAddress, '84:30:CE:02:23:5E')
    lu.assertEquals(op2.ReadingCelsius, 39)
    lu.assertEquals(op2.Presence, 0)

    lu.assertEquals(port3.LLDPEnabled, false)
    lu.assertEquals(port3.SpeedMbps, 0)
    lu.assertEquals(port3.FullDuplex, true)
    lu.assertEquals(port3.AutoSpeedNegotiation, false)
    lu.assertEquals(port3.LinkStatus, 'N/A')
    lu.assertEquals(port3.MACAddress, '84:30:CE:02:23:5F')
    lu.assertEquals(op3.ReadingCelsius, 38)
    lu.assertEquals(op3.Presence, 1)
    client.GetProductObjects = mock_GetProductObjects
    port0.get_optical_module = mock_get_optical_module_0
    port1.get_optical_module = mock_get_optical_module_1
    port2.get_optical_module = mock_get_optical_module_2
    port3.get_optical_module = mock_get_optical_module_3
end

function TEST_oem_smbus_network_adapter_hypercard:test_can_get_ports_info_power_off()
    skynet.sleep(3000)
    local port_origin = require 'device.class.network_port'
    local port0 = deepCopy(port_origin)
    port0.MACAddress = '50:D3:3B:01:7D:0D'
    port0.PermanentMACAddress = '50:D3:3B:01:7D:0D'
    port0.FirmwareVersion = '3.0.6'
    port0.LLDPEnabled = true
    port0.SpeedMbps = 25000
    port0.FullDuplex = true
    port0.AutoSpeedNegotiation = true
    port0.LinkStatus = 'Connected'
    local op_origin = require 'device.class.optical_module'
    local op0 = deepCopy(op_origin)
    op0.ReadingCelsius = 38

    local mock_get_optical_module = port0.get_optical_module
    port0.get_optical_module = function ()
        return op0
    end

    local ports = {port0}
    adapter:update_oem_smbus_info_power_off(ports)

    -- mac,固件版本,lldp维持最后一次获取的数据不变，其余信息恢复
    lu.assertEquals(port0.MACAddress, '50:D3:3B:01:7D:0D')
    lu.assertEquals(port0.PermanentMACAddress, '50:D3:3B:01:7D:0D')
    lu.assertEquals(port0.FirmwareVersion, '3.0.6')
    lu.assertEquals(port0.LLDPEnabled, true)
    lu.assertEquals(port0.SpeedMbps, 0)
    lu.assertEquals(port0.FullDuplex, false)
    lu.assertEquals(port0.AutoSpeedNegotiation, false)
    lu.assertEquals(port0.LinkStatus, 'N/A')
    lu.assertEquals(op0.ReadingCelsius, 0xffff)
    port0.get_optical_module = mock_get_optical_module
end
