-- Copyright (c) 2026 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.
--         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 context = require 'mc.context'
local cjson = require 'cjson'

local m = {}

local FruType =  {
    ['RiserCard'] = '15',
    ['CpuBoard'] =  '36',
    ['ExpBoard'] =  '50'
}

local PhysicalContextType =  {
    ['RiserCard'] = 'Board',
    ['CpuBoard'] =  'Backplane',
    ['ExpBoard'] =  'ExpansionSubsystem'
}

local function get_valid_string_property(val)
    if val == '' or val == 'N/A' or val == 'NA' or val == 'Unknown' or val == 'null' then
        return null
    end
    return val
end

local function flatten_arrays(input)
    local result = {}
    for _, arr in ipairs(input) do
        for _, sub_arr in ipairs(arr) do
            for _, elem in ipairs(sub_arr) do
                table.insert(result, elem)
            end
        end
    end
    return result
end

local assembly = {
    ['Processors/CPU'] = function (path, index)
         local processorObj = mdb.get_object(bus, path, 'bmc.kepler.Systems.Processor')
         return {
            MemberId = index,
            Name = processorObj.Name,
            Model = get_valid_string_property(processorObj.Model),
            PartNumber = get_valid_string_property(processorObj.PartNumber),
            PhysicalContext = 'CPU',
            Producer = get_valid_string_property(processorObj.Manufacturer),
            SerialNumber = get_valid_string_property(processorObj.SN)
         }
    end,
    ['Memory'] = function (path, index)
        local memoryObj = mdb.get_object(bus, path, 'bmc.kepler.Systems.Memory')
        return {
            MemberId = index,
            Name = memoryObj.DimmName,
            Model = get_valid_string_property(memoryObj.DimmName),
            PartNumber = get_valid_string_property(memoryObj.PartNumber),
            PhysicalContext = 'Memory',
            Producer = get_valid_string_property(memoryObj.Manufacturer),
            ProductionDate = get_valid_string_property(memoryObj.ManufacturingDate),
            SerialNumber = get_valid_string_property(memoryObj.SerialNumber)
        }
    end,
    ['Storage/Drives'] = function (path, index)
        local driveObj = mdb.get_object(bus, path, 'bmc.kepler.Systems.Storage.Drive')
        local hardwareObj = mdb.get_object(bus, path, 'bmc.kepler.Inventory.Hardware')
        return {
            MemberId = index,
            Name = driveObj.Name,
            Model = get_valid_string_property(driveObj.Model),
            PartNumber = get_valid_string_property(hardwareObj.PartNumber),
            PhysicalContext = driveObj.ContainerPhysicalContext,
            Producer = get_valid_string_property(driveObj.Manufacturer),
            ProductionDate = get_valid_string_property(hardwareObj.ManufactureDate),
            SerialNumber = get_valid_string_property(driveObj.SerialNumber)
        }
    end,
    ['NetworkAdapters'] = function (path, index)
        local hardwareObj = mdb.get_object(bus, path, 'bmc.kepler.Inventory.Hardware')
        local networkAdapterObj = mdb.get_object(bus, path, 'bmc.kepler.Systems.NetworkAdapter')
        return {
            MemberId = index,
            Name = hardwareObj.AssetName,
            Model = get_valid_string_property(networkAdapterObj.Model),
            PartNumber = get_valid_string_property(hardwareObj.PartNumber),
            PhysicalContext = 'NetworkingDevice',
            Producer = get_valid_string_property(networkAdapterObj.Manufacturer),
            ProductionDate = get_valid_string_property(hardwareObj.ManufacturingDate),
            SerialNumber = get_valid_string_property(hardwareObj.SerialNumber)
        }
    end,
    ['Storage/Controllers'] = function (path, index)
        local hardwareObj = mdb.get_object(bus, path, 'bmc.kepler.Inventory.Hardware')
        local controllerChipObj = mdb.get_object(bus, path, 'bmc.kepler.Systems.Storage.Controller.Chip')
        return {
            MemberId = index,
            Name = hardwareObj.AssetName,
            Model = get_valid_string_property(controllerChipObj.Model),
            PartNumber = get_valid_string_property(hardwareObj.PartNumber),
            PhysicalContext = 'StorageDevice',
            Producer = get_valid_string_property(hardwareObj.Manufacturer),
            ProductionDate = get_valid_string_property(hardwareObj.ManufacturingDate),
            SerialNumber = get_valid_string_property(hardwareObj.InventorySerialNumber)
        }
    end,
    ['PowerMgmt'] = function (path, index)
        local powerMgmtObj = mdb.get_object(bus, path, 'bmc.kepler.Systems.PowerMgmt.OnePower')
        return {
            MemberId = index,
            Name= 'PSU' .. powerMgmtObj.SlotNumber,
            Model = get_valid_string_property(powerMgmtObj.Model),
            PartNumber = get_valid_string_property(powerMgmtObj.PartNumber),
            PhysicalContext = 'PowerSupply',
            Producer = get_valid_string_property(powerMgmtObj.Manufacturer),
            ProductionDate = get_valid_string_property(powerMgmtObj.ProductionDate),
            SerialNumber = get_valid_string_property(powerMgmtObj.SerialNumber)
        }
    end,
    ['Board'] = function (path, index)
        local boardstr = string.match(path, '/Boards/([^/]+)/')
        local boardObj = mdb.get_object(bus, path, 'bmc.kepler.Systems.Board')
        local fru_path = bus:call('bmc.kepler.maca', '/bmc/kepler/MdbService', 'bmc.kepler.Mdb', 'GetPath',
        'a{ss}ssb', context.new(), "bmc.kepler.Systems.FruData.Overview", cjson.encode({ FruType = FruType[boardstr] }), false)
        local res = {
            MemberId = index,
            Name= boardObj.Name,
            Model = null,
            PartNumber = get_valid_string_property(boardObj.PartNumber),
            PhysicalContext = PhysicalContextType[boardstr],
            Producer = get_valid_string_property(boardObj.Manufacturer),
            ProductionDate = null,
            SerialNumber = null
        }
        if fru_path ~= '' then
            local fruDataObj = mdb.get_object(bus, fru_path, 'bmc.kepler.Systems.FruData.Board')
            res.Model = get_valid_string_property(fruDataObj.BoardProductName)
            res.ProductionDate =  get_valid_string_property(fruDataObj.MfgDate)
            res.SerialNumber = get_valid_string_property(fruDataObj.BoardSerialNumber)
        end
        return res
    end
}

function m.get_assemblies(input)
    local result = {}
    for index, element in ipairs(input) do
        local found_match = false
        for component_path, handler_func in pairs(assembly) do
            if string.find(element, component_path) ~= nil then
                table.insert(result, handler_func(element, index - 1))
                found_match = true
                break  
            end
        end
    end
    return {
        Assemblies = result,
        Count = #result
    }
end

function m.get_assemblies_with_flatten(input)
    local flattened_input = flatten_arrays(input)
    return m.get_assemblies(flattened_input)
end

return m