-- 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 hardware_config = require 'nvme.nvme_mi_protocol.nvme_mi'
local NVME_MI_OPTIONS = require 'nvme.nvme_mi_protocol.nvme_mi_def'
local nvme_utils = require 'nvme.utils'

TEST_nvmemi_send_receive = {}

local function pack(obj)
    local result = ''
    for _, v in ipairs(obj) do
        result = result .. v
    end
    return result
end

local smart_log_expect_request = {
    message_type = NVME_MI_OPTIONS.NVME_ADMIN_COMMANDS_OPCODE.NVME_ADMIN_GET_LOG_PAGE,
    command_slot_identifier = 0,
    data = pack({
        '\x02',             -- opcode
        '\x03',             -- flags
        '\x01\x00',         -- ctrl_id
        '\xFF\xFF\xFF\xFF', -- nsid
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- data_offset
        '\x00\x02\x00\x00', -- data_len
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x02\x00\x7F\x00', -- cdw10
        '\x00\x00\x00\x00', -- cdw11
        '\x00\x00\x00\x00', -- cdw12
        '\x00\x00\x00\x00', -- cdw13
        '\x00\x00\x00\x00', -- cdw14
        '\x00\x00\x00\x00'  -- rsvd
    })
}

function TEST_nvmemi_send_receive:test_nvme_mctp_smart_log_request()
    local request = hardware_config.mctp().properties.SmartLog.request
    for k, v in pairs(smart_log_expect_request) do
        lu.assertEquals(request[k], v)
    end
end

local smart_log_expect_response = {
    host_reads               = 69889,
    critical_warning         = 0,
    endu_grp_crit_warn_sumry = 0,
    critical_comp_time       = 0,
    avail_spare              = 100,
    thm_temp1_trans_count    = 0,
    power_on_hours           = 17,
    unsafe_shutdowns         = 1,
    temperature              = 307,
    warning_temp_time        = 2,
    ctrl_busy_time           = 0,
    power_cycles             = 52,
    data_units_read          = 2918,
    percent_used             = 1,
    thm_temp1_total_time     = 0,
    thm_temp2_total_time     = 308,
    thm_temp2_trans_count    = 0,
    num_err_log_entries      = 0,
    media_errors             = 0,
    temp_sensor1             = 0,
    temp_sensor2             = 0,
    temp_sensor3             = 0,
    temp_sensor4             = 0,
    temp_sensor5             = 0,
    temp_sensor6             = 0,
    temp_sensor7             = 0,
    temp_sensor8             = 0,
    host_writes              = 3286,
    data_units_written       = 51,
    spare_thresh             = 10
}

function TEST_nvmemi_send_receive:test_nvme_mctp_smart_log_response()
    local bin_rsp = hardware_config.mctp().properties.SmartLog.response(pack({
        '\x00',
        '\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00',                                                             -- critical_warning
        '\x33\x01',                                                         -- temperature
        '\x64',                                                             -- avail_spare
        '\x0A',                                                             -- spare_thresh
        '\x01',                                                             -- percent_used
        '\x00',                                                             -- endu_grp_crit_warn_sumry
        '\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',
        '\x66\x0B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- data_units_read
        '\x33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- data_units_written
        '\x01\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- data_units_written
        '\xD6\x0C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- host_reads
        '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- host_writes
        '\x34\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- ctrl_busy_time
        '\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- power_cycles
        '\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- power_on_hours
        '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- unsafe_shutdowns
        '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- media_errors
        '\x02\x00\x00\x00',                                                 -- warning_temp_time
        '\x00\x00\x00\x00',                                                 -- critical_comp_time
        '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- temp_sensor
        '\x00\x00\x00\x00',                                                 -- thm_temp1_trans_count
        '\x00\x00\x00\x00',                                                 -- thm_temp2_trans_count
        '\x00\x00\x00\x00',                                                 -- thm_temp1_total_time
        '\x34\x01\x00\x00',                                                 -- thm_temp2_total_time
        '\x00\x00\x00\x00'                                                  -- rsvd
    }))
    local data = nvme_utils.parse_rsp_status(bin_rsp)
    if not data then
        return
    end
    local rsp = NVME_MI_OPTIONS.smart_log_response:unpack(data, true)
    local response = {
        critical_warning = rsp.critical_warning,
        temperature = rsp.temperature,
        avail_spare = rsp.avail_spare,
        spare_thresh = rsp.spare_thresh,
        percent_used = rsp.percent_used,
        endu_grp_crit_warn_sumry = rsp.endu_grp_crit_warn_sumry,
        data_units_read = rsp.data_units_read_h << 8 | rsp.data_units_read_l,
        data_units_written = rsp.data_units_written_h << 8 | rsp.data_units_written_l,
        host_reads = rsp.host_reads_h << 8 | rsp.host_reads_l,
        host_writes = rsp.host_writes_h << 8 | rsp.host_writes_l,
        ctrl_busy_time = rsp.ctrl_busy_time_h << 8 | rsp.ctrl_busy_time_l,
        power_cycles = rsp.power_cycles_h << 8 | rsp.power_cycles_l,
        power_on_hours = rsp.power_on_hours_h << 8 | rsp.power_on_hours_l,
        unsafe_shutdowns = rsp.unsafe_shutdowns_h << 8 | rsp.unsafe_shutdowns_l,
        media_errors = rsp.media_errors_h << 8 | rsp.media_errors_l,
        num_err_log_entries = rsp.num_err_log_entries_h << 8 | rsp.num_err_log_entries_l,
        warning_temp_time = rsp.warning_temp_time,
        critical_comp_time = rsp.critical_comp_time,
        temp_sensor1 = rsp.temp_sensor1,
        temp_sensor2 = rsp.temp_sensor2,
        temp_sensor3 = rsp.temp_sensor3,
        temp_sensor4 = rsp.temp_sensor4,
        temp_sensor5 = rsp.temp_sensor5,
        temp_sensor6 = rsp.temp_sensor6,
        temp_sensor7 = rsp.temp_sensor7,
        temp_sensor8 = rsp.temp_sensor8,
        thm_temp1_trans_count = rsp.thm_temp1_trans_count,
        thm_temp2_trans_count = rsp.thm_temp2_trans_count,
        thm_temp1_total_time = rsp.thm_temp1_total_time,
        thm_temp2_total_time = rsp.thm_temp2_total_time
    }
    for k, v in pairs(smart_log_expect_response) do
        lu.assertEquals(response[k], v)
    end
end

local fw_log_expect_request = {
    message_type = NVME_MI_OPTIONS.NVME_ADMIN_COMMANDS_OPCODE.NVME_ADMIN_GET_LOG_PAGE,
    command_slot_identifier = 0,
    data = pack({
        '\x02',             -- opcode
        '\x03',             -- flags
        '\x01\x00',         -- ctrl_id
        '\xFF\xFF\xFF\xFF', -- nsid
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- data_offset
        '\x00\x02\x00\x00', -- data_len
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x03\x80\x7F\x00', -- cdw10
        '\x00\x00\x00\x00', -- cdw11
        '\x00\x00\x00\x00', -- cdw12
        '\x00\x00\x00\x00', -- cdw13
        '\x00\x00\x00\x00', -- cdw14
        '\x00\x00\x00\x00'  -- rsvd
    })
}

function TEST_nvmemi_send_receive:test_nvme_mctp_fw_log_request()
    local request = hardware_config.mctp().properties.FwLog.request
    for k, v in pairs(fw_log_expect_request) do
        lu.assertEquals(request[k], v)
    end
end

local fw_log_expect_response = {
    afi = 1,
    frs1 = 2500,
    frs2 = 1549,
    frs6 = 1512
}

function TEST_nvmemi_send_receive:test_nvme_mctp_fw_log_response()
    local response = hardware_config.mctp().properties.FwLog.response(pack({
        '\x00',
        '\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x01',                             -- afi
        '\x00\x00\x00\x00\x00\x00\x00',     -- rsvd
        '\x32\x35\x30\x30\x20\x20\x20\x20', -- frs1
        '\x31\x35\x34\x39\x20\x20\x20\x20', -- frs2
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- frs3
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- frs4
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- frs5
        '\x31\x35\x31\x32\x20\x20\x20\x20', -- frs6
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- frs7
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- rsvd
    }))
    for k, v in pairs(fw_log_expect_response) do
        lu.assertEquals(response[k], v)
    end
end

local error_log_expect_request = {
    message_type = NVME_MI_OPTIONS.NVME_ADMIN_COMMANDS_OPCODE.NVME_ADMIN_GET_LOG_PAGE,
    command_slot_identifier = 0,
    data = pack({
        '\x02',             -- opcode
        '\x03',             -- flags
        '\x01\x00',         -- ctrl_id
        '\xFF\xFF\xFF\xFF', -- nsid
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- data_offset
        '\x00\x10\x00\x00', -- data_len
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x01\x00\xFF\x03', -- cdw10
        '\x00\x00\x00\x00', -- cdw11
        '\x00\x00\x00\x00', -- cdw12
        '\x00\x00\x00\x00', -- cdw13
        '\x00\x00\x00\x00', -- cdw14
        '\x00\x00\x00\x00'  -- rsvd
    })
}

function TEST_nvmemi_send_receive:test_nvme_mctp_error_log_request()
    local request = hardware_config.mctp().properties.ErrorInformation.request
    for k, v in pairs(error_log_expect_request) do
        lu.assertEquals(request[k], v)
    end
end

local error_log_expect_response = {
    [1] = {
        error_count = 0,
        sqid = 0,
        cmdid = 0,
        status_field = 0,
        parm_error_location = 0,
        lba = 0,
        nsid = 0,
        vsia = 0,
        trtype = 0,
        csi = 0,
        trtype_spec_info = 0
    }
}

function TEST_nvmemi_send_receive:test_nvme_mctp_error_log_response()
    local response = hardware_config.mctp().properties.ErrorInformation.response(pack({
        '\x00',
        '\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- error_count
        '\x00\x00',                         -- sqid
        '\x00\x00',                         -- cmdid
        '\x00\x00',                         -- status_field
        '\x00\x00',                         -- parm_error_location
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- lba
        '\x00\x00\x00\x00',                 -- nsid
        '\x00',                             -- vsia
        '\x00',                             -- trtype
        '\x00\x00',                         -- rsvd1
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- csi
        '\x00\x00',                         -- trtype_spec_info
        '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
    }))
    for k, v in pairs(error_log_expect_response[1]) do
        lu.assertEquals(response[1][k], v)
    end
end

function TEST_nvmemi_send_receive:test_nvme_mctp_read_ctrl_list_response()
    local response = hardware_config.mctp().properties.ReadCtrlList.response(pack({
        '\x00',         -- status
        '\x00\x00\x00\x00\x01\x01\x00', -- rsvd
    }))
    lu.assertEquals(response, 1)
end