-- 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 skynet = require 'skynet'
local log = require 'mc.logging'
local singleton = require 'mc.singleton'
local client = require 'chassis.client'
local led_service = require 'led_service'

local EVENTS_PATH = '/bmc/kepler/Systems/1/Events'
local SENSORS_PATH = '/bmc/kepler/Chassis/1/Sensors'
local EVENT = 'Event'
local SENSOR = 'Sensor'
local HEALTH_STATUS_TO_NUM = {
    Normal = 1,
    Minor = 2,
    Major = 3,
    Critical = 4
}
local HEALTH_NUM_TO_STATUS = {
    [1] = 'Normal',
    [2] = 'Minor',
    [3] = 'Major',
    [4] = 'Critical'
}

local led_policy_service = {}
led_policy_service.__index = led_policy_service

function led_policy_service.new()
    return setmetatable({
        led_policy_obj = nil -- 亮灯策略对象
    }, led_policy_service)
end

local function get_system_health_status(name)
    local ok, obj
    if name == EVENT then
        ok, obj = pcall(function()
            return client:GetEventsObjects()[EVENTS_PATH]
        end)
    else
        ok, obj = pcall(function()
            return client:GetSensorsObjects()[SENSORS_PATH]
        end)
    end

    if not ok or not obj then
        log:error('Get %s objects failed', name)
        return
    end
    return obj.Health
end

local function get_healteh_status(self)
    local event_health = get_system_health_status(EVENT)
    if self.led_policy_obj.SeverityReferenceSource == EVENT then
        return event_health
    end
    local sensor_health = get_system_health_status(SENSOR)
    if self.led_policy_obj.SeverityReferenceSource == SENSOR then
        return sensor_health
    end
    if not event_health or not sensor_health then
        return
    end
    return HEALTH_NUM_TO_STATUS[math.max(HEALTH_STATUS_TO_NUM[event_health], HEALTH_STATUS_TO_NUM[sensor_health])]
end

function led_policy_service:execute_with_prop_change(type, values)
    if self.led_policy_obj.SeverityReferenceSource == type then
        return
    end

    -- 需要监听的属性列表
    local properties = {'Health', 'MinorCount', 'MajorCount', 'CriticalCount'}

    -- 检查是否有监听的属性发生变化
    for _, prop_name in ipairs(properties) do
        if values[prop_name] then
            if prop_name == 'Health' then
                log:notice('get_event_health change to: %s', values[prop_name]:value())
            else
                log:info('%s change to: %s', prop_name, values[prop_name]:value())
            end
            self:execute_once_strategy()
            return
        end
    end
end

function led_policy_service:start_system_health_listenning()
    client:OnEventsPropertiesChanged(function(values)
        self:execute_with_prop_change(SENSOR, values)
    end, { SystemId = 1 })
    client:OnSensorsPropertiesChanged(function(values)
        self:execute_with_prop_change(EVENT, values)
    end, { SystemId = 1 })
    -- 起监听后再执行一次。防止漏监听
    self:execute_once_strategy()
end

function led_policy_service:execute_once_strategy()
    local health_status = get_healteh_status(self)
    if not health_status then
        log:error('get health status failed')
        return
    end

    local led_inst = led_service.get_instance()
    -- 获取预期的 LED 状态和颜色
    local led_policy = led_inst:get_led_policy()
    -- 如果当前状态和预期状态相同，则不设置
    if led_inst.sys_obj:get_led_state() == led_policy[health_status][2] and
        led_inst.sys_obj.uidled_info.Capability == led_policy[health_status][1] then
        return
    end

    led_inst:set_led_by_sys_health(health_status)
end

function led_policy_service:led_health_task()
    led_service.get_instance().sys_obj_ready:on(function()
        self.led_policy_obj.property_changed:on(function(name, value)
            if name == 'SeverityReferenceSource' then
                log:notice('change SeverityReferenceSource to %s', value)
            end
            self:execute_once_strategy()
        end)
        self:start_system_health_listenning()
    end)
end

function led_policy_service:add_object(object)
    self.led_policy_obj = object
    skynet.fork_once(function()
        self:led_health_task()
    end)
end

return singleton(led_policy_service)
