-- 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 chassis_def = require 'def.chassis_def'

local uid_state = chassis_def.UIDLEDSTATE
local uid_interval = chassis_def.UIDLEDINTERVAL

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_set_queue = skynet.queue(), -- 设置LED队列
        remote_login_num = 0
    }, 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()
    -- 设置过程中可能存在协程切换，确保按顺序执行
    self.led_set_queue(function()
        local ok, err = pcall(function()
            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()
            local expected_state = led_policy[health_status][2]
            local expected_on_duration, expected_off_duration = led_inst.sys_obj:get_onoffduration_by_state(expected_state)
            local expected_ctrl_value = ((expected_on_duration // 10) << 4) + (expected_off_duration // 10) & 0xFF

            -- 如果当前状态和预期状态完全相同，则不设置
            if led_inst.sys_obj:get_led_state() == expected_state and
                led_inst.sys_obj.uidled_info.Capability == led_policy[health_status][1] and
                led_inst.sys_obj.uidled_info.OnDuration == expected_on_duration and
                led_inst.sys_obj.uidled_info.OffDuration == expected_off_duration and
                led_inst.sys_obj.uidled_info.CtrlValue == expected_ctrl_value then
                return
            end
        
            led_inst:set_led_by_sys_health(health_status)
        end)

        if not ok then
            log:error('set health status failed, err:%s', err)
        end
    end)   
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

-- 用户登录web,kvm,ssh时uid闪烁,GUI对应web，CLI对应ssh
function led_policy_service:check_session_type(value)
    if value == 'GUI' or value == 'CLI' then
        return true
    end
    if string.find(value, 'KVM') then
        return true
    end
    return false
end
function led_policy_service:xxx_call(sender, path, props)

end
function led_policy_service:register_lister_session_to_flash_uid()
    local c_led_service = led_service.get_instance()
    client:OnSessionInterfacesAdded(function(sender, path, props)
        if self:check_session_type(props['SessionType']:value()) then
            if self.remote_login_num == 0 then
                c_led_service:update_uidled_by_remote_login(uid_state.LED_BLINK)
            end
            self.remote_login_num = self.remote_login_num + 1
        end
    end)
    client:OnSessionInterfacesRemoved(function(sender, path, props)
        local count = 0
        for _, obj in pairs(client:GetSessionObjects()) do
            if self:check_session_type(obj.SessionType) then
                count = count + 1
            end
        end
        if count == 0 and self.remote_login_num ~= 0  then
            c_led_service:update_uidled_by_remote_login(uid_state.LED_OFF)
        end
        self.remote_login_num = count
    end)
end

function led_policy_service:add_object(object)
    self.led_policy_obj = object
    skynet.fork_once(function()
        self:led_health_task()
    end)
    if object.UpdateUidStateByRemoteLogin == true then
        self:register_lister_session_to_flash_uid()
    end
end

return singleton(led_policy_service)
