-- 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.

-- Description: 装备测试
local class = require 'mc.class'
local log = require 'mc.logging'
local mdb = require 'mc.mdb'
local ctx = require 'mc.context'
local skynet = require 'skynet'
local ncsi_core = require 'ncsi.ncsi_core'
local manufacture_app = class()

local STATUS_COMPLETE = "Complete"
local STATUS_TESTING = "Testing"
local STATUS_UNSTART = "Unstart"
local STATUS_FAILED = "Failed"

local RESULT_SUCCEED = "Succeed"
local RESULT_FAILED = "Failed"
local RESULT_NON = "Non"

local NCSI_CABLE_PATH <const> = '/bmc/kepler/Systems/1/NCSICapabilities'
local NCSI_CABLE_INTERFACE <const> = 'bmc.kepler.Systems.NCSICapabilities'

local NCSI_DISABLED = 0
local NCSI_ENABLED = 1
local bmc_eth_id = 1
function manufacture_app.new(app)
    return setmetatable({ app = app }, manufacture_app)
end

function manufacture_app:init()
    self.description = ''
    self.result = RESULT_NON
    self.test_in_process_flag = false
    self:register_rpc_methods()
end

local function check_ncsi(bus)
    local ok, obj = pcall(mdb.get_object, bus, NCSI_CABLE_PATH, NCSI_CABLE_INTERFACE)
    if not ok then
        log:error('[network_adapter] get object ncsi cable failed')
        return
    end
    if obj and obj.PCIeNCSIEnabled == 1 then
        return true
    end
end

local function switch_port(eth_id, bus, eth_name, channel_id)
    -- 初始化
    local ok, package_id = ncsi_core.ncsi_paramter_init(channel_id, eth_name, NCSI_ENABLED)
    if not ok then
        log:notice('[network_adapter] ncsi_paramter_init failed')
        return
    end
    -- 取值0, 1
    log:debug('[network_adapter] package_id:%s ,channel_id:%s ,eth_name:%s ,eth_id:%s',
        package_id, channel_id, eth_name, eth_id)
    local link_status
    ok, link_status = ncsi_core.ncsi_get_link_status(package_id, channel_id, eth_name, eth_id)
    if not ok then
        log:notice('[network_adapter] ncsi: ncsi_get_link_status failed, eth_id: %s', eth_id)
        return
    end
    log:info('[network_adapter] ncsi: link_status: %s', link_status)
    return link_status
end

function manufacture_app:verify_ncsi(bus, channel_id)
    local ok = check_ncsi(bus)
    if not ok then
        log:notice('[network_adapter] check ncsi failed')
        return
    end
    local ncsi_service = self.app.ncsi_service
    if not ncsi_service then
        log:notice('[network_adapter] check ncsi service failed')
        return
    end
    local db = ncsi_service.db
    local nc_info = db:select(db.NcsiNCInfo):first()
    local eth_id = nc_info.EthId or 0
    -- 管理组id 默认0
    local eth_name = "eth" .. (nc_info and nc_info.EthId or '0')
    local link_status = switch_port(eth_id, bus, eth_name, channel_id)
    if not link_status then
        log:notice('[network_adapter] check ncsi link_status failed')
        return
    end
    local link_status_str = link_status == 0 and 'Disconnected' or 'Connected'
    return link_status_str
end

function manufacture_app:dft_ncsi_start(obj)
    if obj.Status == STATUS_TESTING then
        log:info("[network_adapter] ncsi test in process, please wait")
        return
    end
    obj.Status = STATUS_TESTING
    self.result = RESULT_NON
    skynet.fork(function()
        local channel_id = obj.DeviceNum
        log:notice('[network_adapter] ncsi test channel_id is: %s', channel_id)
        self.test_in_process_flag = true
        local ret = self:verify_ncsi(self.app.bus, channel_id)
        if ret then
            self.description = ret
            log:notice('[network_adapter] ncsi test success %s', ret)
            self.result = RESULT_SUCCEED
        else
            log:notice('[network_adapter] ncsi test failed')
            self.result = RESULT_FAILED
            self.description = RESULT_FAILED
        end
        obj.Status = STATUS_COMPLETE
        self.test_in_process_flag = false
    end)
end

function manufacture_app:dft_ncsi_stop(obj)
    obj.Status = STATUS_UNSTART
end

function manufacture_app:dft_ncsi_get_result(obj)
    if self.test_in_process_flag then
        log:info("[network_adapter] ncsi test in process, please wait")
        return RESULT_NON, ""
    end
    return self.result, self.description
end
function manufacture_app:register_rpc_methods()
    self:register_ncsi_test()
end

function manufacture_app:register_ncsi_test()
    self.app:ImplDftNCSIManufactureStart(function(obj, ...)
        return self:dft_ncsi_start(obj)
    end)
    self.app:ImplDftNCSIManufactureStop(function(obj, ...)
        return self:dft_ncsi_stop(obj)
    end)
    self.app:ImplDftNCSIManufactureGetResult(function(obj, ...)
        return self:dft_ncsi_get_result(obj)
    end)
end

return manufacture_app
