-- 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 m = {}

local ETH_OBJ_PATH<const> = '/bmc/kepler/Managers/1/EthernetInterfaces'
local ETH_PORT_INTERFACE<const> = 'bmc.kepler.Managers.EthernetInterfaces.MgmtPort'
local ETH_GROUP_INTERFACE<const> = 'bmc.kepler.Managers.EthernetInterfaces.EthGroup'

-- 0-专用/1-LOM/2-pcie,目前只支持专用网口、pcie
local port_type_table = {
    Dedicated = 0,
    Lom = 1,
    ExternalPCIe = 2,
    Ocp = 5,
    Ocp2 = 6
}

function m.check_active_port_param_valid(port_type, port_num, port_paths)
    if not port_type and not port_num then
        return false
    end
    if port_type ~= 0 and not port_num then
        return false
    end

    local type_list = {}
    for _, path in pairs(port_paths) do
        local obj = mdb.get_object(bus, path, 'bmc.kepler.Managers.EthernetInterfaces.MgmtPort')
        if port_type_table[obj.Type] then
            table.insert(type_list, port_type_table[obj.Type])
        end
    end
    for _, value in pairs(type_list) do
        if port_type == value then
            return true
        end
    end
    return false
end

function m.get_active_port_type_list(port_paths)
    local port_type_list = {}
    for _, path in pairs(port_paths) do
        local obj = mdb.get_object(bus, path, 'bmc.kepler.Managers.EthernetInterfaces.MgmtPort')
        local type_num = port_type_table[obj.Type]
        if type_num then
            port_type_list[tostring(type_num)] = true
        end
    end
    return port_type_list
end

function m.check_port_exist(port_type, port_num, port_paths)
    -- 如果是专用网口，并且没有配置prot_num，直接返回true，Id返回1
    if port_type == 0 and not port_num then
        return {true, 1}
    end
    local port_type_str = ''
    for k, v in pairs(port_type_table) do
        if port_type == v then
            port_type_str = k
        end
    end
    if port_type_str == '' then
        return {false}
    end
    for _, path in pairs(port_paths) do
        local obj = mdb.get_object(bus, path, 'bmc.kepler.Managers.EthernetInterfaces.MgmtPort')
        if obj.Type == port_type_str and obj.DevicePortId == port_num then
            return {true, obj.Id}
        end
    end
    return {false}
end

function m.get_backup_state(ip_mode, ip, mask, backup_ip, backup_mask)
    local backup_state = 'Deactivated'
    local core = require 'network.core'
    if ip_mode == 'DHCP' then
        if ip == '' then
            backup_state = 'Activated'
            return backup_state
        else
            -- 判断是否同网段
            local dhcp_ip_num = core.inet_aton(ip)
            local dhcp_mask_num = core.inet_aton(mask)
            local backup_ip_num = core.inet_aton(backup_ip)
            local backup_mask_num = core.inet_aton(backup_mask)
            -- 如果同网段，则备份ip不生效，不同网段，备份ip生效
            if (dhcp_ip_num & dhcp_mask_num) ~= (backup_ip_num & backup_mask_num) then
                backup_state = 'Activated'
                return backup_state
            end
        end
        backup_state = 'Deactivated'
    end
    return backup_state
end

local function is_include(value, table)
    for _, v in pairs(table) do
        if v == value then
            return true
        end
    end
    return false
end

function m.get_all_eth(eth_name, port_paths)
    local eth_list = {}
    local eth_list_str = ''
    local eth_exist = false
    local input_eth_id = string.sub(eth_name, 4)
    for _, path in pairs(port_paths) do
        local obj = mdb.get_object(bus, path, 'bmc.kepler.Managers.EthernetInterfaces.MgmtPort')
        if not is_include(obj.EthId, eth_list) then
            table.insert(eth_list, obj.EthId)
            eth_list_str = eth_list_str .. tostring(obj.EthId) .. '|'
        end
        if tostring(obj.EthId) == input_eth_id then
            eth_exist = true
        end
    end
    -- 去掉最后一个'|'
    eth_list_str = string.sub(eth_list_str, 1, -2)
    -- 将得到的所有eth_id拼接成 eth[x|x|x] 的字符串返回给cli回显
    eth_list_str = string.format('eth[%s]', eth_list_str)
    return {eth_list_str, eth_exist}
end

function m.get_eth_name_valid(eth_name)
    local ports = mdb.get_sub_objects(bus, '/bmc/kepler/Managers/1/EthernetInterfaces',
        'bmc.kepler.Managers.EthernetInterfaces.MgmtPort')
    for _, port in pairs(ports) do
        if tostring(port.EthId) == string.sub(eth_name, 4) then
            return true
        end
    end
    return false
end

local cli_type_table = {
    Dedicated = 'Dedicated',
    Lom = 'LOM',
    ExternalPCIe = 'PCIE',
    Aggregation = 'Aggregation',
    FlexIO = 'FlexIO',
    Ocp = 'OCP',
    Ocp2 = 'OCP2'
}

function m.get_active_port_info(port_id)
    local path = '/bmc/kepler/Managers/1/EthernetInterfaces/' .. port_id
    local ok, obj = pcall(mdb.get_object, bus, path, 'bmc.kepler.Managers.EthernetInterfaces.MgmtPort')
    if not ok then
        return {Type = 'Unknown', DevicePortId = 'Unknown'}
    end
    return {Type = cli_type_table[obj.Type], DevicePortId = obj.DevicePortId}
end

function m.get_maint_ip_info(group_paths)
    for _, group in pairs(group_paths) do
        local ok, obj = pcall(mdb.get_object, bus, group, 'bmc.kepler.Managers.EthernetInterfaces.EthGroup')
        if ok and obj.OutType == 8 then -- 8表示维护EthGroup
            return {IpAddr = obj.IpAddr, SubnetMask = obj.SubnetMask}
        end
    end
    return {IpAddr = 'nil', SubnetMask = 'nil'}
end

local function get_vlan_type(port_id, ports)
    for _, value in ipairs(ports) do
        if port_id == value.Id then
            if value.Type == "Dedicated" then
                return "DedicatedVLAN"
            else
                return "NcsiVLAN"
            end
        end
    end
end

local function get_ethernet_group(ethGroup_obj, ports)
    local active_port_id = ethGroup_obj.ActivePortId
    local port_path = ETH_OBJ_PATH .. "/" .. active_port_id
    local ok, port_obj = pcall(mdb.get_object, bus, port_path, ETH_PORT_INTERFACE)
    if not ok then
       return
    end
    local vlan_type = get_vlan_type(active_port_id, ports)
    local ethernet_group = {
        GroupId = ethGroup_obj.GroupId,
        ActiveEthId = ethGroup_obj.ActiveEthId,
        Channel = ethGroup_obj.Channel,
        NetMode = ethGroup_obj.NetMode,
        ActivePort = {
            Type = cli_type_table[port_obj.Type],
            DevicePortId = port_obj.DevicePortId
        },

        IpVersion = ethGroup_obj.IpVersion,
        IpMode = ethGroup_obj.IpMode,
        IpAddr = ethGroup_obj.IpAddr,
        SubnetMask = ethGroup_obj.SubnetMask,
        DefaultGateway = ethGroup_obj.DefaultGateway,
        BackupIpAddr = ethGroup_obj.BackupIpAddr,
        BackupSubnetMask = ethGroup_obj.BackupSubnetMask,
        Mac = ethGroup_obj.Mac,

        Ipv6Mode = ethGroup_obj.Ipv6Mode,
        Ipv6Addr = ethGroup_obj.Ipv6Addr,
        Ipv6PrefixLength = ethGroup_obj.PrefixLength,
        Ipv6DefaultGateway = ethGroup_obj.Ipv6DefaultGateway,

        LinkLocalAddress = ethGroup_obj.LinkLocalAddress,
        SLAACAddressList = ethGroup_obj.SLAACAddressList,

        VLANType = vlan_type,
        VLANEnabledEnabled = ethGroup_obj.VLANEnabled,
        VLANId = ethGroup_obj.VLANId
    }

    return ethernet_group
end

function m.get_ethernet_interface_groups(ports, group_paths)
    local eth_groups = {}

    for _, group_path in ipairs(group_paths) do
        local ok, ethGroup_obj = pcall(mdb.get_object, bus, group_path, ETH_GROUP_INTERFACE)
        if ok and ethGroup_obj.OutType == 2 and ethGroup_obj.Status then
            local ethernet_group = get_ethernet_group(ethGroup_obj, ports)
            table.insert(eth_groups, ethernet_group)
        end

    end
    table.sort(eth_groups, function(a, b)
        return a.GroupId < b.GroupId
    end)

    return eth_groups
end

return m
