/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "app_spawn_test_helper.h"

#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <pthread.h>
#include <csignal>
#include <string>
#include <sys/eventfd.h>
#include <sys/wait.h>
#include <unistd.h>
#include <inttypes.h>

#include "appspawn.h"
#include "appspawn_client.h"
#include "appspawn_modulemgr.h"
#include "appspawn_msg.h"
#include "appspawn_server.h"
#include "appspawn_service.h"
#include "appspawn_manager.h"
#include "appspawn_utils.h"
#include "loop_event.h"
#include "parameters.h"
#include "securec.h"

#include "app_spawn_stub.h"

namespace OHOS {
typedef struct {
    int32_t bundleIndex;
    char bundleName[APP_LEN_BUNDLE_NAME];  // process name
} AppBundleInfo;

typedef struct {
    uint32_t hapFlags;
    char apl[APP_APL_MAX_LEN];
} AppDomainInfo;

const uint32_t AppSpawnTestServer::defaultProtectTime = 60000; // 60000 60s

uint32_t AppSpawnTestServer::serverId = 0;
static int TestChildLoopRun(AppSpawnContent *content, AppSpawnClient *client)
{
    APPSPAWN_LOGV("ChildLoopRun ...");
    AppSpawningCtx *property = (AppSpawningCtx *)client;
    APPSPAWN_CHECK(content != NULL && property != NULL, return 0, "invalid param in clearEnv");
    int fd = property->forkCtx.fd[1];
    property->forkCtx.fd[1] = -1;
    APPSPAWN_CHECK(fd >= 0, return 0, "invalid fd for notify parent");
    int ret = 0;
    (void)write(fd, &ret, sizeof(ret));
    (void)close(fd);
    sleep(1);
    return 0;
}

AppSpawnTestServer::~AppSpawnTestServer()
{
    if (localServer_) {
        delete localServer_;
        localServer_ = nullptr;
    }
}

void AppSpawnTestServer::CloseCheckHandler(void)
{
    APPSPAWN_LOGV("CloseCheckHandler");
#ifdef USER_TIMER_TO_CHECK
    if (timer_ != nullptr) {
        LE_StopTimer(LE_GetDefaultLoop(), timer_);
        timer_ = nullptr;
    }
#else
    if (idle_) {
        LE_DelIdle(idle_);
        idle_ = nullptr;
    }
#endif
}

void AppSpawnTestServer::StartCheckHandler(void)
{
#ifdef USER_TIMER_TO_CHECK
    int ret = LE_CreateTimer(LE_GetDefaultLoop(), &timer_, ProcessIdle, this);
    if (ret == 0) {
        ret = LE_StartTimer(LE_GetDefaultLoop(), timer_, 100, 10000000);  // 100 10000000 repeat
    }
#else
    LE_AddIdle(LE_GetDefaultLoop(), &idle_, ProcessIdle, this, 10000000);  // 10000000 repeat
#endif
}

void AppSpawnTestServer::ServiceThread()
{
    CmdArgs *args = nullptr;
    pid_t pid = getpid();
    APPSPAWN_LOGV("AppSpawnTestServer::ServiceThread %{public}s", serviceCmd_.c_str());

    running_ = true;
    // 测试server时,使用appspawn的server
    if (testServer_) {
        content_ = AppSpawnTestHelper::StartSpawnServer(serviceCmd_, args);
        if (content_ == nullptr) {
            return;
        }
        if (pid == getpid()) {  // 主进程进行处理
            APPSPAWN_LOGV("Service start timer %{public}s ", serviceCmd_.c_str());
            StartCheckHandler();
            AppSpawnMgr *content = reinterpret_cast<AppSpawnMgr *>(content_);
            APPSPAWN_CHECK_ONLY_EXPER(content != nullptr, return);
            // register
            RegChildLooper(&content->content, TestChildLoopRun);
        }
        content_->runAppSpawn(content_, args->argc, args->argv);
        if (pid != getpid()) {  // 子进程退出
            exit(0);
        } else {
            content_ = nullptr;
        }
    } else {
        StartCheckHandler();
        localServer_ = new LocalTestServer();
        localServer_->Run(APPSPAWN_SOCKET_NAME, recvMsgProcess_);
    }
    APPSPAWN_LOGV("AppSpawnTestServer::ServiceThread finish %{public}s ", serviceCmd_.c_str());
    if (args) {
        free(args);
    }
    return;
}

void AppSpawnTestServer::Start(void)
{
    Start(nullptr);
}

static void *ServiceHelperThread(void *arg)
{
    AppSpawnTestServer *server = reinterpret_cast<AppSpawnTestServer *>(arg);
    APPSPAWN_LOGV("AppSpawnTestServer::thread ");
    server->ServiceThread();
    return nullptr;
}

void AppSpawnTestServer::Start(RecvMsgProcess process, uint32_t time)
{
    APPSPAWN_LOGV("AppSpawnTestServer::Start serverId %{public}u", AppSpawnTestServer::serverId);
    protectTime_ = time;
    uint32_t retry = 0;
    if (threadId_ != 0) {
        return;
    }
    clock_gettime(CLOCK_MONOTONIC, &startTime_);
    recvMsgProcess_ = process;
    errno = 0;
    int ret = 0;
    do {
        threadId_ = 0;
        ret = pthread_create(&threadId_, nullptr, ServiceHelperThread, static_cast<void *>(this));
        if (ret == 0) {
            break;
        }
        APPSPAWN_LOGE("AppSpawnTestServer::Start create thread fail %{public}d %{public}d", ret, errno);
        usleep(20000); // 20000 20ms
        retry++;
    } while (ret == EAGAIN && retry < 10); // 10 max retry

    // wait server thread run
    retry = 0;
    while (!running_ && (retry < 10)) { // 10 max retry
        usleep(20000); // 20000 20ms
        retry++;
    }
    APPSPAWN_LOGV("AppSpawnTestServer::Start retry %{public}u", retry);
}

void AppSpawnTestServer::Stop()
{
    APPSPAWN_LOGV("AppSpawnTestServer::Stop serverId %{public}u", AppSpawnTestServer::serverId);
    if (threadId_ != 0) {
        stop_ = true;
        pthread_join(threadId_, nullptr);
        threadId_ = 0;
        APPSPAWN_LOGV("Stop");
    }
}

void AppSpawnTestServer::KillNWebSpawnServer()
{
    APPSPAWN_LOGV("Kill nwebspawn %{public}d", serverId_);
    if (appPid_ > 0) {
        kill(appPid_, SIGKILL);
    }
}

void AppSpawnTestServer::StopSpawnService(void)
{
    APPSPAWN_LOGV("StopSpawnService ");
    if (serverStoped) {
        CloseCheckHandler();
    }
    serverStoped = true;
    if (testServer_) {
        struct signalfd_siginfo siginfo = {};
        siginfo.ssi_signo = SIGTERM;
        siginfo.ssi_uid = 0;
        ProcessSignal(&siginfo);
    } else {
        localServer_->Stop();
    }
}

#ifdef USER_TIMER_TO_CHECK
void AppSpawnTestServer::ProcessIdle(const TimerHandle taskHandle, void *context)
#else
void AppSpawnTestServer::ProcessIdle(const IdleHandle taskHandle, void *context)
#endif
{
    APPSPAWN_LOGV("AppSpawnTestServer::ProcessIdle");
    AppSpawnTestServer *server = reinterpret_cast<AppSpawnTestServer *>(const_cast<void *>(context));
    if (server->stop_) {
        server->StopSpawnService();
        return;
    }

    struct timespec end;
    clock_gettime(CLOCK_MONOTONIC, &end);
    uint64_t diff = DiffTime(&server->startTime_, &end);
    if (diff >= (server->protectTime_ * 1000)) {  // 1000 ms -> us
        APPSPAWN_LOGV("AppSpawnTestServer:: timeout %{public}u %{public}" PRIu64 "", server->protectTime_, diff);
        server->StopSpawnService();
        return;
    }
}

static int HandleRecvMessage(const TaskHandle taskHandle, uint8_t * buffer, int bufferSize, int flags)
{
    int socketFd = LE_GetSocketFd(taskHandle);
    struct iovec iov = {
        .iov_base = buffer,
        .iov_len = bufferSize,
    };
    char ctrlBuffer[CMSG_SPACE(APP_MAX_FD_COUNT * sizeof(int))];
    struct msghdr msg = {
        .msg_iov = &iov,
        .msg_iovlen = 1,
        .msg_control = ctrlBuffer,
        .msg_controllen = sizeof(ctrlBuffer),
    };

    AppSpawnConnection *connection = (AppSpawnConnection *) LE_GetUserData(taskHandle);
    errno = 0;
    int recvLen = recvmsg(socketFd, &msg, flags);
    APPSPAWN_CHECK_ONLY_LOG(errno == 0, "recvmsg with errno %d", errno);
    struct cmsghdr *cmsg = nullptr;
    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
        if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
            int fdCount = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
            int* fd = reinterpret_cast<int*>(CMSG_DATA(cmsg));
            APPSPAWN_CHECK(fdCount <= APP_MAX_FD_COUNT,
                return -1, "failed to recv fd %d %d", connection->receiverCtx.fdCount, fdCount);
            APPSPAWN_CHECK(memcpy_s(connection->receiverCtx.fds, fdCount * sizeof(int), fd,
                fdCount * sizeof(int)) == 0, return -1, "memcpy_s fd failed");
            connection->receiverCtx.fdCount = fdCount;
        }
    }

    return recvLen;
}

int LocalTestServer::OnConnection(const LoopHandle loopHandle, const TaskHandle server)
{
    static uint32_t connectionId = 0;
    TaskHandle stream;
    LE_StreamInfo info = {};
    info.baseInfo.flags = TASK_STREAM | TASK_PIPE | TASK_CONNECT;
    info.baseInfo.close = OnClose;
    info.baseInfo.userDataSize = sizeof(TestConnection);
    info.disConnectComplete = nullptr;
    info.sendMessageComplete = SendMessageComplete;
    info.recvMessage = OnReceiveRequest;
    info.handleRecvMsg = HandleRecvMessage;

    ServerInfo *serverInfo = (ServerInfo *)LE_GetUserData(server);
    APPSPAWN_CHECK(serverInfo != nullptr, return -1, "Failed to alloc stream");

    LE_STATUS ret = LE_AcceptStreamClient(loopHandle, server, &stream, &info);
    APPSPAWN_CHECK(ret == 0, return -1, "Failed to alloc stream");
    TestConnection *connection = (TestConnection *)LE_GetUserData(stream);
    APPSPAWN_CHECK(connection != nullptr, return -1, "Failed to alloc stream");
    connection->connectionId = ++connectionId;
    connection->stream = stream;
    connection->msgRecvLen = 0;
    (void)memset_s(&connection->msg, sizeof(connection->msg), 0, sizeof(connection->msg));
    connection->buffer = nullptr;
    connection->recvMsgProcess = serverInfo->recvMsgProcess;
    APPSPAWN_LOGI("OnConnection connection.id %{public}d fd %{public}d ",
        connection->connectionId, LE_GetSocketFd(stream));
    return 0;
}

void LocalTestServer::SendMessageComplete(const TaskHandle taskHandle, BufferHandle handle)
{
    return;
}

void LocalTestServer::OnClose(const TaskHandle taskHandle)
{
    TestConnection *connection = (TestConnection *)LE_GetUserData(taskHandle);
    APPSPAWN_CHECK(connection != nullptr, return, "Invalid connection");
    APPSPAWN_LOGI("OnClose connection.id %{public}d socket %{public}d",
        connection->connectionId, LE_GetSocketFd(taskHandle));

    AppSpawnConnection *spawnConnection = (AppSpawnConnection *) LE_GetUserData(taskHandle);
    if (spawnConnection != nullptr) {
        int fdCount = spawnConnection->receiverCtx.fdCount;
        for (int i = 0; i < fdCount; i++) {
            APPSPAWN_LOGI("OnClose close fd %d", spawnConnection->receiverCtx.fds[i]);
            if (spawnConnection->receiverCtx.fds[i] >= 0) {
                close(spawnConnection->receiverCtx.fds[i]);
            }
        }
    }
}

void LocalTestServer::OnReceiveRequest(const TaskHandle taskHandle, const uint8_t *buffer, uint32_t buffLen)
{
    TestConnection *connection = (TestConnection *)LE_GetUserData(taskHandle);
    APPSPAWN_CHECK(connection != nullptr, LE_CloseTask(LE_GetDefaultLoop(), taskHandle);
        return, "Failed to get client form socket");

    if (connection->recvMsgProcess) {
        connection->recvMsgProcess(connection, buffer, buffLen);
    }
}

int LocalTestServer::Run(const char *socketName, RecvMsgProcess recvMsg)
{
    char path[128] = {0};  // 128 max path
    int ret = snprintf_s(path, sizeof(path), sizeof(path) - 1, "%s%s", APPSPAWN_SOCKET_DIR, socketName);
    APPSPAWN_CHECK(ret >= 0, return -1, "Failed to snprintf_s %{public}d", ret);
    LE_StreamServerInfo info = {};
    info.baseInfo.flags = TASK_STREAM | TASK_PIPE | TASK_SERVER;
    info.baseInfo.userDataSize = sizeof(ServerInfo);
    info.socketId = -1;
    info.server = path;
    info.baseInfo.close = nullptr;
    info.incommingConnect = OnConnection;

    MakeDirRec(path, 0711, 0);  // 0711 default mask
    ret = LE_CreateStreamServer(LE_GetDefaultLoop(), &serverHandle_, &info);
    APPSPAWN_CHECK(ret == 0, return -1, "Failed to create socket for %{public}s errno: %{public}d", path, errno);
    APPSPAWN_LOGI("LocalTestServer path %{public}s fd %{public}d", path, LE_GetSocketFd(serverHandle_));

    ServerInfo *serverInfo = (ServerInfo *)LE_GetUserData(serverHandle_);
    APPSPAWN_CHECK(serverInfo != nullptr, return -1, "Failed to alloc stream");
    serverInfo->local = this;
    serverInfo->recvMsgProcess = recvMsg;
    LE_RunLoop(LE_GetDefaultLoop());
    LE_CloseStreamTask(LE_GetDefaultLoop(), serverHandle_);
    LE_StopLoop(LE_GetDefaultLoop());
    LE_CloseLoop(LE_GetDefaultLoop());
    APPSPAWN_LOGI("LocalTestServer exit");
    return 0;
}

void LocalTestServer::Stop()
{
    APPSPAWN_LOGI("Stop LocalTestServer ");
    LE_StopLoop(LE_GetDefaultLoop());
}

int TestConnection::SendResponse(const AppSpawnMsg *msg, int result, pid_t pid)
{
    APPSPAWN_LOGV("SendResponse result: %{public}d pid: %{public}d", result, pid);
    uint32_t bufferSize = sizeof(AppSpawnResponseMsg);
    BufferHandle handle = LE_CreateBuffer(LE_GetDefaultLoop(), bufferSize);
    AppSpawnResponseMsg *buffer = (AppSpawnResponseMsg *)LE_GetBufferInfo(handle, nullptr, &bufferSize);
    int ret = memcpy_s(buffer, bufferSize, msg, sizeof(AppSpawnMsg));
    APPSPAWN_CHECK(ret == 0, return -1, "Failed to memcpy_s bufferSize");
    buffer->result.result = result;
    buffer->result.pid = pid;
    return LE_Send(LE_GetDefaultLoop(), stream, handle, bufferSize);
}

uint32_t AppSpawnTestHelper::GenRandom(void)
{
    uint32_t random = 0;
    int fd = open("/dev/random", O_RDONLY);
    if (fd >= 0) {
        read(fd, &random, sizeof(random));
        close(fd);
    }
    return random;
}

CmdArgs *AppSpawnTestHelper::ToCmdList(const char *cmd)
{
    const uint32_t maxArgc = 20;
    const uint32_t length = sizeof(CmdArgs) + maxArgc * sizeof(char *) + strlen(cmd) + APP_LEN_PROC_NAME + 1 + 2;
    char *buffer = static_cast<char *>(malloc(length));
    CmdArgs *args = reinterpret_cast<CmdArgs *>(buffer);
    APPSPAWN_CHECK(buffer != nullptr, return nullptr, "Failed to alloc args");
    (void)memset_s(args, length, 0, length);
    char *start = buffer + sizeof(CmdArgs) + maxArgc * sizeof(char *);
    char *end = buffer + length;
    uint32_t index = 0;
    char *curr = const_cast<char *>(cmd);
    while (isspace(*curr)) {
        curr++;
    }

    while (index < (maxArgc - 1) && *curr != '\0') {
        if (args->argv[index] == nullptr) {
            args->argv[index] = start;
        }
        *start = *curr;
        if (isspace(*curr)) {
            *start = '\0';
            // 为SetProcessName 预留空间
            start = (index == 0) ? start + APP_LEN_PROC_NAME : start + 1;
            while (isspace(*curr) && *curr != '\0') {
                curr++;
            }
            if (*curr != '\0') {
                index++;
            }
        } else {
            start++;
            curr++;
        }
    }

    index++;
    args->argv[index] = end - 2;  // 2 last
    args->argv[index][0] = '#';
    args->argv[index][1] = '\0';
    args->argc = index + 1;
    return args;
}

int AppSpawnTestHelper::AddDacInfo(AppSpawnReqMsgHandle &reqHandle)
{
    AppDacInfo dacInfo = {};
    dacInfo.uid = defaultTestUid_;
    dacInfo.gid = defaultTestGid_;
    dacInfo.gidCount = 2;  // 2 count
    dacInfo.gidTable[0] = defaultTestGidGroup_;
    dacInfo.gidTable[1] = defaultTestGidGroup_ + 1;
    APPSPAWN_CHECK_ONLY_EXPER(strcpy_s(dacInfo.userName, sizeof(dacInfo.userName), "test-app-name") == 0,
        return APPSPAWN_ARG_INVALID);
    return AppSpawnReqMsgSetAppDacInfo(reqHandle, &dacInfo);
}

int AppSpawnTestHelper::AddFdInfo(AppSpawnReqMsgHandle &reqHandle)
{
    if (fdArg < 0) {
        fdArg = open("/dev/random", O_RDONLY);
    }
    APPSPAWN_LOGE("Add fd info %{public}d", fdArg);
    APPSPAWN_CHECK(fdArg >= 0, return -1, "open fd failed ");
    return AppSpawnReqMsgAddFd(reqHandle, "fdname", fdArg);
}

AppSpawnReqMsgHandle AppSpawnTestHelper::CreateMsg(AppSpawnClientHandle handle, uint32_t msgType, int base)
{
    AppSpawnReqMsgHandle reqHandle = 0;
    int ret = AppSpawnReqMsgCreate(static_cast<AppSpawnMsgType>(msgType), processName_.c_str(), &reqHandle);
    APPSPAWN_CHECK(ret == 0, return INVALID_REQ_HANDLE, "Failed to create req %{public}s", processName_.c_str());
    APPSPAWN_CHECK_ONLY_EXPER(msgType == MSG_APP_SPAWN || msgType == MSG_SPAWN_NATIVE_PROCESS, return reqHandle);
    do {
        ret = AddFdInfo(reqHandle);
        APPSPAWN_CHECK(ret == 0, break, "Failed to add fd %{public}s", processName_.c_str());
        ret = AppSpawnReqMsgSetBundleInfo(reqHandle, 100, processName_.c_str());  // 100 test index
        APPSPAWN_CHECK(ret == 0, break, "Failed to add bundle info req %{public}s", processName_.c_str());
        ret = AddDacInfo(reqHandle);
        APPSPAWN_CHECK(ret == 0, break, "Failed to add dac %{public}s", processName_.c_str());
        ret = AppSpawnReqMsgSetAppAccessToken(reqHandle, accessTokenIdEx_);
        APPSPAWN_CHECK(ret == 0, break, "Failed to add access token %{public}s", processName_.c_str());

        if (defaultMsgFlags_ != 0) {
            (void)AppSpawnReqMsgSetFlags(reqHandle, TLV_MSG_FLAGS, defaultMsgFlags_);
        }
        if (base) {
            return reqHandle;
        }
        const char *testData = "ssssssssssssss sssssssss ssssssss";
        ret = AppSpawnReqMsgAddExtInfo(reqHandle, "tlv-name-1",
            reinterpret_cast<uint8_t *>(const_cast<char *>(testData)), strlen(testData));
        APPSPAWN_CHECK(ret == 0, break, "Failed to ext tlv %{public}s", processName_.c_str());
        size_t count = permissions_.size();
        for (size_t i = 0; i < count; i++) {
            ret = AppSpawnReqMsgAddPermission(reqHandle, permissions_[i]);
            APPSPAWN_CHECK(ret == 0, break, "Failed to permission %{public}s", permissions_[i]);
        }

        ret = AppSpawnReqMsgSetAppInternetPermissionInfo(reqHandle, 1, 0);
        APPSPAWN_CHECK(ret == 0, break, "Failed to internet info %{public}s", processName_.c_str());

        ret = AppSpawnReqMsgSetAppOwnerId(reqHandle, ownerId_.c_str());
        APPSPAWN_CHECK(ret == 0, break, "Failed to ownerid %{public}s", processName_.c_str());
        const char *renderCmd = "/system/bin/sh ls -l #--ipc-fd=3#--shared-fd=4#--crash-fd=20";
        ret = AppSpawnReqMsgAddExtInfo(reqHandle, MSG_EXT_NAME_RENDER_CMD,
            reinterpret_cast<const uint8_t *>(renderCmd), strlen(renderCmd));
        APPSPAWN_CHECK(ret == 0, break, "Failed to render cmd %{public}s", processName_.c_str());
        ret = AppSpawnReqMsgSetAppDomainInfo(reqHandle, 1, defaultApl_.c_str());
        APPSPAWN_CHECK(ret == 0, break, "Failed to domain info %{public}s", processName_.c_str());
        return reqHandle;
    } while (0);
    AppSpawnReqMsgFree(reqHandle);
    return INVALID_REQ_HANDLE;
}

AppSpawnReqMsgHandle AppSpawnTestHelper::CreateNWebMsg(AppSpawnClientHandle handle, uint32_t msgType, int base)
{
#define TEST_FILE_PERM 0644 // 0644: Test file R/W permissions
    if (renderIpcFd < 0) {
        renderIpcFd = open("render_ipc.txt", O_RDONLY | O_CREAT, TEST_FILE_PERM);
        APPSPAWN_LOGI("Add ipc fd info %{public}d", renderIpcFd);
    }
    if (renderSharedFd < 0) {
        renderSharedFd = open("render_shared.txt", O_RDONLY | O_CREAT, TEST_FILE_PERM);
        APPSPAWN_LOGI("Add shared fd info %{public}d", renderIpcFd);
    }
    if (renderCrashFd < 0) {
        renderCrashFd = open("render_crash.txt", O_RDONLY | O_CREAT, TEST_FILE_PERM);
        APPSPAWN_LOGI("Add crash fd info %{public}d", renderIpcFd);
    }
    if (renderIpcFd < 0 || renderSharedFd < 0 || renderCrashFd < 0) {
        return INVALID_REQ_HANDLE;
    }
    auto reqHandle = CreateMsg(handle, msgType, base);
    do {
        int ret = AppSpawnReqMsgAddFd(reqHandle, "ipc-fd", renderIpcFd);
        APPSPAWN_CHECK(ret == 0, break, "Failed to add fd %{public}s", processName_.c_str());
        ret = AppSpawnReqMsgAddFd(reqHandle, "shared-fd", renderSharedFd);
        APPSPAWN_CHECK(ret == 0, break, "Failed to add fd %{public}s", processName_.c_str());
        ret = AppSpawnReqMsgAddFd(reqHandle, "crash-fd", renderCrashFd);
        APPSPAWN_CHECK(ret == 0, break, "Failed to add fd %{public}s", processName_.c_str());
        return reqHandle;
    } while (0);
    AppSpawnReqMsgFree(reqHandle);
    return INVALID_REQ_HANDLE;
}

AppSpawnMsgNode *AppSpawnTestHelper::CreateAppSpawnMsg(AppSpawnMsg *msg)
{
    AppSpawnMsgNode *msgNode = static_cast<AppSpawnMsgNode *>(calloc(1, sizeof(AppSpawnMsgNode)));
    APPSPAWN_CHECK(msgNode != nullptr, return nullptr, "Failed to create receiver");
    int ret = memcpy_s(&msgNode->msgHeader, sizeof(msgNode->msgHeader), msg, sizeof(msgNode->msgHeader));
    APPSPAWN_CHECK(ret == 0, free(msgNode);
        return nullptr, "Failed to memcpy msg");
    msgNode->buffer = static_cast<uint8_t *>(malloc(msg->msgLen));
    APPSPAWN_CHECK(msgNode->buffer != nullptr, free(msgNode);
        return nullptr, "Failed to memcpy msg");
    uint32_t totalCount = msg->tlvCount + TLV_MAX;
    msgNode->tlvOffset = static_cast<uint32_t *>(malloc(totalCount * sizeof(uint32_t)));
    APPSPAWN_CHECK(msgNode->tlvOffset != nullptr, free(msgNode);
        return nullptr, "Failed to alloc memory for recv message");
    for (uint32_t i = 0; i < totalCount; i++) {
        msgNode->tlvOffset[i] = INVALID_OFFSET;
    }
    return msgNode;
}

AppSpawningCtx *AppSpawnTestHelper::GetAppProperty(AppSpawnClientHandle handle, AppSpawnReqMsgHandle reqHandle)
{
    AppSpawnReqMsgNode *reqNode = static_cast<AppSpawnReqMsgNode *>(reqHandle);
    APPSPAWN_CHECK(reqNode != nullptr && reqNode->msg != nullptr, AppSpawnReqMsgFree(reqHandle);
        return nullptr, "Invalid reqNode");

    AppSpawnMsgNode *msgNode = CreateAppSpawnMsg(reqNode->msg);
    APPSPAWN_CHECK(msgNode != nullptr, return nullptr, "Failed to alloc for msg");

    uint32_t bufferSize = reqNode->msg->msgLen;
    uint32_t currIndex = 0;
    uint32_t bufferStart = sizeof(AppSpawnMsg);
    ListNode *node = reqNode->msgBlocks.next;
    while (node != &reqNode->msgBlocks) {
        AppSpawnMsgBlock *block = ListEntry(node, AppSpawnMsgBlock, node);
        int ret = memcpy_s(msgNode->buffer + currIndex, bufferSize - currIndex,
            block->buffer + bufferStart, block->currentIndex - bufferStart);
        if (ret != 0) {
            AppSpawnReqMsgFree(reqHandle);
            DeleteAppSpawnMsg(&msgNode);
            return nullptr;
        }
        currIndex += block->currentIndex - bufferStart;
        bufferStart = 0;
        node = node->next;
    }
    APPSPAWN_LOGV("GetAppProperty header magic 0x%{public}x type %{public}u id %{public}u len %{public}u %{public}s",
        msgNode->msgHeader.magic, msgNode->msgHeader.msgType,
        msgNode->msgHeader.msgId, msgNode->msgHeader.msgLen, msgNode->msgHeader.processName);

    // delete reqHandle
    AppSpawnReqMsgFree(reqHandle);
    int ret = DecodeAppSpawnMsg(msgNode);
    APPSPAWN_CHECK(ret == 0, DeleteAppSpawnMsg(&msgNode);
        return nullptr, "Decode msg fail");
    AppSpawningCtx *property = CreateAppSpawningCtx();
    APPSPAWN_CHECK_ONLY_EXPER(property != nullptr, DeleteAppSpawnMsg(&msgNode);
        return nullptr);
    property->message = msgNode;
    SetDefaultTestData();
    return property;
}

void AppSpawnTestHelper::SetDefaultTestData()
{
    processName_ = std::string("com.example.myapplication");
    defaultTestUid_ = 20010029;       // 20010029 test
    defaultTestGid_ = 20010029;       // 20010029 test
    defaultTestGidGroup_ = 20010029;  // 20010029 test
    defaultTestBundleIndex_ = 100;    // 100 test
    defaultApl_ = std::string("system_core");
    defaultMsgFlags_ = 0;
    accessTokenIdEx_ = 4832386455;
    ownerId_ = "5765880207854616753";
}

int AppSpawnTestHelper::CreateSocket(int type)
{
    const uint32_t maxCount = 10;
    uint32_t count = 0;
    int socketId = -1;
    while ((socketId < 0) && (count < maxCount)) {
        usleep(20000);                        // 20000 20ms
        socketId = CreateClientSocket(type, 2);  // 2s
        if (socketId > 0) {
            return socketId;
        }
        count++;
    }
    return socketId;
}

int AppSpawnTestHelper::CreateSendMsg(std::vector<uint8_t> &buffer, uint32_t msgType, uint32_t &msgLen,
    const std::vector<AddTlvFunction> &addTlvFuncs)
{
    if (buffer.size() < sizeof(AppSpawnMsg)) {
        return -1;
    }
    AppSpawnMsg *msg = reinterpret_cast<AppSpawnMsg *>(buffer.data());
    msg->magic = APPSPAWN_MSG_MAGIC;
    msg->msgType = msgType;
    msg->msgLen = sizeof(AppSpawnMsg);
    msg->msgId = 1;
    msg->tlvCount = 0;
    APPSPAWN_CHECK_ONLY_EXPER(strcpy_s(msg->processName, sizeof(msg->processName), processName_.c_str()) == 0,
        return -1);
    // add tlv
    uint32_t currLen = sizeof(AppSpawnMsg);
    for (auto addTlvFunc : addTlvFuncs) {
        uint32_t realLen = 0;
        uint32_t tlvCount = 0;
        int ret = addTlvFunc(buffer.data() + currLen, buffer.size() - currLen, realLen, tlvCount);
        APPSPAWN_CHECK(ret == 0 && (currLen + realLen) < buffer.size(),
            return -1, "Failed add tlv to msg %{public}s", processName_.c_str());
        msg->msgLen += realLen;
        currLen += realLen;
        msg->tlvCount += tlvCount;
    }
    msgLen = msg->msgLen;
    APPSPAWN_LOGV("CreateSendMsg msgLen %{public}d", msgLen);
    return 0;
}

static int inline AddOneTlv(uint8_t *buffer, uint32_t bufferLen, const AppSpawnTlv &tlv, const uint8_t *data)
{
    if (tlv.tlvLen > bufferLen) {
        return -1;
    }
    int ret = memcpy_s(buffer, bufferLen, &tlv, sizeof(tlv));
    APPSPAWN_CHECK(ret == 0, return -1, "Failed to memcpy_s bufferSize");
    ret = memcpy_s(buffer + sizeof(tlv), bufferLen - sizeof(tlv), data, tlv.tlvLen - sizeof(tlv));
    APPSPAWN_CHECK(ret == 0, return -1, "Failed to memcpy_s bufferSize");
    return 0;
}

#define ADD_TLV(type, value, currLen, tlvCount)  \
do {    \
    AppSpawnTlv d_tlv = {}; \
    d_tlv.tlvType = (type);    \
    d_tlv.tlvLen = sizeof(AppSpawnTlv) + sizeof(value);   \
    ret = AddOneTlv(buffer + (currLen), bufferLen - (currLen), d_tlv, (uint8_t *)&(value));   \
    APPSPAWN_CHECK(ret == 0, return -1, "Failed add tlv %{public}u", d_tlv.tlvType);  \
    (currLen) += d_tlv.tlvLen;    \
    (tlvCount)++;   \
} while (0)

int AppSpawnTestHelper::AddBaseTlv(uint8_t *buffer, uint32_t bufferLen, uint32_t &realLen, uint32_t &tlvCount)
{
    // add app flags
    uint32_t currLen = 0;
    uint32_t flags[3] = {2, 0b1010, 0};
    AppSpawnTlv tlv = {};
    tlv.tlvType = TLV_MSG_FLAGS;
    tlv.tlvLen = sizeof(AppSpawnTlv) + sizeof(flags);
    int ret = AddOneTlv(buffer + currLen, bufferLen - currLen, tlv, (uint8_t *)flags);
    APPSPAWN_CHECK(ret == 0, return -1, "Failed add tlv %{public}u", tlv.tlvType);
    currLen += tlv.tlvLen;
    tlvCount++;

    tlv.tlvType = TLV_PERMISSION;
    tlv.tlvLen = sizeof(AppSpawnTlv) + sizeof(flags);
    ret = AddOneTlv(buffer + currLen, bufferLen - currLen, tlv, (uint8_t *)flags);
    APPSPAWN_CHECK(ret == 0, return -1, "Failed add tlv %{public}u", tlv.tlvType);
    currLen += tlv.tlvLen;
    tlvCount++;

    AppDomainInfo domainInfo = {0, "normal"};
    ADD_TLV(TLV_DOMAIN_INFO, domainInfo, currLen, tlvCount);

    AppSpawnMsgAccessToken token = {12345678};  // 12345678
    ADD_TLV(TLV_ACCESS_TOKEN_INFO, token, currLen, tlvCount);

    AppSpawnMsgInternetInfo internetInfo = {0, 1};
    ADD_TLV(TLV_INTERNET_INFO, internetInfo, currLen, tlvCount);

    // add bundle info
    AppBundleInfo info = {};
    APPSPAWN_CHECK_ONLY_EXPER(strcpy_s(info.bundleName, sizeof(info.bundleName), "test-bundleName") == 0,
        return -1);
    info.bundleIndex = 100;  // 100 test index
    ADD_TLV(TLV_BUNDLE_INFO, info, currLen, tlvCount);

    // add dac
    AppDacInfo  dacInfo = {};
    dacInfo.uid = 20010029; // 20010029 test uid
    dacInfo.gid = 20010029; // 20010029 test gid
    dacInfo.gidCount = 2; // 2 count
    dacInfo.gidTable[0] = 20010029; // 20010029 test gid
    dacInfo.gidTable[1] = 20010030; // 20010030 test gid
    ADD_TLV(TLV_DAC_INFO, dacInfo, currLen, tlvCount);
    realLen = currLen;
    return 0;
}

AppSpawnContent *AppSpawnTestHelper::StartSpawnServer(std::string &cmd, CmdArgs *&args)
{
    args = AppSpawnTestHelper::ToCmdList(cmd.c_str());
    APPSPAWN_CHECK(args != nullptr, return nullptr, "Failed to alloc args");

    static const std::unordered_map<std::string, AppSpawnStartArg> spawnConfigs = {
        {"appspawn", {MODE_FOR_APP_SPAWN, MODULE_APPSPAWN, APPSPAWN_SOCKET_NAME, APPSPAWN_SERVER_NAME, 1}},
        {"nwebspawn", {MODE_FOR_NWEB_SPAWN, MODULE_NWEBSPAWN,  NWEBSPAWN_SOCKET_NAME, NWEBSPAWN_SERVER_NAME, 1}},
        {"nativespawn", {MODE_FOR_NATIVE_SPAWN, MODULE_NATIVESPAWN, NATIVESPAWN_SOCKET_NAME,
            NATIVESPAWN_SERVER_NAME, 1}},
        {"cjappspawn", {MODE_FOR_CJAPP_SPAWN, MODULE_APPSPAWN, CJAPPSPAWN_SOCKET_NAME, CJAPPSPAWN_SERVER_NAME, 1}},
        {"hybridspawn", {MODE_FOR_HYBRID_SPAWN, MODULE_HYBRIDSPAWN, HYBRIDSPAWN_SOCKET_NAME,
            HYBRIDSPAWN_SERVER_NAME, 1}},
        {"app_cold", {MODE_FOR_APP_COLD_RUN, MODULE_APPSPAWN, APPSPAWN_SOCKET_NAME, APPSPAWN_SERVER_NAME, 0}},
        {"nweb_cold", {MODE_FOR_NWEB_COLD_RUN, MODULE_NWEBSPAWN, NWEBSPAWN_SOCKET_NAME, NWEBSPAWN_SERVER_NAME, 0}},
        {"native_cold", {MODE_FOR_NATIVE_COLD_RUN, MODULE_NATIVESPAWN, NATIVESPAWN_SOCKET_NAME,
            NATIVESPAWN_SERVER_NAME, 0}},
        {"hybrid_cold", {MODE_FOR_HYBRID_COLD_RUN, MODULE_HYBRIDSPAWN, HYBRIDSPAWN_SOCKET_NAME,
            HYBRIDSPAWN_SERVER_NAME, 0}},
        {"cj_app_cold", {MODE_FOR_CJAPP_COLD_RUN, MODULE_APPSPAWN, CJAPPSPAWN_SOCKET_NAME, CJAPPSPAWN_SERVER_NAME, 0}}
    };

    AppSpawnStartArg startRrg = {};
    std::string spawnType = (args->argc <= MODE_VALUE_INDEX) ? args->argv[0] : args->argv[MODE_VALUE_INDEX];

    auto it = spawnConfigs.find(spawnType);
    if (it != spawnConfigs.end()) {
        const auto &config = it->second;
        if (spawnType.find("_cold") != std::string::npos) {
            APPSPAWN_CHECK(args->argc >= ARG_NULL, free(args);
                return nullptr, "Invalid arg for cold start %{public}d", args->argc);
        }
        startRrg.mode = config.mode;
        startRrg.moduleType = config.moduleType;
        startRrg.socketName = config.socketName;
        startRrg.serviceName = config.serviceName;
        startRrg.initArg = config.initArg;
    }

    APPSPAWN_LOGV("Start service %{public}s", startRrg.serviceName);
    AppSpawnContent *content = StartSpawnService(&startRrg, APP_LEN_PROC_NAME, args->argc, args->argv);
    if (content == nullptr) {
        free(args);
    }
    return content;
}
}  // namespace OHOS

MODULE_CONSTRUCTOR(void)
{
    MakeDirRec(APPSPAWN_MSG_DIR "appspawn", 0771, 1);
    MakeDirRec(APPSPAWN_MSG_DIR "hybridspawn", 0771, 1);
    MakeDirRec(APPSPAWN_MSG_DIR "nwebspawn", 0771, 1);
    MakeDirRec(APPSPAWN_MSG_DIR "nativespawn", 0771, 1);
}