-- 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 COMPONENT_TYPE_FAN<const> = 4

local m = {}

local function get_fans(list)
    local res = {}
    for _, path in pairs(list) do
        local ok, fan_obj = pcall(mdb.get_object, bus, path, 'bmc.kepler.Systems.Fan')
        if not ok then
            goto continue
        end
        res[fan_obj.FanId] = fan_obj

        ::continue::
    end
    return res
end

local function get_pumps(list)
    local res = {}
    for _, path in pairs(list) do
        local ok, pump_obj = pcall(mdb.get_object, bus, path, 'bmc.kepler.Systems.Pump')
        if not ok then
            goto continue
        end
        res[pump_obj.Id] = pump_obj

        ::continue::
    end
    return res
end

function m.get_fan_state(fan_info)
    if fan_info.IsTwins then
        if fan_info.RearPresence == 0 and fan_info.FrontPresence == 0 then
            return 2
        elseif fan_info.FrontSpeed == 0 and fan_info.RearSpeed == 0 then
            return 1
        else
            return 0
        end
    else
        if fan_info.RearPresence == 0 then
            return 2
        elseif fan_info.RearSpeed == 0 then
            return 1
        else
            return 0
        end
    end
end

function m.get_pump_state(pump_info)
    if pump_info.Presence == 0 then
        return 2
    elseif pump_info.SpeedRPM == 0 then
        return 1
    else
        return 0
    end
end

local state = {
    [0] = 'Enabled',
    [1] = 'StandbyOffline',
    [2] = 'Absent'
}

function m.get_state_by_code(state_code)
    if not state[state_code] then
        return cjson.null
    end

    return state[state_code]
end

local health = {
    [0] = 'OK',
    [1] = 'Warning',
    [2] = 'Warning',
    [3] = 'Critical'
}

function m.get_health_by_code(health_code)
    if not health[health_code] then
        return cjson.null
    end

    return health[health_code]
end

local function contains_value(fan_slots, target)
    for _, value in ipairs(fan_slots) do
        if value == target then
            return true
        end
    end
    return false
end

function m.get_fan_status(components, fan_slots, fan_list)
    local fans = get_fans(fan_list)
    local state_code = 0
    local health = 0

    for _, obj in pairs(fans) do
        if contains_value(fan_slots, obj.FanId) then
            local fan_state = m.get_fan_state(obj)
            if fan_state > state_code then
                state_code = fan_state
            end

            for _, item in pairs(components) do
                -- 查看对应风扇组件的健康状态
                if item.Type == COMPONENT_TYPE_FAN and item.Presence == 1 and item.Health > health and
                    item.Name == "Fan" .. obj.FanId then
                    health = item.Health
                end
            end
        end
    end

    local state = m.get_state_by_code(state_code)

    local status_obj = cjson.json_object_new_object()
    status_obj["State"] = state
    status_obj["Health"] = state == "Absent" and cjson.null or m.get_health_by_code(health)

    return status_obj
end

function m.get_status(components, pump_list, fan_list)
    local fans = get_fans(fan_list)
    local pumps = get_pumps(pump_list)
    local state_code = 0
    local health = 0

    for _, obj in pairs(fans) do
        local fan_state = m.get_fan_state(obj)
        if fan_state > state_code then
            state_code = fan_state
        end
    end

    for _, item in pairs(components) do
        -- 查看对应风扇组件的健康状态
        if item.Type == COMPONENT_TYPE_FAN and item.Presence == 1 and item.Health > health then
            health = item.Health
        end
    end

    for _, obj in pairs(pumps) do
        local pump_state = m.get_pump_state(obj)
        if pump_state > state_code then
            state_code = pump_state
        end
    end

    local state = m.get_state_by_code(state_code)

    local status_obj = cjson.json_object_new_object()
    status_obj["State"] = state
    status_obj["Health"] = state == "Absent" and cjson.null or m.get_health_by_code(health)

    return status_obj
end

function m.get_redundancy_groups(input)
    local res = cjson.json_object_new_array()
    for _, fan_group_path in ipairs(input[1]) do
        -- 获取风扇组信息
        local ok, fan_group = pcall(mdb.get_object, bus, fan_group_path, 'bmc.kepler.Chassis.FanGroup')
        if not ok then
            goto continue
        end
        local redundancy_group = cjson.json_object_new_object()
        redundancy_group.RedundancyType = "NotRedundant"
        redundancy_group.MaxSupportedInGroup = #fan_group.FanSlots
        redundancy_group.MinNeededInGroup = #fan_group.FanSlots
        table.sort(fan_group.FanSlots, function(a, b) return tonumber(a) < tonumber(b) end)
        local group_info = cjson.json_object_new_array()
        for _, fan_slot in ipairs(fan_group.FanSlots) do
            local odata_obj = cjson.json_object_new_object()
            local uri = string.format('/redfish/v1/Chassis/%s/ThermalSubsystem/Fans/%s', input[2], fan_slot)
            odata_obj['@odata.id'] = uri
            group_info[#group_info + 1] = odata_obj
        end
        redundancy_group.RedundancyGroup = group_info
        redundancy_group.Status = m.get_fan_status(input[3], fan_group.FanSlots, input[4])
        res[#res+1] = redundancy_group
        ::continue::
    end
    return res
end

return m
