-- 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 class = require 'mc.class'
local log = require 'mc.logging'
local c_service = require 'network_adapter.service'
local c_device_manager = require 'device.device_manager'
local reboot_manage = require 'mc.mdb.micro_component.reboot'
local ncsi_service = require 'ncsi.ncsi_service'
local intf_debug = require 'mc.mdb.micro_component.debug'
local c_object_manage = require 'mc.orm.object_manage'
local c_tasks = require 'mc.orm.tasks'
local c_bma = require 'bma'
local c_bma_rpc_bond = require 'bma.rpc.bond'
local c_bma_rpc_port = require 'bma.rpc.port'
local ipmi_struct = require 'network_adapter.ipmi.ipmi'
local msg = require 'network_adapter.ipmi.ipmi_message'
local base_messages = require 'messages.base'
local debug = require 'debug.init'
local ipmi = require 'ipmi'
local mc_admin = require 'mc.mc_admin'
local utils_core = require 'utils.core'
local comp_code = ipmi.types.Cc
local event_mgmt = require 'event.event_mgmt'
local dfx_collect = require 'dfx_collect.dfx_collect_mgnt'
local metric_collect = require 'metric.metric_collect'
local optical_module_prbs_test = require 'prbs_test.optical_module_prbs_test'

local app = class(c_service)
local RET_OK <const> = 0
local SYSTEM_ID_DEFAULT <const> = '1'
local MANUFACTURE_ID<const> = 0x0007db

function app:ctor()
    self.ncsi_service = ncsi_service.new(self.db, self.bus)
    self.device_manager = c_device_manager.new(self.db, self.bus)
    self.debug = debug
    self.bma = c_bma.new(self.bus)
    self.bma_rpc_bond = c_bma_rpc_bond.new(self.bus)
    self.bma_rpc_port = c_bma_rpc_port.new(self.bus)
    self.metric_collect = metric_collect.new(self.bus)
    self.dfx_collector = dfx_collect.new()
    self.prbs_test = optical_module_prbs_test.new(self.bus)
end

function app:check_dependencies()
    local admin = mc_admin.new()
    admin:parse_dependency(utils_core.getcwd() .. '/mds/service.json')
    admin:check_dependency(self.bus)
end

function app:init_internal()
    self.object_manage:start()
    self:register_ipmi()
end

-- 资源树没有属性时，自发现不分发对象，需要手动上树
function app:config_register()
    self:CreatePRBSTest(SYSTEM_ID_DEFAULT)
end

function app:init()
    app.super.init(self)
    self:check_dependencies()
    self.network_adapters = self:CreateNetworkAdapters(SYSTEM_ID_DEFAULT)
    self.object_manage = c_object_manage.new(self.db, self.bus)
    self.object_manage.app = app
    self.object_manage.per_db = self.db
    self:config_register()
    c_tasks.get_instance():next_tick(function()
        self:init_internal()
        self:register_rpc()
        self:register_debug_method()
        self.ncsi_service:init()
    end)
    -- 注册平滑重启回调函数
    -- 准备重启回调
    local SUCCESS <const> = 0
    local FAIL <const> = -1
    reboot_manage.on_prepare(function()
        return SUCCESS
    end)
    -- 执行重启回调
    reboot_manage.on_action(function()
        return SUCCESS
    end)
    -- 取消重启回调
    reboot_manage.on_cancel(function()
    end)

    -- 日志收集注册回调
    intf_debug.on_dump(function(ctx, path)
        self.device_manager:on_log_file_dump(ctx, path, self.bma)
    end)
    -- 初始化dfx收集
    dfx_collect.get_instance():init()
    -- 初始化数据库
    event_mgmt:init(self.reset_local_db)

    log:info('[network_adapter] init finished.')
end

function app:main()
    log:info('[network_adapter] start.')
end

function app:register_network_adapter_rpc()
    self:ImplNetworkAdaptersNetworkAdaptersGetNetCardPortNum(function(obj, ctx, ...)
        local num = self.device_manager:method_get_netcard_port_num(...)
        if not num then
            error(base_messages.PropertyValueNotInList('', ''))
        end
        return 1, num
    end)

    self:ImplNetworkAdaptersNetworkAdaptersGetPortSpeed(function(obj, ctx, ...)
        return self.device_manager:method_get_netport_speed(...)
    end)

    self:ImplNetworkAdaptersNetworkAdaptersGetNetworkAdapterNum(function(obj, ctx, ...)
        return self.device_manager:method_get_network_adapter_num(...)
    end)

    self:ImplNetworkAdaptersNetworkAdaptersGetNetworkPortNum(function(obj, ctx, ...)
        return self.device_manager:method_get_network_port_num()
    end)
    self:ImplNetworkAdaptersNetworkAdaptersGetBandwidthHistory(function(obj, ctx, ...)
        return self.device_manager:method_get_bandwidth_history()
    end)
    self:ImplNetworkAdaptersNetworkAdaptersClearBandwidthHistory(function(obj, ctx, ...)
        return self.device_manager:clear_port_usage_history(...)
    end)
    self:ImplNetworkAdaptersNetworkAdaptersSetBandwidthThreshold(function(obj, ctx, ...)
        return self.device_manager:set_usage_threshold(...)
    end)

    self:ImplNetworkAdapterNetworkAdapterStartRemovingDevice(function(obj, ctx, ...)
        return self.device_manager:method_start_removing_device(ctx, ...)
    end)
    self:ImplNetworkAdapterNetworkAdapterDumpBlackBox(function(obj, ctx, path, ...)
        return self.device_manager:method_dump_black_box(obj, ctx, path)
    end)

    self:ImplOpticalModuleMetricGetItems(function(obj, ctx, ...)
        return self.metric_collect:get_metric_items(obj, ...)
    end)
    self:ImplOpticalModuleMetricGetData(function(obj, ctx, ...)
        return self.metric_collect:get_metric_data(obj, ...)
    end)
end

function app:register_network_bonding_rpc()
    self:ImplNetworkAdaptersNetworkBondingsCreateBond(function(obj, ctx, ...)
        return self.bma_rpc_bond:create_bond(obj, ctx, ...)
    end)
    self:ImplNetworkAdaptersNetworkBondingsDeleteBond(function(obj, ctx, ...)
        return self.bma_rpc_bond:delete_bond(obj, ctx, ...)
    end)
    self:ImplNetworkAdaptersNetworkBondingsSetLinkMonitorPeriodMS(function(obj, ctx, ...)
        return self.bma_rpc_bond:set_link_monitor_period(obj, ctx, ...)
    end)
end

function app:register_network_port_rpc()
    self:ImplVLANsVLANsCreateVLAN(function(...)
        return self.bma_rpc_port:create_vlan(...)
    end)
    self:ImplVLANVLANConfigureVLAN(function(...)
        return self.bma_rpc_port:configure_vlan(...)
    end)
    self:ImplVLANVLANDeleteVLAN(function(...)
        return self.bma_rpc_port:delete_vlan(...)
    end)
    self:ImplNetworkPortNetworkPortConfigure(function(...)
        return self.bma_rpc_port:configure(...)
    end)
    self:ImplPRBSTestPRBSTestQueryInfo(function (obj, ctx, ...)
        return self.prbs_test:query_info(ctx, ...)
    end)
    self:ImplPRBSTestPRBSTestConfig(function (obj, ctx, ...)
        return self.prbs_test:config(ctx, ...)
    end)
    self:ImplPRBSTestPRBSTestClearStatistics(function (obj, ctx, ...)
        return self.prbs_test:clear_statistics(ctx, ...)
    end)
    self:ImplPRBSTestPRBSTestShutdown(function (obj, ctx, ...)
        return self.prbs_test:shutdown(ctx, ...)
    end)
end

function app:register_rpc()
    self:register_network_adapter_rpc()
    self:register_network_bonding_rpc()
    self:register_network_port_rpc()
end

function app:register_debug_method()
    self:CreateNCSI(SYSTEM_ID_DEFAULT)
    self:ImplNCSINCSISendCmdOverMCTP(function(obj, ctx, ...)
        local data = self.debug:send_and_receive_ncsi(...)
        log:error(data)
        return data
    end)
end

function app:register_ipmi()
    self:register_ipmi_cmd(ipmi_struct.WriteMacAddress, function(req, ctx, ...)
        self.device_manager:set_nic_mac_address_from_ipmi(req, ctx)
        return msg.WriteMacAddressRsp.new(req.FruId)
    end)
    self:register_ipmi_cmd(ipmi_struct.GetMacAddress, function(req, ctx, ...)
        if not req.Data or (#req.Data ~= 1 and #req.Data ~= 2) then
            log:error('get mac address fail, cause data is invalid')
            return msg.GetMacAddressRsp.new(comp_code.InvalidFieldRequest,
                0, 0, 0, '')
        end
        local mac_id, interface_type, mac = self.device_manager:get_mac_address_from_ipmi(req)
        log:error('get nic mac,mac_id %s, interface_type%s, mac:%s', mac_id, interface_type, mac)
        return msg.GetMacAddressRsp.new(RET_OK, req.FruId, mac_id, interface_type, mac)
    end)

    self:register_ipmi_cmd(ipmi_struct.GetNcsiSlot, function(req, ctx, ...)
        return msg.GetNcsiSlotRsp.new(RET_OK,MANUFACTURE_ID, 255)
    end)
    self:register_ipmi_cmd(ipmi_struct.GetBusinessPortInfo, function(...)
        return self.device_manager:get_business_port_info_from_ipmi(...)
    end)

    self:register_ipmi_cmd(ipmi_struct.SetIPMCConfiguration, function(...)
        return self.ncsi_service:set_ncsi_rx_channel_from_ipmi(...)
    end)
    self:register_ipmi_cmd(ipmi_struct.GetIPMCConfiguration, function(...)
        return self.ncsi_service:get_ncsi_rx_channel_from_ipmi(...)
    end)

    self:register_ipmi_cmd(ipmi_struct.GetNetworkAdapterModelByLOM, function(...)
        return self.device_manager:get_network_adapter_chip_model_from_ipmi(msg.GetNetworkAdapterModelByLOMRsp, ...)
    end)
    self:register_ipmi_cmd(ipmi_struct.GetNetworkAdapterModelByOCPCard, function(...)
        return self.device_manager:get_network_adapter_chip_model_from_ipmi(msg.GetNetworkAdapterModelByOCPCardRsp, ...)
    end)
end

return app
