-- 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: 管理事件以及持久化数据
local singleton = require 'mc.singleton'
local class = require 'mc.class'
local log = require 'mc.logging'
local json = require 'cjson'
local ctx = require 'mc.context'
local client = require 'network_adapter.client'

local event_mgmt = class()

local DB_PERID = 'alarm_msgs'
local TABLE_NAME = 't_network_adapter_reset'

-- 动态参数用作唯一标识: args = false
local alarm_states = {}

function event_mgmt:init(reset_db)
    self.reset_db = reset_db
    -- 初始化阶段同步告警信息
    local res = self:query_alarm_msgs()
    if res then
        for _, msg in pairs(res) do
            alarm_states[msg] = true
        end
    end
end

function event_mgmt:add_event(params)
    local event_obj
    client:ForeachEventsObjects(function(o)
        event_obj = o -- 此对象唯一
    end)
    if not event_obj then
        log:error('get events object failed')
        return
    end

    local ok, res = pcall(function ()
        return event_obj:AddEvent_PACKED(ctx.new(), params):unpack()
    end)
    if not ok then
        log:error('add events failed, %s', res)
        return false
    end

    log:notice('add event successfully, record id [%s]', res)
    return true
end

-- 链路错包告警
function event_mgmt:check_oam_err_pkt_cnt_alarm(cnt, device_name, port_id)
    local args = json.encode({device_name, '', 'Port ' .. (port_id + 1)})
    local assert = cnt > 0
    if not assert == not alarm_states[args] then
        return
    end

    local params = {
        {'ComponentName', 'Port' .. (port_id + 1)}, -- port资源树id从0开始,部件id从1开始
        {'State', assert and 'true' or 'false'},
        {'EventKeyId', 'Port.PortOAMErrorPackage'},
        {'MessageArgs', args},
        {'SystemId', ''},
        {'ManagerId', ''},
        {'ChassisId', ''},
        {'NodeId', ''}
    }
    local is_ok = self:add_event(params)
    if not is_ok then
        return false
    end
    -- 更新本地告警信息
    alarm_states[args] = assert
    self:update_alarm_msg(assert, args, '')
    return true
end

-- 链路异常告警
function event_mgmt:check_oam_lost_link_state_alarm(state, device_name, port_id)
    local args = json.encode({device_name, '', 'Port ' .. (port_id + 1)})
    local assert = state == 1 -- 0:未告警 1:告警
    local alarm_state = alarm_states[args]
    if not assert == not alarm_state then   -- 默认nil，此处取反
        return
    end

    local params = {
        {'ComponentName', 'Port' .. (port_id + 1)}, -- port资源树id从0开始,部件id从1开始
        {'State', assert and 'true' or 'false'},
        {'EventKeyId', 'Port.PortOAMLostLink'},
        {'MessageArgs', args},
        {'SystemId', ''},
        {'ManagerId', ''},
        {'ChassisId', ''},
        {'NodeId', ''}
    }
    local is_ok = self:add_event(params)
    if not is_ok then
        return false
    end
    -- 更新本地告警信息
    alarm_states[args] = assert
    self:update_alarm_msg(assert, args, '') -- 动态参数具备唯一性所以可以直接做key，不需要value值
    return true
end


-- 存储告警信息
function event_mgmt:update_alarm_msg(assert, key, value)
    local t = self.reset_db.tables[TABLE_NAME]
    local record = t({PerId = DB_PERID, Key = key})
    if not assert then
        record:delete()
        return
    end
    if next(record.__datas) then -- 脏数据更新
        record.Value = value
    else
        record = t({PerId = DB_PERID, Key = key, Value = value})
    end
    record:save()
end

-- 查询所有告警信息
function event_mgmt:query_alarm_msgs()
    local t = self.reset_db.tables[TABLE_NAME]
    local records = self.reset_db:select(t):where(t.PerId:eq(DB_PERID)):all()
    if not records or #records == 0 then
        return nil
    end
    local res = {}
    for _, value in pairs(records) do
        table.insert(res, value.__datas.Key)
    end
    return res
end

return singleton(event_mgmt)