-- 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 skynet = require 'skynet'
local class = require 'mc.class'
local log = require 'mc.logging'
local reboot_manage = require 'mc.mdb.micro_component.reboot'
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 mc_admin = require 'mc.mc_admin'
local base_messages = require 'messages.base'
local utils_core = require 'utils.core'
local ipmi = require 'ipmi'
local comp_code = ipmi.types.Cc
local ipmi_struct = require 'network_adapter.ipmi.ipmi'
local msg = require 'network_adapter.ipmi.ipmi_message'
local c_service = require 'network_adapter.service'
local c_network_adapter_debug = require 'network_adapter_debug'
local debug = require 'debug.init'
local event_mgmt = require 'event.event_mgmt'
local metric_collect = require 'metric.metric_collect'
local dfx_collect = require 'dfx_collect.dfx_collect_mgnt'
local optical_module_prbs_test = require 'prbs_test.optical_module_prbs_test'
local optical_module_dirty_test = require 'dirty_test.optical_module_dirty_test'
local c_bma = require 'bma'
local c_bma_rpc_bond = require 'bma.rpc.bond'
local c_bma_rpc_port = require 'bma.rpc.port'
local ncsi_service = require 'ncsi.ncsi_service'
local nic_mgmt = require 'device.class.nic_mgmt'
local c_device_manager = require 'device.device_manager'
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.network_adapter_debug = c_network_adapter_debug.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)
    self.dirty_test            = optical_module_dirty_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)
    self:CreateContaminationDetection(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
    c_tasks.get_instance():next_tick(function()
        nic_mgmt.new(self.bus)
    end)
    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()
        log:notice('[network_adapter] reboot preparation completed successfully')
        return SUCCESS
    end)
    -- 执行重启回调
    reboot_manage.on_action(function()
        self:reset_ncsi_port_type()
        log:notice('[network_adapter] reboot on action completed successfully')
        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)
    -- 常驻gc任务
    self:gc_task()

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

function app:reset_ncsi_port_type()
    -- 重启前恢复网口类型，启动再重新探测类型更新
    self.db:select(self.db.NcsiNCPortInfo):fold(function(port_obj)
        port_obj.Type = 'ExternalPCIe'
        port_obj:save()
    end)
end

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

function app:register_network_adapter_rpc()
    self:ImplNetworkAdapterskeplerSystemsNetworkAdaptersGetNetCardPortNum(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:ImplNetworkAdapterskeplerSystemsNetworkAdaptersGetPortSpeed(function(obj, ctx, ...)
        return self.device_manager:method_get_netport_speed(...)
    end)

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

    self:ImplNetworkAdapterskeplerSystemsNetworkAdaptersGetNetworkPortNum(function(obj, ctx, ...)
        return self.device_manager:method_get_network_port_num()
    end)
    self:ImplNetworkAdapterskeplerSystemsNetworkAdaptersGetBandwidthHistory(function(obj, ctx, ...)
        return self.device_manager:method_get_bandwidth_history()
    end)
    self:ImplNetworkAdapterskeplerSystemsNetworkAdaptersClearBandwidthHistory(function(obj, ctx, ...)
        return self.device_manager:clear_port_usage_history(...)
    end)
    self:ImplNetworkAdapterskeplerSystemsNetworkAdaptersSetBandwidthThreshold(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)
    self:ImplContaminationDetectionContaminationDetectionInitiate(function (obj, ctx, ...)
        return self.dirty_test:initiate(ctx, ...)
    end)
    self:ImplContaminationDetectionContaminationDetectionStart(function (obj, ctx, ...)
        return self.dirty_test:start(ctx, ...)
    end)
    self:ImplContaminationDetectionContaminationDetectionGetResults(function (obj, ctx, ...)
        return self.dirty_test:get_result(ctx, ...)
    end)
    self:ImplContaminationDetectionContaminationDetectionShutdown(function (obj, ctx, ...)
        return self.dirty_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)
    local network_adapters_debug_obj = self:CreateNetworkAdaptersDebug(SYSTEM_ID_DEFAULT)
    self:ImplNetworkAdaptersDebugDebugSystemsNetworkAdaptersDump(function(obj, ctx, ...)
        self.device_manager:collect_netcard_info(...)
    end)
    network_adapters_debug_obj.property_changed:on(function (name, value)
        self.network_adapter_debug:props_changed_callback(name, value)
    end)
end

function app:gc_task()
    skynet.fork_loop({count = 0}, function()
        skynet.sleep(60000) -- 启动阶段等待10分钟后开始gc任务
        log:notice('gc task start')
        while true do
            collectgarbage('collect')
            utils_core.malloc_trim(0)
            skynet.sleep(30000)  -- 5分钟gc一次
        end
    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)

    self:register_ipmi_cmd(ipmi_struct.GetDpuOSStartupStatus, function(...)
        return self.device_manager:get_dpu_os_startup_status(...)
    end)
    self:register_ipmi_cmd(ipmi_struct.GetDpuForcePowerOnState, function(...)
        return self.device_manager:get_dpu_force_power_on_status(...)
    end)
    self:register_ipmi_cmd(ipmi_struct.SetDpuForcePowerOnState, function(...)
        return self.device_manager:set_dpu_force_power_on_status(...)
    end)
    self:register_ipmi_cmd(ipmi_struct.GetDpuHostAccess, function(...)
        return self.device_manager:get_dpu_host_access(...)
    end)
    self:register_ipmi_cmd(ipmi_struct.SetDpuHostAccess, function(...)
        return self.device_manager:set_dpu_host_access(...)
    end)
    self:register_ipmi_cmd(ipmi_struct.GetDpuExtendedHostPrivilege, function(...)
        return self.device_manager:get_dpu_extended_host_privilege(...)
    end)
    self:register_ipmi_cmd(ipmi_struct.SetDpuExtendedHostPrivilege, function(...)
        return self.device_manager:set_dpu_extended_host_privilege(...)
    end)
    self:register_ipmi_cmd(ipmi_struct.GetDpuMode, function(...)
        return self.device_manager:get_dpu_mode(...)
    end)
    self:register_ipmi_cmd(ipmi_struct.SetDpuMode, function(...)
        return self.device_manager:set_dpu_mode(...)
    end)
end

return app
