/* Copyright (c) 2025 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.
 */

#include "common.h"
#include "luafilter/l_callbacks.h"
#include "transfer/spdm_dbus.h"

unordered_map<string, unique_ptr<LCallbacks::st_cb>> LCallbacks::cb_map;

void LCallbacks::register_to(lua_State* L, luawrap::stack_table& t)
{
    luawrap::restore_stack_top _s(L);

    auto cls = luawrap::lua_class<LCallbacks>(L);
    cls.ctor<>();
    cls.def("set_send_recv_cb", c_func_wrap(L, LCallbacks::set_send_recv_cb));
    cls.def("set_send_recv_dbus", c_func_wrap(L, LCallbacks::set_send_recv_dbus));

    t.set("callbacks", cls);
}

// MCTP 模式:注册 Lua 回调
void LCallbacks::set_send_recv_cb(const string& device_name, optional<t_send_recv_cb> cb)
{
    auto it = cb_map.find(device_name);
    if (it == cb_map.end()) {
        unique_ptr<st_cb> s_cb = make_unique<LCallbacks::st_cb>();
        cb_map.emplace(device_name, move(s_cb));
        it = cb_map.find(device_name);
        if (it == cb_map.end()) {
            return;
        }
    }

    auto&              s_cb = *it->second;
    unique_lock<mutex> guard(s_cb.send_recv_mutex);
    s_cb.mode         = CallMode::LUA_CALLBACK;
    s_cb.send_recv_cb = move(cb);
}

// RPC 模式:注册 D-Bus 目标信息
void LCallbacks::set_send_recv_dbus(const string& device_name, const string& object_path)
{
    auto it = cb_map.find(device_name);
    if (it == cb_map.end()) {
        unique_ptr<st_cb> s_cb = make_unique<LCallbacks::st_cb>();
        cb_map.emplace(device_name, move(s_cb));
        it = cb_map.find(device_name);
        if (it == cb_map.end()) {
            return;
        }
    }

    auto&              s_cb = *it->second;
    unique_lock<mutex> guard(s_cb.send_recv_mutex);
    s_cb.mode        = CallMode::DBUS_SYNC;
    s_cb.object_path = object_path;
}

// MCTP 模式:Lua 回调调用
static int32_t lua_callback_send_recv(LCallbacks::st_cb& s_sb, const string& device_name, const uint8_t* request,
                                      size_t request_size, uint8_t* response, size_t* response_size)
{
    if (!s_sb.send_recv_cb) {
        debug_log(DLOG_ERROR, "lua callback is null");
        return RET_ERR;
    }

    string_view req_data(reinterpret_cast<const char*>(request), request_size);

    int32_t               result;
    optional<string_view> rsp_data;
    uint32_t              timeout = 30000; // 超时时长30秒
    tie(result, rsp_data)         = (*s_sb.send_recv_cb)(device_name, request_size, req_data, timeout);
    if (result != RET_OK) {
        debug_log(DLOG_ERROR, "lua callback send message failed, result: %d", result);
        return RET_ERR;
    }

    result = memcpy_s(response, *response_size, rsp_data->data(), rsp_data->size());
    if (result != RET_OK) {
        debug_log(DLOG_ERROR, "memcpy_s failed, ret = %d", result);
        *response_size = 0;
    } else {
        *response_size = rsp_data->size();
    }

    return result;
}

// 根据 mode 分发
int32_t LCallbacks::send_recv(const string& device_name, const uint8_t* request, size_t request_size, uint8_t* response,
                              size_t* response_size)
{
    auto it = cb_map.find(device_name);
    if (it == cb_map.end()) {
        debug_log(DLOG_ERROR, "can not found send/recv callback for device %s", device_name.c_str());
        return RET_ERR;
    }

    auto&              s_cb = *it->second;
    unique_lock<mutex> guard(s_cb.send_recv_mutex);

    switch (s_cb.mode) {
        case CallMode::DBUS_SYNC:
            return dbus_sync_send_recv(s_cb.object_path, request, request_size, response, response_size);
        case CallMode::LUA_CALLBACK:
            return lua_callback_send_recv(s_cb, device_name, request, request_size, response, response_size);
        default:
            debug_log(DLOG_ERROR, "unknown call mode");
            return RET_ERR;
    }
}