-- 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 context = require 'mc.context'
local log = require 'mc.logging'
local custom_msg = require 'messages.custom'
local mdb_service = require 'mc.mdb.mdb_service'
local OBJECT_PROPERTIES_INTERFACE <const> = 'bmc.kepler.Object.Properties'
--FruCtrl 所在服务
local FRUCTRL_MDB_SERVICE <const> = 'bmc.kepler.fructrl'
-- System FruCtrl 资源树接口
local SYSTEM_FRUCTRL_MDB_INTF <const> = 'bmc.kepler.Systems.FruCtrl'
-- Chassis FruCtrl 资源树接口
local CHASSIS_FRUCTRL_MDB_INTF <const> = 'bmc.kepler.Chassis.FruCtrl'
-- PowerAction 资源树接口
local POWERACTION_MDB_INTF <const> = 'bmc.kepler.Systems.PowerAction'
local FruStateEnums = {
    ['OFF'] = 'Off',
    ['ONING'] = 'Off',
    ['ON'] = 'On',
    ['OFFING'] = 'On'
}

local PowerCtrlType = {
    [0] = 'GracefulShutdown',
    [1] = 'On',
    [2] = 'ForceOff'
}

local FruCtrlType = {
    [0] = 'ForceRestart',
    [2] = 'ForcePowerCycle'
}

local RestartCause <const> = "ChassisControlCommand"

local m = {}

-- 返回值为数字类型
local function get_sysid(path)
    local sysid = -1
    local err, rsp = bus:pcall(FRUCTRL_MDB_SERVICE, path, OBJECT_PROPERTIES_INTERFACE, 'GetAllWithContext', 'a{ss}s',
        context.get_context() or context.new(), OBJECT_PROPERTIES_INTERFACE)
    if not err then
        local identifier = rsp.ObjectIdentifier and rsp.ObjectIdentifier:value()
        if identifier then
            sysid = identifier[1]
        end
    end
    return sysid
end

local function get_systemfructrllist(input)
    local frulist
    if #input ~= 0 then
        -- input[1]代表 -s 传的参数
        frulist = mdb.get_sub_objects(bus, '/bmc/kepler/Systems/'.. input[1] ..'/FruCtrl', SYSTEM_FRUCTRL_MDB_INTF)
    else
        frulist = mdb.get_sub_objects(bus, '/bmc/kepler/Systems', SYSTEM_FRUCTRL_MDB_INTF, 3)
    end
    return frulist
end

local function get_chassisfructrl()
    return mdb.get_object(bus, '/bmc/kepler/Chassis/1/FruCtrl', CHASSIS_FRUCTRL_MDB_INTF)
end

local function get_all_system_id()
    -- 装备测试L1板环境上当前路径下没有深度为1的子路径，所以直接找一个可以覆盖的路径深度，深度为20
    local ok, rsp = pcall(mdb_service.get_sub_paths, bus, '/bmc/kepler/Systems', 20, {})
    if not ok then
        log:error('Invalid path, err(%s)', rsp)
    end

    local pre_suffix_len = string.len('/bmc/kepler/Systems/')
    local result = {}
    local hash = {}
    local sub_str
    local start
    for _, v in pairs(rsp.SubPaths) do
        start = string.find(v, '/', pre_suffix_len + 1)
        if start == nil then
            sub_str = v:sub(pre_suffix_len + 1)
        elseif start > pre_suffix_len + 1 then
            sub_str = v:sub(pre_suffix_len + 1, start - 1)
        end
        if sub_str ~= nil and sub_str ~= '' and hash[sub_str] == nil then
            table.insert(result, sub_str)
            hash[sub_str] = true
        end
    end

    return next(result) and result or {'1'}
end

local function powercontrol(systemidlist, ctrltype)
    local is_multihost = mdb.get_object(bus, '/bmc/kepler/Managers/1/Multihost',
        'bmc.kepler.Managers.Multihost').HostType == 'Multihost'
    local ctx = context.get_context_or_default()
    local objs = {}
    local fruobjs
    if is_multihost and #systemidlist == 0 then
        get_chassisfructrl():PowerCtrl(ctx, ctrltype, RestartCause)
        for _, sysid in pairs(get_all_system_id()) do
            objs[#objs + 1] = {}
            objs[#objs].sysid = tonumber(sysid)
        end
    else
        fruobjs = get_systemfructrllist(systemidlist)
        for _, v in pairs(fruobjs) do
            objs[#objs + 1] = {}
            objs[#objs].sysid = get_sysid(v.path)
            v:FruPowerCtrl(ctx, ctrltype, RestartCause, 0)
        end
    end

    table.sort(objs, function(a, b)
        return a.sysid < b.sysid
    end)
    return objs
end

function m.get_hosttype()
    return mdb.get_object(bus, '/bmc/kepler/Managers/1/Multihost',
        'bmc.kepler.Managers.Multihost').HostType
end

function m.get_power_state_hotswap(input)
    local objs = {}
    local frulist = get_systemfructrllist(input)
    for _, v in pairs(frulist) do
        objs[#objs + 1] = {}
        objs[#objs].sysid = get_sysid(v.path)
        objs[#objs].PowerState = FruStateEnums[v.PowerState]
        objs[#objs].HotswapState = v.HotswapState
    end
    table.sort(objs, function(a, b)
        return a.sysid < b.sysid
    end)
    return objs
end

function m.get_shutdowntimeenabled(input)
    local objs = {}
    local frulist = get_systemfructrllist(input)
    for _, v in pairs(frulist) do
        objs[#objs + 1] = {}
        objs[#objs].sysid = get_sysid(v.path)
        objs[#objs].TimeoutEnabled = v.GracefulShutdownTimeoutEnabled
        objs[#objs].TimeoutSeconds = v.GracefulShutdownTimeoutSeconds
    end
    table.sort(objs, function(a, b)
        return a.sysid < b.sysid
    end)
    return objs
end

function m.get_power_onlock(input)
    local frulist
    local fruobj
    local objs = {}
    if #input ~= 0 then
        local sysid = input[1]
        -- input[1]代表 -s 传的参数
        fruobj = mdb.get_object(bus, '/bmc/kepler/Systems/'.. sysid ..'/PowerAction', POWERACTION_MDB_INTF)
        objs[#objs + 1] = {}
        objs[#objs].sysid = tonumber(sysid)
        objs[#objs].PowerOnTimeoutFlag = fruobj.PowerOnTimeoutFlag
    else
        frulist= mdb.get_sub_objects(bus, '/bmc/kepler/Systems', POWERACTION_MDB_INTF, 2)
        for _, v in pairs(frulist) do
            objs[#objs + 1] = {}
            objs[#objs].sysid = get_sysid(v.path)
            objs[#objs].PowerOnTimeoutFlag = v.PowerOnTimeoutFlag
        end
        table.sort(objs, function(a, b)
            return a.sysid < b.sysid
        end)
    end
    return objs
end

function m.set_powerstate(systemidlist, option)
    return powercontrol(systemidlist, PowerCtrlType[option])
end

function m.set_frucontrol(systemidlist, option)
    return powercontrol(systemidlist, FruCtrlType[option])
end

function m.set_shutdowntimeout(systemidlist, time)
    local objs = {}
    local frulist = get_systemfructrllist(systemidlist)
    for _, v in pairs(frulist) do
        objs[#objs + 1] = {}
        objs[#objs].sysid = get_sysid(v.path)
        if time == 0 then
            v.GracefulShutdownTimeoutEnabled = 0
        else
            v.GracefulShutdownTimeoutEnabled = 1
            v.GracefulShutdownTimeoutSeconds = time
        end
    end
    table.sort(objs, function(a, b)
        return a.sysid < b.sysid
    end)
    return objs
end

return m
