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

#include <atomic>
#include <cstddef>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <utility>

#include "iam_check.h"
#include "iam_logger.h"
#include "iam_para2str.h"

#include "event_manager_adapter.h"
#include "resident_task_runner.h"
#include "temporary_task_runner.h"
#include "xcollie_helper.h"

#define LOG_TAG "CDA_SA"
#define LOG_FILE_ID LOG_FILE_TASK_RUNNER_MANAGER

namespace OHOS {
namespace UserIam {
namespace CompanionDeviceAuth {

namespace {
thread_local bool g_runningOnDefaultTaskRunner = false;
constexpr uint32_t TASK_BLOCK_MONITOR_TIMEOUT = 70;
} // namespace

TaskRunnerManager &TaskRunnerManager::GetInstance()
{
    static TaskRunnerManager defaultInstance;
    return defaultInstance;
}

bool TaskRunnerManager::RunningOnDefaultTaskRunner() const
{
    return g_runningOnDefaultTaskRunner;
}

void TaskRunnerManager::SetRunningOnDefaultTaskRunner(bool value)
{
    g_runningOnDefaultTaskRunner = value;
}

void TaskRunnerManager::CheckRunningOnResidentThread(const char *caller)
{
    ENSURE_OR_RETURN(caller != nullptr);
    if (!RunningOnDefaultTaskRunner()) {
        IAM_LOGE("FATAL: %{public}s not running on resident thread!", caller);
        std::string faultInfo = std::string(caller) + ": Not running on resident thread\nCallStack:\n" + GetCallStack();
        PostTaskOnResident([faultInfo = std::move(faultInfo)]() {
            ReportSystemFault("THREAD_CHECK_FAILED", "THREAD_CHECK", faultInfo);
        });
    }
}

TaskRunnerManager::TaskRunnerManager()
{
    auto taskRunner = std::make_shared<ResidentTaskRunner>();
    ENSURE_OR_RETURN(taskRunner != nullptr);
    taskRunnerMap_.emplace(RESIDENT_TASK_RUNNER_NAME, taskRunner);
}

bool TaskRunnerManager::CreateTaskRunner(const std::string &name)
{
    std::lock_guard<std::recursive_mutex> lock(mutex_);
    if (taskRunnerMap_.find(name) != taskRunnerMap_.end()) {
        IAM_LOGE("task runner %{public}s already exists", name.c_str());
        return false;
    }
    auto taskRunner = std::make_shared<TemporaryTaskRunner>(name, true);
    ENSURE_OR_RETURN_VAL(taskRunner != nullptr, false);
    taskRunnerMap_.emplace(name, taskRunner);
    IAM_LOGI("task runner %{public}s create success", name.c_str());
    return true;
}

void TaskRunnerManager::DestroyTaskRunner(const std::string &name)
{
    if (name == RESIDENT_TASK_RUNNER_NAME) {
        IAM_LOGE("task runner %{public}s cannot destroy", name.c_str());
        return;
    }

    std::lock_guard<std::recursive_mutex> lock(mutex_);

    if (taskRunnerMap_.find(name) == taskRunnerMap_.end()) {
        IAM_LOGE("task runner %{public}s not exist", name.c_str());
        return;
    }

    auto taskRunner = taskRunnerMap_[name];
    ENSURE_OR_RETURN(taskRunner != nullptr);
    taskRunner->PostTask([name]() {
        auto taskRunner = TaskRunnerManager::GetInstance().GetTaskRunner(RESIDENT_TASK_RUNNER_NAME);
        ENSURE_OR_RETURN(taskRunner != nullptr);
        taskRunner->PostTask([name]() {
            TaskRunnerManager::GetInstance().DeleteTaskRunner(name);
            IAM_LOGI("task runner %{public}s deleted", name.c_str());
        });
        IAM_LOGI("task runner %{public}s delete task posted", name.c_str());
    });
    taskRunner->Suspend();
    IAM_LOGI("task runner %{public}s destroy started", name.c_str());
}

void TaskRunnerManager::DeleteTaskRunner(const std::string &name)
{
    if (name == RESIDENT_TASK_RUNNER_NAME) {
        IAM_LOGE("task runner %{public}s cannot delete", name.c_str());
        return;
    }

    std::lock_guard<std::recursive_mutex> lock(mutex_);
    if (taskRunnerMap_.find(name) == taskRunnerMap_.end()) {
        IAM_LOGE("task runner %{public}s not exist", name.c_str());
        return;
    }

    taskRunnerMap_.erase(name);
    IAM_LOGI("task runner %{public}s is deleted", name.c_str());
}

std::shared_ptr<TaskRunner> TaskRunnerManager::GetTaskRunner(const std::string &name)
{
    std::lock_guard<std::recursive_mutex> lock(mutex_);
    if (taskRunnerMap_.find(name) == taskRunnerMap_.end()) {
        IAM_LOGE("task runner %{public}s not exist", name.c_str());
        return nullptr;
    }
    return taskRunnerMap_[name];
}

void TaskRunnerManager::PostTask(const std::string &name, std::function<void()> &&task)
{
    std::lock_guard<std::recursive_mutex> lock(mutex_);
    if (taskRunnerMap_.find(name) == taskRunnerMap_.end()) {
        IAM_LOGE("task runner %{public}s not exist", name.c_str());
        return;
    }
    auto taskRunner = taskRunnerMap_[name];
    ENSURE_OR_RETURN(taskRunner != nullptr);

    auto taskBlockMonitor = std::make_shared<XCollieHelper>("taskBlockMonitor", TASK_BLOCK_MONITOR_TIMEOUT, false);
    ENSURE_OR_RETURN(taskBlockMonitor != nullptr);

    taskRunner->PostTask([taskRunner, taskBlockMonitor, originalTask = std::move(task)]() mutable { originalTask(); });
}

void TaskRunnerManager::PostTaskOnResident(std::function<void()> &&task)
{
    PostTask(RESIDENT_TASK_RUNNER_NAME, std::move(task));
}

void TaskRunnerManager::PostTaskOnTemporary(const std::string &name, std::function<void()> &&task)
{
    static std::atomic<uint32_t> runnerSerial = 1;
    constexpr size_t maxConcurrentRunners = 9;

    std::lock_guard<std::recursive_mutex> lock(mutex_);
    size_t mapSize = taskRunnerMap_.size();
    if (mapSize >= maxConcurrentRunners) {
        IAM_LOGE("too many concurrent temporary task runners %{public}zu, reject '%{public}s'", mapSize, name.c_str());
        return;
    }

    uint32_t serial = runnerSerial.fetch_add(1);
    std::string thread_name = name + "_" + std::to_string(serial);
    CreateTaskRunner(thread_name);
    PostTask(thread_name, std::move(task));
    DestroyTaskRunner(thread_name);
}

} // namespace CompanionDeviceAuth
} // namespace UserIam
} // namespace OHOS