-- 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.

-- Description: 整机power cycle接口

local skynet = require 'skynet'
local class = require 'mc.class'
local context = require 'mc.context'
local log = require 'mc.logging'
local m_enums = require 'types.enums'
local power_ctrl_utils = require 'types.power_ctrl_utils'
local power_ctrl_state_enums = power_ctrl_utils.power_ctrl_state_enums

local chassis_power_cycle = class()

local power_cycle_state = {
    NO_ACTION = 0,
    POWER_OFFING = 1,
    POWER_ONING = 2,
}

function chassis_power_cycle:ctor(chassis_fructrl_obj, fructrl_proxy_ins)
    self.chassis_fructrl_obj = chassis_fructrl_obj
    self.fructrl_proxy_ins = fructrl_proxy_ins
    self.power_ctrl_state = power_ctrl_state_enums.INITED
end

function chassis_power_cycle:init()
    -- 处理未完成的powercycle动作
    local powercycle_type = m_enums.PowerCycleType[self.chassis_fructrl_obj.PowerCycleType]
    local powercycle_state = self.chassis_fructrl_obj.PowerCycleState
    log:notice("[chassis power cycle] powercycle info(type:%s state:%s) in the init phase",
        powercycle_type, powercycle_state)
    if powercycle_state ~= power_cycle_state.NO_ACTION and powercycle_type then
        skynet.fork_once(
            self:power_cycle_action(powercycle_state, context.new("N/A", "N/A", "localhost"), 'Unknown',
                powercycle_type)
        )
    end
end

local function chassis_power_on(self, ctx, cause)
    -- 整机的cycle的上电操作不能被屏蔽
    ctx.Requestor = 'bmc.kepler.Chassis.FruCtrl'
    self.fructrl_proxy_ins:all_host_power_on(ctx, cause)
    self.fructrl_proxy_ins:wait_all_host_power_on_finished()
end

local function chassis_power_off(self, ctx, cause, type)
    self.fructrl_proxy_ins:all_host_power_off(ctx, cause, type)
end

local function wait_power_cycle_interval(self)
    -- 延时PowerCycleInterval s
    local interval_100ms = self.chassis_fructrl_obj.PowerCycleInterval * 10
    while interval_100ms > 0 do
        if self.power_ctrl_state == power_ctrl_state_enums.INTERRUPTED then
            log:notice("[chassis power cycle] power cycle interrupted in power cycle interval")
            return false
        end
        skynet.sleep(10)
        interval_100ms = interval_100ms - 1
    end
    return true
end

-- 整机powercycle的流程为先所有host全部下电
function chassis_power_cycle:power_cycle_action(powercycle_state, ctx, restart_cause, powercycle_type)
    -- 当前有任务，则退出
    if self.power_cycle_task then
        log:notice("duplicate power cycle action")
        return false
    end
    skynet.fork_once(function ()
        self.power_cycle_task = true
        self.power_ctrl_state = power_ctrl_state_enums.PROCESSING

        -- 接着上次powercycle流程继续
        if powercycle_state == power_cycle_state.POWER_OFFING then
            goto POWER_OFF
        elseif powercycle_state == power_cycle_state.POWER_ONING then
            goto POWER_ON
        end

        -- 记录当前power_cycle的类型
        self.chassis_fructrl_obj.PowerCycleType = m_enums.PowerCycleType[powercycle_type]
        log:notice("[chassis power cycle] set chassis power cycle type to %s", self.chassis_fructrl_obj.PowerCycleType)
        :: POWER_OFF ::
        -- 根据类型先调用下电操作
        self.chassis_fructrl_obj.PowerCycleState = power_cycle_state.POWER_OFFING
        chassis_power_off(self, ctx, restart_cause, (powercycle_type == m_enums.PowerCtrlType.ForcePowerCycle and
            m_enums.PowerCtrlType.ForceOff or m_enums.PowerCtrlType.GracefulShutdown))
        if not power_ctrl_utils.wait_chassis_power_off(self, 1200) then -- 下电超时时间20min，与单host保持一致
            self.chassis_fructrl_obj.PowerCycleState = power_cycle_state.NO_ACTION
            self.chassis_fructrl_obj.PowerCycleType = 0
            log:notice("[chassis power cycle] current power cycle type:%s, power cycle state:%s",
                self.chassis_fructrl_obj.PowerCycleType, self.chassis_fructrl_obj.PowerCycleState)
            self.power_ctrl_state = power_ctrl_state_enums.INITED
            self.power_cycle_task = false
            return
        end

        wait_power_cycle_interval(self)

        :: POWER_ON ::
        self.chassis_fructrl_obj.PowerCycleState = power_cycle_state.POWER_ONING
        -- 调用整机上电操作
        chassis_power_on(self, ctx, restart_cause)

        -- 恢复状态
        self.chassis_fructrl_obj.PowerCycleState = power_cycle_state.NO_ACTION
        self.chassis_fructrl_obj.PowerCycleType = 0
        log:notice("[chassis power cycle] current power cycle type:%s, power cycle state:%s",
            self.chassis_fructrl_obj.PowerCycleType, self.chassis_fructrl_obj.PowerCycleState)
        self.power_ctrl_state = power_ctrl_state_enums.INITED
        self.power_cycle_task = false
    end)
    return true
end

function chassis_power_cycle:exit_power_cycle()
    if self.power_ctrl_state == power_ctrl_state_enums.INITED then
        return true
    end

    -- 设置标志为INTERRUPTED，中断当前的power_cycle操作
    self.power_ctrl_state = power_ctrl_state_enums.INTERRUPTED

    while self.power_ctrl_state ~= power_ctrl_state_enums.INITED do
        skynet.sleep(50)
    end

    return true
end

return chassis_power_cycle