-- Copyright (c) 2026 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 log = require 'mc.logging'
local def = require 'biz_topo.def'
local client = require 'pcie_device.client'

local c_ub_rule_cable_monitor_plugin = class()

function c_ub_rule_cable_monitor_plugin:ctor(biz_connector_list)
    self.biz_connector_list = biz_connector_list
    self.sub_down_connector = {} -- 子板上的下行连接器
    self.ub_unnecessary_cable = {} -- 保存因为有UB卡可以不插的线缆
    self.has_detect = false
end

function c_ub_rule_cable_monitor_plugin:init_detect_rule()
    -- 获取所有的IEU下行连接器
    for _, c in ipairs(self.biz_connector_list) do
        if c:is_pcie_biz_connector() then
            table.insert(self.sub_down_connector, c)
        end
    end
end

-- 判断一个slot对应的上行槽位是不是必插
function c_ub_rule_cable_monitor_plugin:get_unnecessary_cable_info(slot)
    local pcie_addr_info

    for _, down_connector in pairs(self.sub_down_connector) do
        pcie_addr_info = down_connector.ref_pcie_addr_info
        if not pcie_addr_info then
            goto continue
        end

        if pcie_addr_info:get_prop('ComponentType') ~= def.com_type.PCIE_CARD or
            pcie_addr_info:get_prop('SlotID') ~= slot then
            goto continue
        end

        -- 找到了对应的下行连接器
        -- 用拓扑建立中已经匹配的上行连接器进行查找
        for _, id in pairs(down_connector.res_port_ids) do
            self.ub_unnecessary_cable[#self.ub_unnecessary_cable + 1] = {
                container_unit_type = pcie_addr_info:get_prop('ContainerUnitType'),
                container_slot = pcie_addr_info:get_prop('ContainerSlot'),
                uid = pcie_addr_info:get_prop('ContainerUID'),
                port_id = id
            }
            log:notice("[UBRuleCableMonitorPlugin] %s%s uid %s port %s has UB Card, don't need UBCDD cable.", 
                self.ub_unnecessary_cable[#self.ub_unnecessary_cable].container_unit_type,
                self.ub_unnecessary_cable[#self.ub_unnecessary_cable].container_slot,
                self.ub_unnecessary_cable[#self.ub_unnecessary_cable].uid,
                self.ub_unnecessary_cable[#self.ub_unnecessary_cable].port_id)
        end
        ::continue::
    end
end

-- 当检测到UB卡的时候，对应槽位的UBCDD线缆可以不插
function c_ub_rule_cable_monitor_plugin:ub_rules_get_unnecessary_cable()
    -- 只执行一次
    if self.has_detect then
        return
    end
    self.has_detect = true

    self:init_detect_rule()

    local ubcard_objs = client:GetSystemsBoardObjects()
    if not ubcard_objs then
        return
    end
    for _, ub_card in pairs(ubcard_objs) do
        self:get_unnecessary_cable_info(ub_card.Slot)
    end
end

-- 判断是否符合UB例外情况
function c_ub_rule_cable_monitor_plugin:is_match_ub_rule(config, slot_type, slot_number)
    for _, ub_unnecessary_cable in pairs(self.ub_unnecessary_cable) do
        if config.uid == ub_unnecessary_cable.uid and
            slot_type == ub_unnecessary_cable.container_unit_type and
            slot_number == ub_unnecessary_cable.container_slot then
            config.connected_target_ports_id[ub_unnecessary_cable.port_id] = true
        end
    end
    local match_times = 0
    for _, target_port_id in pairs(config.target_ports_id) do
        if config.connected_target_ports_id[target_port_id] then
            match_times = match_times + 1
        end
    end
    if match_times == #config.target_ports_id then
        return true
    end
    return false
end

-- 判断是否符合UB例外情况，精细化告警
function c_ub_rule_cable_monitor_plugin:is_match_ub_rule_refine_alarm(config_uid, slot_type, slot_number, port_id)
    for _, ub_unnecessary_cable in pairs(self.ub_unnecessary_cable) do
        if config_uid == ub_unnecessary_cable.uid and
            slot_type == ub_unnecessary_cable.container_unit_type and
            slot_number == ub_unnecessary_cable.container_slot and 
            port_id == ub_unnecessary_cable.port_id then
            return true
        end
    end
    return false
end

return c_ub_rule_cable_monitor_plugin