/*
 * Copyright (c) 2025-2026 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 <map>
#include "ani_settings_observer.h"
#include "napi_settings_log.h"
#include "abs_shared_result_set.h"
#include "values_bucket.h"
#include "ani_base_context.h"
#include "os_account_manager.h"

using namespace OHOS::AppExecFwk;
using namespace OHOS::DataShare;
using namespace OHOS::AccountSA;

namespace OHOS {
namespace Settings {
std::map<std::string, sptr<SettingsObserver>> g_observerMap;
std::mutex g_observerMapMutex;

SettingsObserver::~SettingsObserver()
{
    if (this->cbInfo == nullptr) {
        return;
    }
    delete this->cbInfo;
    this->cbInfo = nullptr;
}

bool IsExistObserver(SettingsObserver *settingsObserver)
{
    for (auto it = g_observerMap.begin(); it != g_observerMap.end(); ++it) {
        if (&(*(it->second)) == settingsObserver) {
            return true;
        }
    }
    return false;
}

ani_object BoolToObject(ani_env *env, bool value)
{
    ani_object aniObject = nullptr;
    ani_boolean boolValue = static_cast<bool>(value);
    const char *className = "std.core.Boolean";
    ani_class aniClass;
    if (ANI_OK != env->FindClass(className, &aniClass)) {
        SETTING_LOG_ERROR("Not found '%{public}s.'", className);
        return aniObject;
    }

    ani_method personInfoCtor;
    if (ANI_OK != env->Class_FindMethod(aniClass, "<ctor>", "z:", &personInfoCtor)) {
        SETTING_LOG_ERROR("Class_GetMethod Failed '%{public}s' <ctor>.", className);
        return aniObject;
    }

    if (ANI_OK != env->Object_New(aniClass, personInfoCtor, &aniObject, boolValue)) {
        SETTING_LOG_ERROR("Object_New Failed '%{public}s' <ctor>.", className);
    }

    return aniObject;
}

ani_object CreateError(ani_env *env, const std::string &msg)
{
    ani_class cls{};
    ani_method method{};
    ani_object obj = nullptr;
    ani_status status = ANI_ERROR;
    if (env == nullptr) {
        SETTING_LOG_ERROR("null env");
        return nullptr;
    }

    ani_string aniMsg = nullptr;
    if ((status = env->String_NewUTF8(msg.c_str(), msg.size(), &aniMsg)) != ANI_OK) {
        SETTING_LOG_ERROR("String_NewUTF8 failed %{public}d", status);
        return nullptr;
    }

    ani_ref undefRef;
    if ((status = env->GetUndefined(&undefRef)) != ANI_OK) {
        SETTING_LOG_ERROR("GetUndefined failed %{public}d", status);
        return nullptr;
    }

    if ((status = env->FindClass("std.core.Error", &cls)) != ANI_OK) {
        SETTING_LOG_ERROR("FindClass failed %{public}d", status);
        return nullptr;
    }
    if ((status = env->Class_FindMethod(cls, "<ctor>", "C{std.core.String}C{std.core.ErrorOptions}:", &method)) !=
        ANI_OK) {
        SETTING_LOG_ERROR("Class_FindMethod failed %{public}d", status);
        return nullptr;
    }

    if ((status = env->Object_New(cls, method, &obj, aniMsg, undefRef)) != ANI_OK) {
        SETTING_LOG_ERROR("Object_New failed %{public}d", status);
        return nullptr;
    }
    return obj;
}

ani_object CreateBusinessError(ani_env *env, int code, const std::string &msg)
{
    ani_class cls{};
    ani_method method{};
    ani_object obj = nullptr;
    ani_status status = ANI_ERROR;
    if (env == nullptr) {
        SETTING_LOG_ERROR("null env");
        return nullptr;
    }
    if ((status = env->FindClass("@ohos.base.BusinessError", &cls)) != ANI_OK) {
        SETTING_LOG_ERROR("FindClass failed %{public}d", status);
        return nullptr;
    }
    if ((status = env->Class_FindMethod(cls, "<ctor>", "iC{std.core.Error}:", &method)) != ANI_OK) {
        SETTING_LOG_ERROR("Class_FindMethod failed %{public}d", status);
        return nullptr;
    }
    ani_object error = CreateError(env, msg);
    if (error == nullptr) {
        SETTING_LOG_ERROR("error null");
        return nullptr;
    }
    ani_double dCode(code);
    if ((status = env->Object_New(cls, method, &obj, dCode, error)) != ANI_OK) {
        SETTING_LOG_ERROR("Object_New failed %{public}d", status);
        return nullptr;
    }
    return obj;
}

void SettingsObserver::OnChange()
{
    SETTING_LOG_INFO("n_s_o_c");
    std::lock_guard<std::mutex> lockGuard(g_observerMapMutex);
    if (this->cbInfo == nullptr) {
        SETTING_LOG_ERROR("%{public}s, cbInfo is null.", __func__);
        return;
    }

    SETTING_LOG_INFO("n_s_o_c_a");
    SettingsObserver *settingsObserver = reinterpret_cast<SettingsObserver *>(this);
    if (!IsExistObserver(settingsObserver) || settingsObserver == nullptr || settingsObserver->cbInfo == nullptr ||
        settingsObserver->toBeDelete) {
        SETTING_LOG_ERROR("ani_call_function: cbInfo invalid.");
        return;
    }

    ani_env *env = nullptr;
    ani_options aniArgs{0, nullptr};
    if (settingsObserver->vm_ == nullptr) {
        SETTING_LOG_ERROR("VM is nullptr");
        return;
    }
    if (ANI_OK != settingsObserver->vm_->AttachCurrentThread(&aniArgs, ANI_VERSION_1, &env)) {
        SETTING_LOG_ERROR("AttachCurrentThread failed");
        if (ANI_OK != settingsObserver->vm_->GetEnv(ANI_VERSION_1, &env)) {
            SETTING_LOG_ERROR("GetEnv failed");
            return;
        }
    }

    ani_size nr_refs = 16;
    env->CreateLocalScope(nr_refs);

    ani_ref result;
    auto fnObj = static_cast<ani_fn_object>(settingsObserver->callback_);
    if (fnObj == nullptr) {
        SETTING_LOG_ERROR("%{public}s: fnObj == nullptr", __func__);
        return;
    }

    std::vector<ani_ref> args;
    std::string msg = "";
    ani_object errObj = CreateBusinessError(env, 802, msg);

    ani_ref dataObj = BoolToObject(env, false);
    args.push_back(errObj);
    args.push_back(dataObj);

    ani_status callStatus = env->FunctionalObject_Call(fnObj, args.size(), args.data(), &result);
    if (ANI_OK != callStatus) {
        SETTING_LOG_ERROR("ani_call_function failed status : %{public}d", callStatus);
        return;
    }

    SETTING_LOG_INFO("%{public}s, ani_eventhandler_work success.", __func__);
    env->DestroyLocalScope();
    settingsObserver->vm_->DetachCurrentThread();
}

int GetObserverIdStr()
{
    int currentUserId = -1;
    OHOS::AccountSA::OsAccountManager::GetOsAccountLocalIdFromProcess(currentUserId);
    int tmpId = 100;
    if (currentUserId > 0) {
        tmpId = currentUserId;
        SETTING_LOG_INFO("userId is %{public}d", tmpId);
    } else if (currentUserId == 0) {
        OHOS::AccountSA::OsAccountManager::GetForegroundOsAccountLocalId(currentUserId);
        tmpId = currentUserId;
        SETTING_LOG_INFO("user0 userId is %{public}d", tmpId);
    } else {
        SETTING_LOG_INFO("%{public}s, user id 100.", __func__);
    }
    return tmpId;
}

void DeleteAsyncCallbackInfo(AsyncCallbackInfo *asyncCallbackInfo)
{
    if (asyncCallbackInfo != nullptr) {
        delete asyncCallbackInfo;
        asyncCallbackInfo = nullptr;
    }
}

ani_boolean ani_settings_register_observer(
    ani_env *env, ani_object context, ani_string name, ani_string domainName, ani_object observer)
{
    SETTING_LOG_INFO("n_s_r_o");

    ani_boolean stageMode = false;
    ani_status status = OHOS::AbilityRuntime::IsStageContext(env, context, stageMode);
    if (status != ANI_OK) {
        SETTING_LOG_ERROR("%{public}s, not stage mode.", __func__);
        return false;
    }
    AsyncCallbackInfo *callbackInfo = new AsyncCallbackInfo();
    if (callbackInfo == nullptr) {
        SETTING_LOG_ERROR("%{public}s, failed to get callbackInfo.", __func__);
        return false;
    }

    callbackInfo->env = env;
    ani_vm *vm = nullptr;
    if (env->GetVM(&vm) != ANI_OK) {
        SETTING_LOG_ERROR("GetVM failed");
        DeleteAsyncCallbackInfo(callbackInfo);
        return false;
    }
    callbackInfo->key = unwrap_string_from_js(env, name);
    callbackInfo->tableName = unwrap_string_from_js(env, domainName);
    env->GlobalReference_Create(observer, &(callbackInfo->callbackRef));

    std::lock_guard<std::mutex> lockGuard(g_observerMapMutex);
    if (g_observerMap.find(callbackInfo->key) != g_observerMap.end() && g_observerMap[callbackInfo->key] != nullptr) {
        SETTING_LOG_INFO("%{public}s, already registered.", __func__);
        env->GlobalReference_Delete(callbackInfo->callbackRef);
        DeleteAsyncCallbackInfo(callbackInfo);
        return false;
    }

    auto dataShareHelper = getDataShareHelper(env, context, callbackInfo->tableName);
    if (dataShareHelper == nullptr) {
        env->GlobalReference_Delete(callbackInfo->callbackRef);
        DeleteAsyncCallbackInfo(callbackInfo);
        return false;
    }

    std::string strUri = GetStageUriStr(callbackInfo->tableName, GetObserverIdStr(), callbackInfo->key);
    OHOS::Uri uri(strUri);
    sptr<SettingsObserver> settingsObserver =
        sptr<SettingsObserver>(new (std::nothrow) SettingsObserver(vm, observer, callbackInfo));
    if (settingsObserver != nullptr && settingsObserver->callback_ != nullptr) {
        env->GlobalReference_Create(observer, &(settingsObserver->callback_));
    }

    g_observerMap[callbackInfo->key] = settingsObserver;
    dataShareHelper->RegisterObserver(uri, settingsObserver);
    dataShareHelper->Release();
    return true;
}

ani_boolean ani_settings_unregister_observer(ani_env *env, ani_object context, ani_string name, ani_string domainName)
{
    SETTING_LOG_INFO("n_s_u_o");

    ani_boolean stageMode = false;
    ani_status status = OHOS::AbilityRuntime::IsStageContext(env, context, stageMode);
    if (status != ANI_OK) {
        SETTING_LOG_ERROR("%{public}s, not stage mode.", __func__);
        return false;
    }

    std::string key = unwrap_string_from_js(env, name);
    std::string tableName = unwrap_string_from_js(env, domainName);

    std::lock_guard<std::mutex> lockGuard(g_observerMapMutex);
    if (g_observerMap.find(key) == g_observerMap.end()) {
        SETTING_LOG_ERROR("%{public}s, null.", __func__);
        return false;
    }

    if (g_observerMap[key] == nullptr) {
        g_observerMap.erase(key);
        return false;
    }

    auto dataShareHelper = getDataShareHelper(env, context, tableName);
    if (dataShareHelper == nullptr) {
        SETTING_LOG_ERROR("%{public}s, data share is null.", __func__);
        return false;
    }
    std::string strUri = GetStageUriStr(tableName, GetObserverIdStr(), key);
    OHOS::Uri uri(strUri);

    env->GlobalReference_Delete(g_observerMap[key]->cbInfo->callbackRef);
    env->GlobalReference_Delete(g_observerMap[key]->callback_);
    dataShareHelper->UnregisterObserver(uri, g_observerMap[key]);
    dataShareHelper->Release();
    g_observerMap[key]->toBeDelete = true;
    g_observerMap[key] = nullptr;
    g_observerMap.erase(key);
    return true;
}
}  // namespace Settings
}  // namespace OHOS