-- 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 class = require 'mc.class'
local c_service = require 'power_mgmt.service'
local psu_service = require 'psu_service'
local power_upgrade = require 'power_upgrade'
local log = require 'mc.logging'
local cpower_mgmt = class(c_service)
local initiator = require 'mc.initiator'
local object_manage = require 'mc.mdb.object_manage'
local c_object_manage = require 'mc.orm.object_manage'
local signal = require 'signal'
local reboot_manage = require 'mc.mdb.micro_component.reboot'
local log_service = require 'log_service'
local system_power = require 'system_power'
local power_ipmi = require 'power_ipmi'
local metric_collect = require 'metric_collect'
local mc_admin = require 'mc.mc_admin'
local power_mgmt_bus = require 'power_mgmt_bus'
local utils_core = require 'utils.core'
local external_interface = require 'external_interface.handler'
local intf_debug = require'mc.mdb.micro_component.debug'
local mdb_config_manage = require 'mc.mdb.micro_component.config_manage'
local import_export_eng = require 'export_import_engine'
local skynet = require 'skynet'
local utils = require 'power_mgmt_utils'
local c_psu_object = require 'device.psu'
local client = require 'power_mgmt.client'
local starter_ok, starter = pcall(require, 'mc.mem_collect.starter')

local E_OK <const> = nil -- 函数执行成功返回nil
local E_FAILED <const> = '' -- 空错误信息

function cpower_mgmt:register_rpc_methods()
    self:ImplOnePowerOnePowerSetPowerWorkMode(function(obj, ctx, ...)
        return self:set_power_work_mode(ctx, obj, ...)
    end)
    self:ImplOnePowerStatusSetSleepMode(function(obj, ctx, ...)
        return self:set_power_sleep_mode(ctx, obj, ...)
    end)
    self:ImplOnePowerMetricGetData(function(obj, ctx, ...)
        return self.metric_collect:get_psu_data_collection_data(obj, ...)
    end)
    self:ImplOnePowerMetricGetItems(function(obj, ctx, ...)
        return self.metric_collect:get_psu_data_collection_items(obj, ...)
    end)
    self:ImplOnePowerBlackBoxDumpPsuBlackbox(function (obj, ctx, ...)
        local psu_obj = c_psu_object.collection:find(function(object)
            return object.path == obj.path
        end)
        return self.log_service:dump_psu_blackbox(psu_obj.ps_id)
    end)
    self:ImplPowerSuppliesPowerSuppliesSetPsusFanMinPWM(function (obj, ctx, ...)
        return self.psu_service:set_psus_fan_min_pwm(...)
    end)
end

function cpower_mgmt:on_add_object(class_name, object, position)
    (({
        ['OnePower'] = function ()
            self.psu_service:add_psu_object(class_name, object)
        end,
        ['PsuSlot'] = function ()
            self.psu_service:add_psu_object(class_name, object)
        end,
        ['DftPowerSupply'] = function ()
            object.Slot = tonumber(string.sub(position, -2)) -- 截取position后两位做为slot
        end,
        ['DftCanChannel'] = function ()
        end
    })[class_name] or function() end)()
end

function cpower_mgmt:ctor()
    utils.new()
    -- 类初始化
    self.system_power = system_power.new(self.local_db)
    self.psu_service = psu_service.get_instance(self.bus)
    self.log_service = log_service.new(self.bus, self.db)
    self.metric_collect = metric_collect.new(self.bus)
    self.power_mgmt_bus = power_mgmt_bus.new(self.bus)
    self.object_manage = c_object_manage.new(self.db, self.bus, true)
    self.power_upgrade = power_upgrade.new()
    self.external_interface = external_interface.get_instance(self.bus)
end

function cpower_mgmt:check_dependencies()
    local admin = mc_admin.new()
    admin:parse_dependency(utils_core.getcwd() .. '/mds/service.json')
    admin:check_dependency(self.bus)
end

function cpower_mgmt:init()
    log:debug('===== power service init =======')
    cpower_mgmt.super.init(self)
    self:check_dependencies()
    signal.init()
    self.psu_service:init()
    self.log_service:init()
    self:register_reboot_methods()
    self:register_rpc_methods()
    self.object_manage:start()
    power_ipmi.register(function(ipmi_req, local_func)
        self:register_ipmi_cmd(ipmi_req, local_func)
    end)
    -- 注册对象响应回调函数
    -- 添加对象回调
    object_manage.on_add_object(self.bus, function(class_name, object, position)
        self:on_add_object(class_name, object, position)
    end)
    -- 删除对象回调
    object_manage.on_delete_object(self.bus, function(class_name, object, position)
    end)
    -- 添加对象完成回调
    object_manage.on_add_object_complete(self.bus, function(position)
        log:notice('[power_mgmt] add object complete, position: %s', position)
    end)
    -- 删除对象完成回调
    object_manage.on_delete_object_complete(self.bus, function(position)
    end)
    intf_debug.on_dump(function (...)
        self.log_service:dump_log(...)
    end)

    -- 导入导出注册
    mdb_config_manage.on_import(function(ctx, config_data)
        import_export_eng.import(ctx, config_data)
    end)
    mdb_config_manage.on_export(function(ctx)
        return import_export_eng.export(ctx)
    end)

    if starter_ok then
        -- 拉起malloc_info采集工具，每个进程只能一个组件拉起一次
        local starter_obj = starter.new(client, client.GetPerformanceObjects, client.OnPerformancePropertiesChanged)
        pcall(starter_obj.start, starter_obj, 'energy')
    end

    -- 创建PowerSupplies资源树,当前用于psu风扇转速下发
    self:CreatePowerSupplies(1)
    self:main()
end

function cpower_mgmt:main()
    skynet.fork(
        function ()
            -- 如果组件异常重启，启动后直接置位未初始化完成
            if self:get_bmc_reset_type() == 3 then
                self.external_interface:set_init_ok_flag(true)
                return
            end
            -- 启动120s后，cpu开始可以常驻线程访问
            skynet.sleep(12000)
            self.external_interface:set_init_ok_flag(true)
            log:notice("[power_mgmt] init OK")
        end
    )
end

-- 获取组件重启的原因
function  cpower_mgmt:get_bmc_reset_type()
    return 0
end

function cpower_mgmt:on_reboot_prepare()
    log:notice("[power_mgmt] reboot prepare")
end

function cpower_mgmt:on_reboot_cancel()
    log:notice("[power_mgmt] reboot cancel")
end

function cpower_mgmt:on_reboot_action()
    log:notice("[power_mgmt] reboot action")
end

function cpower_mgmt:register_reboot_methods()
    -- 注册平滑重启回调函数
    -- Prepare准备重启回调
    reboot_manage.on_prepare(function()
        self:on_reboot_prepare()
        return 0
    end)
    -- Action执行重启回调
    reboot_manage.on_action(function()
        self:on_reboot_action()
        return 0
    end)
    -- Cancel取消重启回调
    reboot_manage.on_cancel(function()
        self:on_reboot_cancel()
    end)
end

local function log_operation(ctx, fmt, ...)
    local intf = 'N/A'
    local user_name = 'N/A'
    local client_addr = 'N/A'
    if ctx then
        intf = ctx.Interface and ctx.Interface or 'N/A'
        user_name = ctx.UserName and ctx.UserName or 'N/A'
        client_addr = ctx.ClientAddr and ctx.ClientAddr or 'N/A'
    end

    log:operation(initiator.new(intf, user_name, client_addr), 'power_mgmt', fmt, ...)
end

function cpower_mgmt:set_power_work_mode(ctx, obj, work_mode)
    local psu_obj = c_psu_object.collection:find(function(object)
        return object.path == obj.path
    end)
    log:maintenance(log.MLOG_INFO, log.FC__PUBLIC_OK, 'set ps%d mode %s',
        psu_obj.ps_id, work_mode == 0 and 'active' or 'standby')
    if self.psu_service:set_power_work_mode(psu_obj.ps_id, work_mode) == E_OK then
        log:maintenance(log.MLOG_INFO, log.FC__PUBLIC_OK, 'set ps%d mode %s success',
        psu_obj.ps_id, work_mode == 0 and 'active' or 'standby')
        return 0 -- 成功，返回0
    end
    log:maintenance(log.MLOG_INFO, log.FC__PUBLIC_OK, 'set ps%d mode %s failed',
        psu_obj.ps_id, work_mode == 0 and 'active' or 'standby')
    return 1 -- 失败，返回1
end

function cpower_mgmt:set_power_sleep_mode(ctx, obj, sleep_mode)
    local psu_obj = c_psu_object.collection:find(function(object)
        return object.path == obj.path
    end)
    log:maintenance(log.MLOG_INFO, log.FC__PUBLIC_OK, 'set ps%d sleep mode %s', psu_obj.ps_id, sleep_mode)

    if self.psu_service:set_power_sleep_mode(psu_obj.ps_id, sleep_mode) == E_OK then
        log:maintenance(log.MLOG_INFO, log.FC__PUBLIC_OK, 'set ps%d sleep mode %s success', psu_obj.ps_id, sleep_mode)
        return 0 -- 成功，返回0
    end
    log:maintenance(log.MLOG_INFO, log.FC__PUBLIC_OK, 'set slot%d sleep mode %s failed', psu_obj.ps_id, sleep_mode)
    return 1 -- 失败，返回1
end

return cpower_mgmt
