-- 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 log = require 'mc.logging'
local utils = require 'mc.utils'
local task_mgmt = require 'mc.mdb.task_mgmt'
local json = require 'cjson'
local client = require 'network_adapter.client'
local task_state <const> = task_mgmt.state
local task_status <const> = task_mgmt.status
local skynet = require 'skynet'
local mdb = require 'mc.mdb'

local TASK_PROCESS_OK <const> = 200
local TASK_PROCESS_FAILED <const> = 500
local TASK_PROCESS_TIMEOUT <const> = 400
local TASK_PROCESS_NOTFOUND <const> = 404
local SMS_PATH<const> = '/bmc/kepler/Systems/1/Sms'
local SMS_INTERFACE<const> = 'bmc.kepler.Systems.Sms.SmsStatus'

local bma_rpc_common = {
    CMD_TYPE = {
        CREATE = 1,
        DELETE = 2,
        MODIFY = 3
    }
}

function bma_rpc_common.is_bma_connected()
    local obj = client:GetSmsSmsStatusObject()
    if obj and obj.State == 0 then
        return true
    else
        return false
    end
end

function bma_rpc_common.is_bma_running()
    local ok, obj = pcall(mdb.get_object, client:get_bus(), SMS_PATH, SMS_INTERFACE)
    if not ok then
        log:info('[network_adapter] get bma obj failed, err:%s', obj)
        return false
    end
    if obj and obj.RunningState == "Running" then
        return true
    end
    return false
end

function bma_rpc_common.update_task_msg_bma_not_present(task_id)
    task_mgmt.update_task(task_id, {State = task_state.Exception, Status = task_status.Error, 
        MessageId = 'ConfigNetworkFailed', MessageArgs = {'Operation failed with BMA disconnect'}})
end

-- 发送请求给bma并获取响应
local function sms_forward_redfish_request(ctx, req)
    local retry_times <const> = 2
    local ok, response = client:PSmsSmsForwardRequest(ctx, _, json.encode(req), retry_times)
    if not ok then
        log:error('forward request failed, %s', response)
        return
    end

    local rsp = json.decode(response.Response)
    rsp.ResponseBody = json.decode(rsp.ResponseBody)
    return rsp
end

local function get_bma_task_state(ctx, task_uri)
    local req = {
        RequestMethod = 'GET',
        RequestUri = task_uri
    }

    local rsp = sms_forward_redfish_request(ctx, req)
    return rsp and rsp.ResponseBody or nil
end

function bma_rpc_common:wait_object_created(find_cb, rsc_id)
    if not rsc_id then
        return
    end

    local count = 0
    local object
    -- 重试5次，最多等10s
    while count < 5 do
        object = find_cb(rsc_id)
        if object then
            break
        end
        skynet.sleep(200)
        count = count + 1
    end
end

local function get_created_rsc_id(uri)
    local list = utils.split(uri, '/')
    if not list then
        return
    end
    return list[#list]
end

function bma_rpc_common:monitor_task(ctx, bma_task, task_id, cmd_type, operate_log_msg, cb)
    task_mgmt.update_task(task_id, {State = task_state.Running, Progress = 20})
    local task_uri = '/redfish/v1/Sms/1/TaskService/Tasks/' .. bma_task
    local task_rsp
    local running_time = 0
    local created_rsc_id
    while true do
        task_rsp = get_bma_task_state(ctx, task_uri)
        if task_rsp == nil then
            goto continue
        end
        log:info('taskState = %s', task_rsp.TaskState)
        if task_rsp.TaskState == 'Running' then
            running_time = running_time + 1
            if running_time > 30 then  -- 大于1min认为超时
                -- 结束任务并产生一个超时的消息描述
                task_mgmt.update_task(task_id, {State = task_state.Exception, Status = task_status.Error, 
                    MessageId = 'OperationTimeout'})
                log:operation(ctx:get_initiator(), 'NetworkAdapter', operate_log_msg .. ' failed')
                break
            end
        elseif task_rsp.TaskState == 'Completed' then
            -- 如果是创建操作，将新创建bond的id返回
            if cmd_type == self.CMD_TYPE.CREATE then
                created_rsc_id = get_created_rsc_id(task_rsp.Messages.MessageArgs[1])
                self:wait_object_created(cb, created_rsc_id)
                task_mgmt.update_task(task_id, {MessageArgs = {created_rsc_id or ''}})
            else
                cb()
            end

            -- 任务正常结束
            task_mgmt.update_task(task_id, {State = task_state.Completed, Status = task_status.OK, 
                Progress = 100})
            log:operation(ctx:get_initiator(), 'NetworkAdapter', operate_log_msg .. ' successfully')
            break
        else
            -- 任务出错，结束任务并记录错误的结果
            task_mgmt.update_task(task_id, {State = task_state.Exception, Status = task_status.Error, 
                MessageId = 'TaskFailed', {MessageArgs = task_rsp.Messages.MessageArgs}})
            log:operation(ctx:get_initiator(), 'NetworkAdapter', operate_log_msg .. ' failed')
            break
        end
        ::continue::
        skynet.sleep(200)
    end
end

function bma_rpc_common:handle_bma_task(ctx, req, task_id, cmd_type, operate_log_msg, cb)
    skynet.fork(function()
        local rsp = sms_forward_redfish_request(ctx, req)
        -- BMA侧任务创建成功，轮询BMA的task
        if rsp and rsp.ResponseStatusCode == TASK_PROCESS_OK then
            task_mgmt.update_task(task_id, {MessageId = 'Success'})
            self:monitor_task(ctx, rsp.ResponseBody.TaskId, task_id, cmd_type, operate_log_msg, cb)
        else
            -- BMA侧任务创建失败
            if rsp and rsp.ResponseStatusCode == TASK_PROCESS_TIMEOUT then
                task_mgmt.update_task(task_id, {MessageId = 'OperationTimeout'})
            end

            if rsp and rsp.ResponseStatusCode == TASK_PROCESS_NOTFOUND then
                task_mgmt.update_task(task_id, {MessageId = 'ResourceMissingAtURI', MessageArgs = {req.RequestUri}})
            end

            task_mgmt.update_task(task_id, {State = task_state.Exception, Status = task_status.Error})
            log:operation(ctx:get_initiator(), 'NetworkAdapter', operate_log_msg .. ' failed')
        end
    end)
end

return bma_rpc_common