-- Copyright (c) 2024 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 log = require 'mc.logging'
local mdb_service = require 'mc.mdb.mdb_service'

local ODATA_ID <const> = "@odata.id"
local OPTICALMODULE_INTERFACE <const> = "bmc.kepler.Systems.OpticalModule"
local NETWORKADAPTER_INTERFACE <const> = "bmc.kepler.Systems.NetworkAdapter"

local m = {}

local FILTER_KEYS = {
    'SerialNumber',
    'Manufacturer',
    'PartNumber'
}
local URL_MAP = {
    ['CPU']               = '/redfish/v1/Systems/%s/Processors/%s',
    ['NPU']               = '/redfish/v1/Systems/%s/Processors/%s',
    ['PCIe GPU Card']     = '/redfish/v1/Systems/%s/Processors/%s',
    ['Memory']            = '/redfish/v1/Systems/%s/Memory/%s',
    ['Disk']              = '/redfish/v1/Chassis/1/Drives/%s',
    ['OpticalModule']     = '/redfish/v1/Chassis/1/NetworkAdapters/%s/NetworkPorts/%s/OpticalModule',
    ['PCIe NPU Card']     = '/redfish/v1/Chassis/1/PCIeDevices/%s',
    ['PCIe Storage Card'] = '/redfish/v1/Chassis/1/PCIeDevices/%s',
    ['NetworkAdapter']    = '/redfish/v1/Chassis/1/NetworkAdapters/%s',
    ['SDI Card']          = '/redfish/v1/Chassis/1/PCIeDevices/%s',
    ['PCIe SDI Card']     = '/redfish/v1/Chassis/1/PCIeDevices/%s',
    ['PCIe RAID Card']    = '/redfish/v1/Systems/%s/Storages/%s'
}

local function get_system_id(path)
    local system_id = string.match(path, "/bmc/kepler/Systems/(%d+)")
    return system_id
end

local function get_filter(data)
    local filter = {}
    for _, key in ipairs(FILTER_KEYS) do
        if data[key] and data[key] ~= 'N/A' then
            filter[key] = data[key]
        end
    end
    return filter
end

local function get_optical_module_odata_id(obj, url_pattern)
    local filter = get_filter(obj)
    local ok, rsp = pcall(mdb_service.get_path, bus, OPTICALMODULE_INTERFACE, cjson.encode(filter), false)
    if not ok then
        log:info("Get OpticalModule path failed, err: %s.", rsp)
        return cjson.null
    end
    local port_id = string.match(rsp.Path, "NetworkPort_(%d+)_")
    local network_adapter_path = string.match(rsp.Path, "(.+)/Ports")
    ok, rsp = pcall(mdb.get_object, bus, network_adapter_path, NETWORKADAPTER_INTERFACE)
    if not ok then
        log:info("Get NetworkAdapter object failed, err: %s.", rsp)
        return cjson.null
    end
    return string.format(url_pattern, rsp.NodeId, tostring(port_id + 1))
end

local function get_cpu_odata_id(obj, url_pattern)
    local id = string.match(obj.AssetName, '%d+')
    return string.format(url_pattern, get_system_id(obj.path), tostring(id))
end

local function get_npu_odata_id(obj, url_pattern)
    local npuid, dieid = string.match(obj.AssetName, '(%d+)-(%d+)')
    --例如npu的name格式为NPU6-1,代表NPU11
    local id = (npuid - 1) * 2 + dieid
    return string.format(url_pattern, get_system_id(obj.path), 'Npu' .. tostring(id))
end

local function get_gpu_odata_id(obj, url_pattern)
    local id = string.match(obj.AssetName, '%d+')
    return string.format(url_pattern, get_system_id(obj.path), 'Gpu' .. tostring(id))
end

local function get_memory_odata_id(obj, url_pattern)
    return string.format(url_pattern, get_system_id(obj.path), obj.AssetName)
end

local function get_disk_odata_id(obj, url_pattern)
    return string.format(url_pattern, 'HDDPlaneDisk' .. obj.Slot)
end

local function get_pciecard_odata_id(obj, url_pattern)
    return string.format(url_pattern, 'PCIeCard' .. obj.Slot)
end

local function get_network_adapter_odata_id(obj, url_pattern)
    local filter = {}
    filter['Name'] = obj['AssetName'] ~= 'N/A' and obj['AssetName'] or nil
    filter['FirmwareVersion'] = obj['FirmwareVersion'] ~= 'N/A' and obj['FirmwareVersion'] or nil
    filter['SlotNumber'] = obj['Slot'] ~= 'N/A' and tonumber(obj['Slot']) or nil
    local ok, rsp = pcall(mdb_service.get_path, bus, NETWORKADAPTER_INTERFACE, cjson.encode(filter), false)
    if not ok then
        log:info("Get NetworkAdapter path failed, err: %s", rsp)
        return cjson.null
    end
    ok, rsp = pcall(mdb.get_object, bus, rsp.Path, NETWORKADAPTER_INTERFACE)
    if not ok then
        log:info("Get NetworkAdapter object failed, err: %s", rsp)
        return cjson.null
    end
    return string.format(url_pattern, rsp.NodeId)
end

local function get_raidcard_odata_id(obj, url_pattern)
    return string.format(url_pattern, get_system_id(obj.path), 'RAIDStorage' .. obj.Slot)
end

local CB_MAP = {
    ['CPU']               = get_cpu_odata_id,
    ['NPU']               = get_npu_odata_id,
    ['PCIe GPU Card']     = get_gpu_odata_id,
    ['Memory']            = get_memory_odata_id,
    ['Disk']              = get_disk_odata_id,
    ['OpticalModule']     = get_optical_module_odata_id,
    ['PCIe NPU Card']     = get_pciecard_odata_id,
    ['PCIe Storage Card'] = get_pciecard_odata_id,
    ['NetworkAdapter']    = get_network_adapter_odata_id,
    ['SDI Card']          = get_pciecard_odata_id,
    ['PCIe SDI Card']     = get_pciecard_odata_id,
    ['PCIe RAID Card']    = get_raidcard_odata_id
}

local function get_odata_id(obj)
    local url_pattern = URL_MAP[obj.AssetType]
    local cb = CB_MAP[obj.AssetType]
    if not cb or not url_pattern then
        return cjson.null
    end
    return cb(obj, url_pattern)
end

function m.get_result(asset_list)
    if not asset_list or #asset_list == 0 then
        return {}
    end
    local res = cjson.json_object_ordered_decode(asset_list)
    local results = cjson.json_object_new_array()
    for i = 1, #res, 1 do
        local asset_item = cjson.json_object_new_object()
        local data_item = cjson.json_object_new_object()
        asset_item.Name = res[i].AssetName
        asset_item[ODATA_ID] = get_odata_id(res[i])
        data_item.AssetType = res[i].AssetType
        data_item.AssetTag = res[i].AssetTag
        data_item.SerialNumber = res[i].SerialNumber
        data_item.PartNumber = res[i].PartNumber
        data_item.PCBVersion = res[i].PCBVersion
        data_item.FirmwareVersion = res[i].FirmwareVersion
        data_item.Manufacturer = res[i].Manufacturer
        data_item.ManufactureDate = res[i].ManufactureDate
        asset_item.Data = data_item
        results[#results + 1] = asset_item
    end
    return results
end

return m
