-- 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 c_object = require 'mc.orm.object'
local log = require 'mc.logging'
local c_network_port = require 'device.class.network_port'
local c_bonding_port = require 'device.class.network_bonding_port'
local c_object_manage = require 'mc.orm.object_manage'

local c_network_bonding = c_object('NetworkBonding')

function c_network_bonding:ctor()
    self.port_objects = {}
end

function c_network_bonding:init()
    self:next_tick(self.match_network_port, self)
    self.super.init(self)
end

function c_network_bonding:match_network_port()
    local port_obj
    for id, port in ipairs(self.Ports) do
        log:debug("network bonding port name %s", port)
        port_obj = c_network_port.collection:find({Name = port})
        if port_obj then
            self.port_objects[#self.port_objects + 1] = c_bonding_port.insert_or_update({
                TeamId = self.Id,
                Id = id,
                NodeId = port_obj.NodeId,
                Name = port
            })
        end
    end
end

function c_network_bonding:update_network_bonding_port(ports)
    local port_obj
    for id, port in ipairs(ports) do
        port_obj = c_network_port.collection:find({Name = port})
        if port_obj and port_obj.WorkloadType ~= 1 then
            c_bonding_port.insert_or_update({
                TeamId = self.Id,
                Id = id,
                NodeId = port_obj.NodeId,
                Name = port
            })
        end
    end

    local exist_ports = c_bonding_port.get_bonding_port(self.Id)
    for id, port  in ipairs(exist_ports) do
        if not ports[id] then
            port:dtor()
        end
    end
end

function c_network_bonding.update_by_physical_port_name(name, node_id)
    local bond_port_obj = c_bonding_port.collection:find({Name = name})
    if bond_port_obj then
        return
    end
    local port_id = nil
    local bond_obj = c_network_bonding.collection:find(function (obj)
        for id, port_name in ipairs(obj.Ports) do
            if port_name == name then
                port_id = id
                return true
            end
        end
        return false
    end)

    if not bond_obj or not port_id then
        return
    end

    c_bonding_port.insert_or_update({
        TeamId = bond_obj.Id,
        Id = port_id,
        NodeId = node_id,
        Name = name
    })
end

function c_network_bonding.update_ports(value)
    local obj = c_network_bonding.collection:find({Id = value.Id})
    if not obj then
        log:error("Update network bonding port failed.")
        return
    end

    obj:update_network_bonding_port(value.Ports)
end


function c_network_bonding.create_mdb_object(value)
    local app = c_object_manage.get_instance().app
    return app:CreateNetworkBonding(1, value.Id, function (obj)
        log:debug("create network bonding id(%s), name(%s)", value.Id, value.Name)
        obj.Id = value.Id
        obj.Name = value.Name
        obj.Ports = value.Ports
    end)
end

function c_network_bonding.insert_or_update(value)
    local obj = c_network_bonding.collection:find({Id = value.Id, Name = value.Name})
    if obj then
        obj.Id = value.Id
        obj.Name = value.Name
        obj.Ports = value.Ports
    end
    return obj or c_network_bonding.__table(value)
end

function c_network_bonding:destroy()
    local ports = c_bonding_port.get_bonding_port(self.Id)
    for _, port in ipairs(ports) do
        port:dtor()
    end
    self:dtor()
end
return c_network_bonding
