/*
 * Copyright (C) 2024-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 <charconv>
#include "accessibility_datashare_helper.h"

#ifdef OHOS_BUILD_ENABLE_DATA_SHARE
#include "datashare_errno.h"
#include "datashare_predicates.h"
#include "datashare_result_set.h"
#include "datashare_values_bucket.h"
#endif
#include "hilog_wrapper.h"
#include "ipc_skeleton.h"
#include "iservice_registry.h"
#include "system_ability_definition.h"
#include "uri.h"
#include "utils.h"

namespace OHOS {
namespace Accessibility {
ffrt::mutex AccessibilityDatashareHelper::observerMutex_;
namespace {
#ifdef OHOS_BUILD_ENABLE_DATA_SHARE
    constexpr int32_t INDEX = 0;
    const std::string SETTING_COLUMN_KEYWORD = "KEYWORD";
    const std::string SETTING_COLUMN_VALUE = "VALUE";
#endif
    constexpr int32_t DECIMAL_NOTATION = 10;
    const std::string SETTINGS_DATA_EXT_URI = "com.ohos.settingsdata.DataAbility";
    const std::string SETTING_GLOBAL_URI = "datashare:///com.ohos.settingsdata/entry/settingsdata/SETTINGSDATA";
    const std::string SETTING_SYSTEM_URI = "datashare:///com.ohos.settingsdata/entry/settingsdata/USER_SETTINGSDATA_";
    const std::string SETTING_SECURE_URI =
        "datashare:///com.ohos.settingsdata/entry/settingsdata/USER_SETTINGSDATA_SECURE_";
}

AccessibilityDatashareHelper::AccessibilityDatashareHelper(DATASHARE_TYPE type, int32_t accountId)
    :type_(type), accountId_(accountId)
{
    HILOG_DEBUG();
}

AccessibilityDatashareHelper::~AccessibilityDatashareHelper()
{
#ifdef OHOS_BUILD_ENABLE_DATA_SHARE
    std::unique_lock<ffrt::shared_mutex> wlock(proxyMutex_);
    if (dataShareHelper_ != nullptr) {
        DestoryDatashareHelper(dataShareHelper_);
        dataShareHelper_ = nullptr;
    }
#endif
}

std::string AccessibilityDatashareHelper::GetStringValue(const std::string& key, const std::string& defaultValue,
    const bool readOnlyFlag)
{
    std::string resultStr = defaultValue;
#ifdef OHOS_BUILD_ENABLE_DATA_SHARE
    std::string callingIdentity = IPCSkeleton::ResetCallingIdentity();
    std::shared_ptr<DataShare::DataShareResultSet> resultSet = nullptr;
    do {
        std::vector<std::string> columns = { SETTING_COLUMN_VALUE };
        DataShare::DataSharePredicates predicates;
        Uri uri(AssembleUri(key));
        int32_t count = 0;
        std::shared_lock<ffrt::shared_mutex> rlock(proxyMutex_);
        predicates.EqualTo(SETTING_COLUMN_KEYWORD, key);
        if (dataShareHelper_ == nullptr) {
            break;
        }
        resultSet = dataShareHelper_->Query(uri, predicates, columns);
        if (resultSet == nullptr) {
            Utils::RecordDatashareInteraction(A11yDatashareValueType::GET);
            break;
        }
        resultSet->GetRowCount(count);
        if (count == 0) {
            if (!readOnlyFlag) {
                RetError ret = PutStringValue(key, defaultValue);
                HILOG_INFO("put default key %{public}s, ret = %{public}d", key.c_str(), static_cast<int32_t>(ret));
            }
            break;
        }
        resultSet->GoToRow(INDEX);
        int32_t rtn = resultSet->GetString(INDEX, resultStr);
        if (rtn  != DataShare::E_OK) {
            break;
        }
    } while (0);
    if (resultSet != nullptr) {
        resultSet->Close();
        resultSet = nullptr;
    }
    IPCSkeleton::SetCallingIdentity(callingIdentity);
#endif
    return resultStr;
}

int64_t AccessibilityDatashareHelper::GetLongValue(const std::string& key, const int64_t& defaultValue,
    const bool readOnlyFlag)
{
    int64_t result = defaultValue;
    std::string valueStr = GetStringValue(key, std::to_string(result), readOnlyFlag);
    if (!valueStr.empty()) {
        int64_t num;
        auto [ptr, ec] = std::from_chars(valueStr.data(), valueStr.data() + valueStr.size(), num);
        if (ec == std::errc()) {
            result = num;
        }
    }
    return result;
}

int32_t AccessibilityDatashareHelper::GetIntValue(const std::string& key, const int32_t& defaultValue,
    const bool readOnlyFlag)
{
    int64_t valueLong = GetLongValue(key, defaultValue, readOnlyFlag);
    return static_cast<int32_t>(valueLong);
}

bool AccessibilityDatashareHelper::GetBoolValue(const std::string& key, const bool& defaultValue,
    const bool readOnlyFlag)
{
    bool result = defaultValue;
    std::string valueStr = GetStringValue(key, result ? "1" : "0", readOnlyFlag);
    if (valueStr != "") {
        result = (valueStr == "1" || valueStr == "true");
    }
    return result;
}

float AccessibilityDatashareHelper::GetFloatValue(const std::string& key, const float& defaultValue,
    const bool readOnlyFlag)
{
    float result = defaultValue;
    std::string valueStr = GetStringValue(key, std::to_string(result), readOnlyFlag);
    if (valueStr != "") {
        result = Utils::StringToFloat(valueStr, defaultValue);
    }
    return result;
}

uint64_t AccessibilityDatashareHelper::GetUnsignedLongValue(const std::string& key, const uint64_t& defaultValue,
    const bool readOnlyFlag)
{
    uint64_t result = defaultValue;
    std::string valueStr = GetStringValue(key, std::to_string(result), readOnlyFlag);
    if (!valueStr.empty()) {
        uint64_t num;
        auto [ptr, ec] = std::from_chars(valueStr.data(), valueStr.data() + valueStr.size(), num);
        if (ec == std::errc()) {
            result = num;
        }
    }
    return result;
}
 

RetError AccessibilityDatashareHelper::PutStringValue(const std::string& key, const std::string& value, bool needNotify)
{
    std::string callingIdentity = IPCSkeleton::ResetCallingIdentity();
    RetError rtn = RET_OK;
#ifdef OHOS_BUILD_ENABLE_DATA_SHARE
    do {
        std::shared_lock<ffrt::shared_mutex> rlock(proxyMutex_);
        if (dataShareHelper_ == nullptr) {
            rtn = RET_ERR_NULLPTR;
            break;
        }
        DataShare::DataShareValueObject keyObj(key);
        DataShare::DataShareValueObject valueObj(value);
        DataShare::DataShareValuesBucket bucket;
        bucket.Put(SETTING_COLUMN_KEYWORD, keyObj);
        bucket.Put(SETTING_COLUMN_VALUE, valueObj);
        DataShare::DataSharePredicates predicates;
        predicates.EqualTo(SETTING_COLUMN_KEYWORD, key);
        Uri uri(AssembleUri(key));
        if (dataShareHelper_->Update(uri, predicates, bucket) <= 0) {
            HILOG_DEBUG("no data exist, insert one row");
            auto ret = dataShareHelper_->Insert(uri, bucket);
            if (ret <= 0) {
                rtn = RET_ERR_FAILED;
            }
            HILOG_INFO("helper insert %{public}s ret(%{public}d).", key.c_str(), static_cast<int>(ret));
        }
        if (needNotify) {
            dataShareHelper_->NotifyChange(AssembleUri(key));
        }
    } while (0);
    IPCSkeleton::SetCallingIdentity(callingIdentity);
#endif
    return rtn;
}

RetError AccessibilityDatashareHelper::PutUnsignedLongValue(const std::string& key, uint64_t value, bool needNotify)
{
    return PutStringValue(key, std::to_string(value), needNotify);
}

RetError AccessibilityDatashareHelper::PutIntValue(const std::string& key, int32_t value, bool needNotify)
{
    return PutStringValue(key, std::to_string(value), needNotify);
}

RetError AccessibilityDatashareHelper::PutLongValue(const std::string& key, int64_t value, bool needNotify)
{
    return PutStringValue(key, std::to_string(value), needNotify);
}

RetError AccessibilityDatashareHelper::PutBoolValue(const std::string& key, bool value, bool needNotify)
{
    std::string valueStr = value ? "1" : "0";
    return PutStringValue(key, valueStr, needNotify);
}

RetError AccessibilityDatashareHelper::PutFloatValue(const std::string& key, float value, bool needNotify)
{
    return PutStringValue(key, std::to_string(value), needNotify);
}

RetError AccessibilityDatashareHelper::Initialize(int32_t systemAbilityId)
{
    auto systemAbilityManager = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    if (systemAbilityManager == nullptr) {
        HILOG_ERROR("get sam return nullptr");
        return RET_ERR_NULLPTR;
    }
    auto remoteObj = systemAbilityManager->GetSystemAbility(systemAbilityId);
    if (remoteObj == nullptr) {
        HILOG_ERROR("Get remoteObj return nullptr, systemAbilityId=%{public}d", systemAbilityId);
        return RET_ERR_NULLPTR;
    }
    remoteObj_ = remoteObj;
    switch (type_) {
        case DATASHARE_TYPE::GLOBAL:
            uriProxyStr_ = SETTING_GLOBAL_URI + "?Proxy=true";
            break;
        case DATASHARE_TYPE::SYSTEM:
            uriProxyStr_ = SETTING_SYSTEM_URI + std::to_string(accountId_) + "?Proxy=true";
            break;
        case DATASHARE_TYPE::SECURE:
            uriProxyStr_ = SETTING_SECURE_URI + std::to_string(accountId_) + "?Proxy=true";
            break;
        default:
            uriProxyStr_ = SETTING_GLOBAL_URI + "?Proxy=true";
            HILOG_WARN("undefined DATASHARE_TYPE, use global table");
            break;
    }
#ifdef OHOS_BUILD_ENABLE_DATA_SHARE
    std::unique_lock<ffrt::shared_mutex> wlock(proxyMutex_);
    dataShareHelper_ = CreateDatashareHelper();
    if (dataShareHelper_ == nullptr) {
        HILOG_ERROR("create dataShareHelper_ failed");
        return RET_ERR_NULLPTR;
    }
#endif
    return RET_OK;
}

void AccessibilityDatashareHelper::Uninitialize()
{
#ifdef OHOS_BUILD_ENABLE_DATA_SHARE
    std::unique_lock<ffrt::shared_mutex> wlock(proxyMutex_);
    if (dataShareHelper_ != nullptr) {
        DestoryDatashareHelper(dataShareHelper_);
        dataShareHelper_ = nullptr;
    }
#endif
}

sptr<AccessibilitySettingObserver> AccessibilityDatashareHelper::CreateObserver(const std::string& key,
    AccessibilitySettingObserver::UpdateFunc& func)
{
    sptr<AccessibilitySettingObserver> observer = new AccessibilitySettingObserver();
    observer->SetKey(key);
    observer->SetUpdateFunc(func);
    return observer;
}

RetError AccessibilityDatashareHelper::RegisterObserver(const sptr<AccessibilitySettingObserver>& observer)
{
    std::string callingIdentity = IPCSkeleton::ResetCallingIdentity();
    auto uri = AssembleUri(observer->GetKey());
#ifdef OHOS_BUILD_ENABLE_DATA_SHARE
    std::shared_lock<ffrt::shared_mutex> rlock(proxyMutex_);
    if (dataShareHelper_ == nullptr) {
        IPCSkeleton::SetCallingIdentity(callingIdentity);
        return RET_ERR_NULLPTR;
    }
    dataShareHelper_->RegisterObserver(uri, observer);
#endif
    IPCSkeleton::SetCallingIdentity(callingIdentity);
    HILOG_DEBUG("succeed to register observer of uri=%{public}s", uri.ToString().c_str());
    return RET_OK;
}

RetError AccessibilityDatashareHelper::RegisterObserver(const std::string& key,
    AccessibilitySettingObserver::UpdateFunc& func)
{
    sptr<AccessibilitySettingObserver> observer = CreateObserver(key, func);
    if (observer == nullptr) {
        return RET_ERR_NULLPTR;
    }
    auto iter = settingObserverMap_.find(key);
    if (iter != settingObserverMap_.end() && iter->second != nullptr) {
        HILOG_INFO("observer of key = %{public}s already exist", key.c_str());
        return RET_OK;
    }
    if (RegisterObserver(observer) != ERR_OK) {
        return RET_ERR_NULLPTR;
    }
    std::lock_guard<ffrt::mutex> lock(observerMutex_);
    settingObserverMap_.insert(std::make_pair(key, observer));
    return RET_OK;
}

RetError AccessibilityDatashareHelper::UnregisterObserver(const sptr<AccessibilitySettingObserver>& observer)
{
    std::string callingIdentity = IPCSkeleton::ResetCallingIdentity();
    auto uri = AssembleUri(observer->GetKey());
#ifdef OHOS_BUILD_ENABLE_DATA_SHARE
    std::shared_lock<ffrt::shared_mutex> rlock(proxyMutex_);
    if (dataShareHelper_ == nullptr) {
        IPCSkeleton::SetCallingIdentity(callingIdentity);
        return RET_ERR_NULLPTR;
    }
    dataShareHelper_->UnregisterObserver(uri, observer);
#endif
    IPCSkeleton::SetCallingIdentity(callingIdentity);
    HILOG_DEBUG("succeed to unregister observer of uri=%{public}s", uri.ToString().c_str());
    return RET_OK;
}

RetError AccessibilityDatashareHelper::UnregisterObserver(const std::string& key)
{
    std::lock_guard<ffrt::mutex> lock(observerMutex_);
    auto iter = settingObserverMap_.find(key);
    if (iter != settingObserverMap_.end() && iter->second != nullptr) {
        sptr<AccessibilitySettingObserver> observer = iter->second;
        if (UnregisterObserver(observer) == ERR_OK) {
            settingObserverMap_.erase(iter);
            HILOG_DEBUG("succeed to unregister observer of key %{public}s", key.c_str());
            return RET_OK;
        } else {
            settingObserverMap_.erase(iter);
            HILOG_WARN("failed to unregister observer of key %{public}s", key.c_str());
            return RET_ERR_FAILED;
        }
    }
    HILOG_WARN("failed to find the key %{public}s", key.c_str());
    return RET_ERR_FAILED;
}

RetError AccessibilityDatashareHelper::ClearObservers()
{
    std::lock_guard<ffrt::mutex> lock(observerMutex_);
    RetError ret = RET_OK;
    for (const auto& [key, observer] : settingObserverMap_) {
        if (UnregisterObserver(observer) != ERR_OK) {
            HILOG_ERROR("failed to unregister observer %{public}s", key.c_str());
            ret = RET_ERR_FAILED;
        }
    }
    settingObserverMap_.clear();
    return ret;
}

#ifdef OHOS_BUILD_ENABLE_DATA_SHARE
std::shared_ptr<DataShare::DataShareHelper> AccessibilityDatashareHelper::CreateDatashareHelper()
{
    if (remoteObj_ == nullptr) {
        return nullptr;
    }
    std::pair<int, std::shared_ptr<DataShare::DataShareHelper>> ret = DataShare::DataShareHelper::Create(remoteObj_,
        uriProxyStr_, SETTINGS_DATA_EXT_URI);
    HILOG_INFO("create helper ret = %{public}d, uri=%{public}s", ret.first, uriProxyStr_.c_str());
    if (ret.first != DataShare::E_OK || ret.second == nullptr) {
        Utils::RecordUnavailableEvent(A11yUnavailableEvent::READ_EVENT, A11yError::ERROR_READ_FAILED);
        return nullptr;
    }
    return ret.second;
}

bool AccessibilityDatashareHelper::DestoryDatashareHelper(std::shared_ptr<DataShare::DataShareHelper>& helper)
{
    if (helper && !helper->Release()) {
        HILOG_WARN("release helper fail.");
        return false;
    }
    return true;
}
#endif

Uri AccessibilityDatashareHelper::AssembleUri(const std::string& key)
{
    Uri uri(uriProxyStr_ + "&key=" + key);
    return uri;
}

} // namespace Accessibility
} // namespace OHOS