-- 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 object_manage = require 'mc.mdb.object_manage'
local utils = require 'mc.utils'
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.JM800'
local skynet = require 'skynet'

TEST_oem_smbus_network_adapter = {}

local chip_data = {
    '\x80','\x12','\x1f','\x53','\xff','\xff','\xff','\xff','\xff','\x50',
    '\xd3','\x3b','\x01','\x7d','\x0d','\xff','\xff','\xff','\xff','\xff',
    '\xff','\xff','\xff','\xff','\xff','\xff','\xff','\xff','\xff','\xff',
    '\xff','\xff','\xff','\xff','\xff','\xff','\xff','\xff','\x03','\x00',
    '\x06','\x01','\x00','\x00','\xff','\xff','\x10','\xff','\xff','\xff',
    '\x2f','\xff','\xff','\x27','\x26','\xff','\xff','\xf0','\x10','\x11',
    '\x12','\x13','\xff','\xff','\x02','\xff','\x52','\x72','\xff','\xff',
    '\xff','\xff','\x00','\x20','\x40','\x60','\xff','\xff','\xff','\xff',
    '\x49','\x49','\x49','\x51','\xff','\xff','\xff','\xff','\x07','\x55',
    '\xff','\xff','\xff','\xff','\xff','\xff','\xff','\xff','\xff','\xff'
}

local 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] = 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
            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_origin = require 'device.class.network_adapter'
    local adapter
    

function TEST_oem_smbus_network_adapter:setUp()
    self.position = '0001010104123'
    self.uuid = 'testuid'
    self.port_count = 4

    adapter = deepCopy(adapter_origin)
    adapter.BoardID = 32786
    adapter.DeviceLocator = 'some locator'
    adapter.Name = "LingYao0517"
    adapter.Position = '0001010104123'
    adapter.Model = "JM800"
    adapter.VendorID = "0x1f53"
    adapter.DeviceID = "0x2c20"
    adapter.NetworkPortCount = 4
    adapter.SupportedLLDP = true
    adapter.RefChip = chip
end

function TEST_oem_smbus_network_adapter:CreateVLANs()
end

function TEST_oem_smbus_network_adapter:InitRefChip(input)
    input.oem_smbus_config_obj = hardware_config.oem_smbus:new(input.RefChip)
    input.oem_smbus_config_obj.chip = chip
    hardware_config.chip = chip
end

function TEST_oem_smbus_network_adapter:tearDown()
    adapter = nil
end

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

function TEST_oem_smbus_network_adapter: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.RefChip:Read(nil, 88, 1)
    local lldp_byte = string.byte(lldp_set_byte, 1)
    local lldp_num = tonumber(string.format("%d", lldp_byte))
    lu.assertEquals(lldp_num, 7)

    local port1 = deepCopy(port_origin)
    port1.PortID = 1
    adapter.oem_smbus_config_obj:SetPortLLDPEnable(port1, false)
    lldp_set_byte = adapter.RefChip:Read(nil, 88, 1)
    lldp_byte = string.byte(lldp_set_byte, 1)
    lldp_num = tonumber(string.format("%d", lldp_byte))
    lu.assertEquals(lldp_num, 5)

    -- 测试熟悉监听逻辑，调用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.RefChip:Read(nil, 88, 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)
    local mock_get_parent_3 = port3.get_parent
    port3.get_parent = function ()
        return {
            SupportedLLDP = true
        }
    end
    port3.PortID = 3
    port3.oem_smbus_config_obj = adapter.oem_smbus_config_obj
    port3:redfish_set_lldp_capability(true)
    lldp_set_byte = adapter.RefChip:Read(nil, 88, 1)
    lldp_byte = string.byte(lldp_set_byte, 1)
    lldp_num = tonumber(string.format("%d", lldp_byte))
    lu.assertEquals(lldp_num, 15)
    port2.get_parent = mock_get_parent
    port3.get_parent = mock_get_parent_3
end

function TEST_oem_smbus_network_adapter:test_can_get_network_adapter_info_power_on()
    skynet.sleep(3000)
    self:InitRefChip(adapter)

    local pro = {}
    adapter:update_oem_smbus_network_adapter_power_on(pro)

    lu.assertEquals(adapter.Position, self.position)
    lu.assertEquals(adapter.TemperatureCelsius, 47)
    lu.assertEquals(adapter.Health, 1)
end

function TEST_oem_smbus_network_adapter:test_can_get_network_adapter_info_power_off()
    skynet.sleep(3000)
    
    self:InitRefChip(adapter)
    adapter.TemperatureCelsius = 66
    adapter.Health = 1

    adapter:update_oem_smbus_info_power_off()

    lu.assertEquals(adapter.Position, self.position)
    lu.assertEquals(adapter.TemperatureCelsius, 0x4000)
    lu.assertEquals(adapter.Health, 0)
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:test_can_get_ports_info_power_on()
    skynet.sleep(3000)
    adapter.RefChip = chip
    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}
    adapter:update_oem_smbus_port_properties_power_on(ports)

    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, 25000)
    lu.assertEquals(port0.FullDuplex, true)
    lu.assertEquals(port0.AutoSpeedNegotiation, false)
    lu.assertEquals(port0.LinkStatus, 'Connected')
    -- 模拟上报无效数据，温度为10，不参与最值计算
    lu.assertEquals(op0.ReadingCelsius, 10)

    lu.assertEquals(port1.MACAddress, '50:D3:3B:01:7D:0E')
    lu.assertEquals(port1.PermanentMACAddress, '50:D3:3B:01:7D:0E')
    lu.assertEquals(port1.FirmwareVersion, '3.0.6')
    lu.assertEquals(port1.LLDPEnabled, true)
    -- 模拟上报无效数据，速率为0
    lu.assertEquals(port1.SpeedMbps, 0)
    lu.assertEquals(port1.FullDuplex, true)
    lu.assertEquals(port1.AutoSpeedNegotiation, false)
    -- 模拟上报无效数据，网口状态为N/A
    lu.assertEquals(port1.LinkStatus, 'N/A')
    -- 模拟上报无效数据，温度为10，不参与最值计算
    lu.assertEquals(op1.ReadingCelsius, 10)

    lu.assertEquals(port2.MACAddress, '50:D3:3B:01:7D:0F')
    lu.assertEquals(port2.PermanentMACAddress, '50:D3:3B:01:7D:0F')
    lu.assertEquals(port2.FirmwareVersion, '3.0.6')
    lu.assertEquals(port2.LLDPEnabled, true)
    lu.assertEquals(port2.SpeedMbps, 25000)
    lu.assertEquals(port2.FullDuplex, true)
    lu.assertEquals(port2.AutoSpeedNegotiation, false)
    lu.assertEquals(port2.LinkStatus, 'Disconnected')
    lu.assertEquals(op2.ReadingCelsius, 39)

    lu.assertEquals(port3.MACAddress, '50:D3:3B:01:7D:10')
    lu.assertEquals(port3.PermanentMACAddress, '50:D3:3B:01:7D:10')
    lu.assertEquals(port3.FirmwareVersion, '3.0.6')
    lu.assertEquals(port3.LLDPEnabled, false)
    lu.assertEquals(port3.SpeedMbps, 25000)
    lu.assertEquals(port3.FullDuplex, true)
    lu.assertEquals(port3.AutoSpeedNegotiation, false)
    lu.assertEquals(port3.LinkStatus, 'Disconnected')
    lu.assertEquals(op3.ReadingCelsius, 38)
end

function TEST_oem_smbus_network_adapter: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