/*
 * Copyright (C) 2021 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 "call_number_utils.h"

#include <regex>

#include "asyoutypeformatter.h"
#include "call_ability_report_proxy.h"
#include "call_manager_errors.h"
#include "call_manager_utils.h"
#include "call_voice_assistant_manager.h"
#include "cellular_call_connection.h"
#include "cellular_data_client.h"
#include "core_service_client.h"
#include "number_identity_data_base_helper.h"
#include "phonenumbers/phonenumber.pb.h"
#include "telephony_log_wrapper.h"
#include "telephony_types.h"

namespace OHOS {
namespace Telephony {
CallNumberUtils::CallNumberUtils() {}

CallNumberUtils::~CallNumberUtils() {}

int32_t CallNumberUtils::FormatPhoneNumber(
    const std::string &phoneNumber, const std::string &countryCode, std::string &formatNumber)
{
    if (phoneNumber.empty()) {
        TELEPHONY_LOGE("phoneNumber is nullptr!");
        return TELEPHONY_ERR_ARGUMENT_INVALID;
    }
    if (phoneNumber.front() == '#' || phoneNumber.front() == '*') {
        formatNumber = phoneNumber;
        return TELEPHONY_SUCCESS;
    }
    i18n::phonenumbers::PhoneNumberUtil *phoneUtils = i18n::phonenumbers::PhoneNumberUtil::GetInstance();
    if (phoneUtils == nullptr) {
        TELEPHONY_LOGE("phoneUtils is nullptr");
        return TELEPHONY_ERR_LOCAL_PTR_NULL;
    }
    std::string tmpCode = countryCode;
    transform(tmpCode.begin(), tmpCode.end(), tmpCode.begin(), ::toupper);
    i18n::phonenumbers::PhoneNumber parseResult;
    phoneUtils->ParseAndKeepRawInput(phoneNumber, tmpCode, &parseResult);
    phoneUtils->FormatInOriginalFormat(parseResult, tmpCode, &formatNumber);
    if (formatNumber.empty() || formatNumber == "0") {
        formatNumber = "";
    }
    return TELEPHONY_SUCCESS;
}

int32_t CallNumberUtils::FormatPhoneNumberToE164(
    const std::string phoneNumber, const std::string countryCode, std::string &formatNumber)
{
    return FormatNumberBase(phoneNumber, countryCode, i18n::phonenumbers::PhoneNumberUtil::E164, formatNumber);
}

int32_t CallNumberUtils::FormatPhoneNumberToNational(
    const std::string phoneNumber, const std::string countryCode, std::string &formatNumber)
{
    int32_t ret = FormatNumberBase(phoneNumber, countryCode,
        i18n::phonenumbers::PhoneNumberUtil::PhoneNumberFormat::NATIONAL, formatNumber);
    ProcessSpace(formatNumber);
    return ret;
}

int32_t CallNumberUtils::FormatPhoneNumberToInternational(
    const std::string phoneNumber, const std::string countryCode, std::string &formatNumber)
{
    int32_t ret = FormatNumberBase(phoneNumber, countryCode,
        i18n::phonenumbers::PhoneNumberUtil::PhoneNumberFormat::INTERNATIONAL, formatNumber);
    ProcessSpace(formatNumber);
    return ret;
}

int32_t CallNumberUtils::FormatNumberBase(const std::string phoneNumber, std::string countryCode,
    const i18n::phonenumbers::PhoneNumberUtil::PhoneNumberFormat formatInfo, std::string &formatNumber)
{
    if (phoneNumber.empty()) {
        TELEPHONY_LOGE("phoneNumber is nullptr!");
        return TELEPHONY_ERR_ARGUMENT_INVALID;
    }
    i18n::phonenumbers::PhoneNumberUtil *phoneUtils = i18n::phonenumbers::PhoneNumberUtil::GetInstance();
    if (phoneUtils == nullptr) {
        TELEPHONY_LOGE("phoneUtils is nullptr");
        return TELEPHONY_ERR_LOCAL_PTR_NULL;
    }
    transform(countryCode.begin(), countryCode.end(), countryCode.begin(), ::toupper);
    i18n::phonenumbers::PhoneNumber parseResult;
    phoneUtils->Parse(phoneNumber, countryCode, &parseResult);
    if (phoneUtils->IsValidNumber(parseResult) || HasBCPhoneNumber(phoneNumber)) {
        phoneUtils->Format(parseResult, formatInfo, &formatNumber);
    }
    return TELEPHONY_SUCCESS;
}

int32_t CallNumberUtils::FormatPhoneNumberAsYouType(
    const std::string &phoneNumber, const std::string &countryCode, std::string &formatNumber)
{
    if (phoneNumber.empty()) {
        TELEPHONY_LOGE("phoneNumber is nullptr!");
        return TELEPHONY_ERR_ARGUMENT_INVALID;
    }
    if (phoneNumber.front() == '#' || phoneNumber.front() == '*') {
        formatNumber = phoneNumber;
        return TELEPHONY_SUCCESS;
    }
    i18n::phonenumbers::PhoneNumberUtil *phoneUtils = i18n::phonenumbers::PhoneNumberUtil::GetInstance();
    if (phoneUtils == nullptr) {
        TELEPHONY_LOGE("phoneUtils is nullptr");
        return TELEPHONY_ERR_LOCAL_PTR_NULL;
    }
    std::string tmpCode = countryCode;
    transform(tmpCode.begin(), tmpCode.end(), tmpCode.begin(), ::toupper);
    std::unique_ptr<i18n::phonenumbers::AsYouTypeFormatter> formatter(phoneUtils->GetAsYouTypeFormatter(tmpCode));
    if (formatter == nullptr) {
        TELEPHONY_LOGE("formatter is nullptr");
        return TELEPHONY_ERR_LOCAL_PTR_NULL;
    }
    formatter->Clear();
    std::string result;
    for (size_t i = 0; i < phoneNumber.length(); i++) {
        char c = phoneNumber.at(i);
        formatNumber = formatter->InputDigit(c, &result);
    }
    if (formatNumber.empty() || formatNumber == "0") {
        formatNumber = "";
    }
    return TELEPHONY_SUCCESS;
}

void CallNumberUtils::ProcessSpace(std::string &number)
{
    std::string word;
    std::stringstream streamNum(number);
    std::string store;
    while (streamNum >> word) {
        store += word;
    }
    number = store;
}

int32_t CallNumberUtils::CheckNumberIsEmergency(const std::string &phoneNumber, const int32_t slotId, bool &enabled)
{
    return DelayedSingleton<CellularCallConnection>::GetInstance()->IsEmergencyPhoneNumber(
        phoneNumber, slotId, enabled);
}

int32_t CallNumberUtils::IsCarrierVtConfig(const int32_t slotId, bool &enabled)
{
    return DelayedSingleton<CellularCallConnection>::GetInstance()->GetCarrierVtConfig(
        slotId, enabled);
}

bool CallNumberUtils::IsValidSlotId(int32_t slotId) const
{
    if ((slotId >= DEFAULT_SIM_SLOT_ID) && (slotId < SIM_SLOT_COUNT_MD)) {
        return true;
    }
    return false;
}

std::string CallNumberUtils::RemoveSeparatorsPhoneNumber(const std::string &phoneString)
{
    std::string newString;
    if (phoneString.empty()) {
        TELEPHONY_LOGE("RemoveSeparatorsPhoneNumber return, phoneStr is empty.");
        return newString;
    }
    for (char c : phoneString) {
        if ((c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == 'N' || c == ',' || c == ';') {
            newString += c;
        }
    }

    return newString;
}

std::string CallNumberUtils::RemovePostDialPhoneNumber(const std::string &phoneString)
{
    std::string newString = "";
    if (phoneString.empty()) {
        TELEPHONY_LOGE("RemovePostDialPhoneNumber return, phoneStr is empty.");
        return newString;
    }
    for (char c : phoneString) {
        if ((c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == 'N') {
            newString += c;
        } else if (c == ',' || c == ';') {
            break;
        }
    }

    return newString;
}

bool CallNumberUtils::HasAlphabetInPhoneNum(const std::string &inputValue)
{
    if (inputValue.empty()) {
        TELEPHONY_LOGE("HasAlphabetInPhoneNum return, input is empty.");
        return true;
    }
    for (char c : inputValue) {
        if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))) {
            TELEPHONY_LOGE("The Phone Number contains letter");
            return true;
        }
    }
    TELEPHONY_LOGI("The Phone Number is valid");
    return false;
}

bool CallNumberUtils::HasBCPhoneNumber(const std::string &phoneNumber)
{
    int32_t phoneNumberStart = 0;
    int32_t phoneNumberStartLength = 3;
    size_t bCNumberLength = 11;
    std::string bCNumberStart = "192";
    if (phoneNumber.length() == bCNumberLength &&
        phoneNumber.substr(phoneNumberStart, phoneNumberStartLength) == bCNumberStart) {
        return true;
    }
    return false;
}

bool CallNumberUtils::SelectAccountId(int32_t slotId, AppExecFwk::PacMap &extras)
{
    if (IsValidSlotId(slotId)) {
        return true;
    }
    int32_t defaultVoiceSlotId = DelayedRefSingleton<CoreServiceClient>::GetInstance().GetDefaultVoiceSlotId();
    if (defaultVoiceSlotId != TELEPHONY_ERR_IPC_CONNECT_STUB_FAIL && IsValidSlotId(defaultVoiceSlotId)) {
        extras.PutIntValue("accountId", defaultVoiceSlotId);
        TELEPHONY_LOGI("select accountId to defaultVoiceSlotId = %{public}d", defaultVoiceSlotId);
        return true;
    }
#ifdef CELLULAR_DATA_SUPPORT
    int32_t defaultDataSlotId = DelayedRefSingleton<CellularDataClient>::GetInstance().GetDefaultCellularDataSlotId();
    if (defaultDataSlotId != TELEPHONY_ERR_IPC_CONNECT_STUB_FAIL && IsValidSlotId(defaultDataSlotId)) {
        extras.PutIntValue("accountId", defaultDataSlotId);
        TELEPHONY_LOGI("select accountId to defaultDataSlotId = %{public}d", defaultDataSlotId);
        return true;
    }
#endif
    return false;
}

int32_t CallNumberUtils::QueryNumberLocationInfo(std::string &numberLocation, std::string accountNumber)
{
    TELEPHONY_LOGW("QueryNumberLocationInfo");
    if (accountNumber == "") {
        TELEPHONY_LOGE("accountNumber is null");
        return TELEPHONY_ERR_ARGUMENT_INVALID;
    }
    std::shared_ptr<NumberIdentityDataBaseHelper> callDataPtr =
        DelayedSingleton<NumberIdentityDataBaseHelper>::GetInstance();
    if (callDataPtr == nullptr) {
        TELEPHONY_LOGE("callDataPtr is nullptr!");
        return TELEPHONY_ERR_LOCAL_PTR_NULL;
    }

    DataShare::DataSharePredicates predicates;
    std::vector<std::string> phoneNumber;
    phoneNumber.push_back(accountNumber);
    predicates.SetWhereArgs(phoneNumber);
    bool ret = callDataPtr->Query(numberLocation, predicates);
    if (!ret) {
        TELEPHONY_LOGE("Query number location database fail!");
        return TELEPHONY_ERR_DATABASE_READ_FAIL;
    }
    return TELEPHONY_SUCCESS;
}

void CallNumberUtils::NumberLocationUpdate(const sptr<CallBase> &callObjectPtr)
{
    CallAttributeInfo info;
    callObjectPtr->GetCallAttributeBaseInfo(info);
    TELEPHONY_LOGW("NumberLocationUpdate, callId[%{public}d]", info.callId);
    std::string numberLocation = callObjectPtr->GetNumberLocation();
    int32_t ret = QueryNumberLocationInfo(numberLocation, callObjectPtr->GetAccountNumber());
    if (ret != TELEPHONY_SUCCESS) {
        return;
    }
    sptr<CallBase> call = callObjectPtr;
    if (info.callState == TelCallState::CALL_STATUS_DIALING) {
        call = CallObjectManager::GetOneCallObject(info.callId);
        if (call == nullptr) {
            TELEPHONY_LOGE("call is nullptr");
            call = callObjectPtr;
        }
    }
    call->SetNumberLocation(numberLocation);
    CallVoiceAssistantManager::GetInstance()->UpdateNumberLocation(numberLocation, info.callId);
    if (!CallObjectManager::IsCallExist(info.callId)) {
        TELEPHONY_LOGE("call is not exist");
        return;
    }
    if (numberLocation != "" && numberLocation != "default") {
        TELEPHONY_LOGW("need report call info of numberLocation");
        call->GetCallAttributeInfo(info);
        DelayedSingleton<CallAbilityReportProxy>::GetInstance()->ReportCallStateInfo(info);
    }
}

void CallNumberUtils::YellowPageAndMarkUpdate(const sptr<CallBase> &callObjectPtr)
{
    if (CallManagerUtils::GetSystemParameter("const.global.region", "CN") != "CN") {
        TELEPHONY_LOGI("not the chinese version, no need to query markinfo.");
        return;
    }
    CallAttributeInfo info;
    callObjectPtr->GetCallAttributeInfo(info);
    TELEPHONY_LOGI("YellowPageAndMarkUpdate, callId[%{public}d]", info.callId);
    NumberMarkInfo numberMarkInfo;
    int32_t ret = QueryYellowPageAndMarkInfo(numberMarkInfo, callObjectPtr->GetAccountNumber());
    if (ret != TELEPHONY_SUCCESS) {
        TELEPHONY_LOGE("QueryYellowPageAndMarkInfo fail!");
        return;
    }
    sptr<CallBase> call = callObjectPtr;
    if (info.callState == TelCallState::CALL_STATUS_DIALING) {
        call = CallObjectManager::GetOneCallObject(info.callId);
        if (call == nullptr) {
            TELEPHONY_LOGE("call is nullptr");
            return;
        }
    }
    call->SetNumberMarkInfo(numberMarkInfo);
    if (!CallObjectManager::IsCallExist(info.callId)) {
        TELEPHONY_LOGE("call is not exist");
        return;
    }
    TELEPHONY_LOGI("markType: %{public}d, isEcc: %{public}d",
        static_cast<int32_t>(numberMarkInfo.markType), info.isEcc);
    if (numberMarkInfo.markType > MarkType::MARK_TYPE_NONE || info.isEcc) {
        call->GetCallAttributeInfo(info);
        DelayedSingleton<CallAbilityReportProxy>::GetInstance()->ReportCallStateInfo(info);
    }
}

int32_t CallNumberUtils::QueryYellowPageAndMarkInfo(NumberMarkInfo &numberMarkInfo, std::string accountNumber)
{
    TELEPHONY_LOGW("QueryYellowPageAndMarkInfo");
    if (accountNumber == "") {
        TELEPHONY_LOGE("accountNumber is null");
        return TELEPHONY_ERR_ARGUMENT_INVALID;
    }
    std::shared_ptr<NumberIdentityDataBaseHelper> callDataPtr =
        DelayedSingleton<NumberIdentityDataBaseHelper>::GetInstance();
    if (callDataPtr == nullptr) {
        TELEPHONY_LOGE("callDataPtr is nullptr!");
        return TELEPHONY_ERR_LOCAL_PTR_NULL;
    }

    DataShare::DataSharePredicates predicates;
    std::vector<std::string> phoneNumber;
    phoneNumber.push_back(accountNumber);
    predicates.SetWhereArgs(phoneNumber);
    bool ret = callDataPtr->QueryYellowPageAndMark(numberMarkInfo, predicates);
    if (!ret) {
        TELEPHONY_LOGE("Query yellow page and mark fail!");
        return TELEPHONY_ERR_DATABASE_READ_FAIL;
    }
    return TELEPHONY_SUCCESS;
}

bool CallNumberUtils::GetAccountIdByNumber(const std::u16string &inputNumber, int32_t& slotId)
{
    for (int32_t i = 0; i < SIM_SLOT_COUNT_MD; i++) {
        std::u16string showNumber;
        DelayedRefSingleton<CoreServiceClient>::GetInstance().GetShowNumber(i, showNumber);
        if (IsSamePhoneNumber(showNumber, inputNumber)) {
            slotId = i;
            return true;
        }
    }
    return false;
}

bool CallNumberUtils::IsSamePhoneNumber(const std::u16string &localNumber, const std::u16string &inputNumber)
{
    std::string localNumberStr = NormalizePhoneNumber(localNumber);
    std::string inputNumberStr = NormalizePhoneNumber(inputNumber);
    if (localNumberStr.empty() || inputNumberStr.empty()) {
        TELEPHONY_LOGE("localNumberStr or inputNumberStr is empty");
        return false;
    }
    return localNumberStr == inputNumberStr;
}

std::string CallNumberUtils::NormalizePhoneNumber(const std::u16string &number)
{
    std::string normalized = std::regex_replace(Str16ToStr8(number), std::regex("[^0-9]"), "");
    std::regex pattern("1\\d{10}");
    std::smatch match;
    if (std::regex_search(normalized, match, pattern)) {
        return match.str(0);
    }
    return "";
}
} // namespace Telephony
} // namespace OHOS