/*
 * 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 <algorithm>
#include <cstdint>
#include <tuple>
#include "app_domain_verify_mgr_client.h"
#include "datetime_ex.h"
#include "inner_verify_status.h"
#include "verify_task.h"
#include "domain_verifier.h"
#include "app_domain_verify_hilog.h"
#include "bundle_verify_status_info.h"
#include "agent_constants.h"
#include "domain_url_util.h"
#include "app_domain_verify_task_mgr.h"
#include "verify_http_task.h"
#include "bundle_info_query.h"

namespace OHOS {
namespace AppDomainVerify {
const std::string HTTPS = "https";
const std::set<std::string> SCHEME_WHITE_SET = { HTTPS };
const std::string FUZZY_HOST_START = "*.";
const static int CLIENT_ERR_MAX_RETRY_COUNTS = 7;          // 7 times for max retry count
const static int CLIENT_ERR_BASE_RETRY_DURATION_S = 3600;  // 1h for base duration
void VerifyTask::OnPostVerify(const std::string& uri, const OHOS::NetStack::HttpClient::HttpClientResponse& response)
{
    APP_DOMAIN_VERIFY_HILOGI(APP_DOMAIN_VERIFY_AGENT_MODULE_SERVICE, "called");
    auto status = DomainVerifier::VerifyHost(response.GetResponseCode(), response.GetResult(), appVerifyBaseInfo_);
    if (appVerifyBaseInfo_.priority != PRIORITY_UNSET &&
        (appVerifyBaseInfo_.priority < PRIORITY_MIN || appVerifyBaseInfo_.priority > PRIORITY_MAX)) {
        status = InnerVerifyStatus::FAILURE_CLIENT_ERROR;
    }
    if (status == InnerVerifyStatus::FAILURE_HTTP_UNKNOWN && appVerifyBaseInfo_.isPreinstalled) {
        status = InnerVerifyStatus::PRE_INSTALLED;
    }
    APP_DOMAIN_VERIFY_HILOGD(APP_DOMAIN_VERIFY_AGENT_MODULE_SERVICE,
        "OnPostVerify status %{public}d appId:%{public}s, priority:%{public}d", status,
        appVerifyBaseInfo_.appIdentifier.c_str(), appVerifyBaseInfo_.priority);
    UpdateVerifyResultInfo(uri, status);
    {
        std::unique_lock<ffrt::mutex> lock(unVerifiedSetMutex_);
        unVerifiedSet_.erase(uri);
        if (unVerifiedSet_.empty()) {
            OnSaveVerifyResult();
        }
    }
    VERIFY_RESULT_EVENT(appVerifyBaseInfo_.appIdentifier, appVerifyBaseInfo_.bundleName, type_, status);
}

void VerifyTask::OnSaveVerifyResult()
{
    APP_DOMAIN_VERIFY_HILOGI(APP_DOMAIN_VERIFY_AGENT_MODULE_SERVICE, "called");
    if (!SaveDomainVerifyStatus(appVerifyBaseInfo_.bundleName, verifyResultInfo_)) {
        APP_DOMAIN_VERIFY_HILOGE(APP_DOMAIN_VERIFY_AGENT_MODULE_SERVICE, "SaveVerifyResult failed");
    }
    APP_DOMAIN_VERIFY_HILOGI(APP_DOMAIN_VERIFY_AGENT_MODULE_SERVICE, "call end");
}
bool VerifyTask::OnPreRequest(OHOS::NetStack::HttpClient::HttpClientRequest& request, const std::string& uri)
{
    APP_DOMAIN_VERIFY_HILOGI(APP_DOMAIN_VERIFY_AGENT_MODULE_SERVICE, "called");
    request.SetURL(uri + ApplinkingAssetKeys::ASSET_PATH + ApplinkingAssetKeys::ASSET_NAME);
    request.SetMethod("GET");
    return true;
}
OHOS::AppDomainVerify::TaskType VerifyTask::GetType()
{
    return type_;
}
const HostVerifyStatusMap& VerifyTask::GetUriVerifyMap()
{
    return verifyResultInfo_.hostVerifyStatusMap;
}
void VerifyTask::InitUriUnVerifySetMap(const VerifyResultInfo& verifyResultInfo)
{
    APP_DOMAIN_VERIFY_HILOGD(APP_DOMAIN_VERIFY_AGENT_MODULE_SERVICE, "called");
    std::unique_lock<ffrt::mutex> lock(unVerifiedSetMutex_);
    for (const auto& hostVerifyStatusInfo : verifyResultInfo.hostVerifyStatusMap) {
        if (IsNeedRetry(hostVerifyStatusInfo.second)) {
            unVerifiedSet_.insert(hostVerifyStatusInfo.first);
        }
    }

    APP_DOMAIN_VERIFY_HILOGD(APP_DOMAIN_VERIFY_AGENT_MODULE_SERVICE, "call end");
}

VerifyTask::VerifyTask(OHOS::AppDomainVerify::TaskType type, const AppVerifyBaseInfo& appVerifyBaseInfo,
    const VerifyResultInfo& verifyResultInfo)
    : type_(type), appVerifyBaseInfo_(appVerifyBaseInfo), verifyResultInfo_(verifyResultInfo)
{
    staHandlerMap[STATE_SUCCESS] = [this](std::string time, int cnt) -> bool { return HandleStateSuccess(time, cnt); };
    staHandlerMap[FAILURE_CLIENT_ERROR] = [this](std::string time, int cnt) -> bool {
        return HandleFailureClientError(time, cnt);
    };
    staHandlerMap[FORBIDDEN_FOREVER] = [this](std::string time, int cnt) -> bool {
        return HandleForbiddenForever(time, cnt);
    };
    InitUriUnVerifySetMap(verifyResultInfo);
}

OHOS::AppDomainVerify::TaskType& VerifyTask::GetTaskType()
{
    return type_;
}
AppVerifyBaseInfo& VerifyTask::GetAppVerifyBaseInfo()
{
    return appVerifyBaseInfo_;
}

bool VerifyTask::SaveDomainVerifyStatus(const std::string& bundleName, const VerifyResultInfo& verifyResultInfo)
{
    return AppDomainVerifyMgrClient::GetInstance()->SaveDomainVerifyStatus(bundleName, verifyResultInfo);
}

void VerifyTask::Execute()
{
    std::unordered_set<std::string> unVerifiedSetTmp;
    {
        std::unique_lock<ffrt::mutex> lock(unVerifiedSetMutex_);
        unVerifiedSetTmp = unVerifiedSet_;
    }
    for (auto& hostVerifyStatusInfo : verifyResultInfo_.hostVerifyStatusMap) {
        if (unVerifiedSetTmp.count(hostVerifyStatusInfo.first) != 0) {
            auto verifyHttpTask = std::make_shared<VerifyHttpTask>(hostVerifyStatusInfo.first, shared_from_this());
            AppDomainVerifyTaskMgr::GetInstance()->AddTask(verifyHttpTask);
        }
    }
}

bool VerifyTask::IsNeedRetry(const VerifyStatus& info)
{
    auto iter = staHandlerMap.find(info.status);
    if (iter != staHandlerMap.end()) {
        return iter->second(info.verifyTime, info.retryCnt);
    }
    return true;
}

int64_t VerifyTask::CalcRetryDuration(int verifyCnt)
{
    int64_t duration = pow(2, verifyCnt) * CLIENT_ERR_BASE_RETRY_DURATION_S;  // base * 2 ^ verifyCnt
    return duration;
}

bool VerifyTask::HandleFailureClientError(std::string verifyTime, int verifyCnt)
{
    APP_DOMAIN_VERIFY_HILOGD(APP_DOMAIN_VERIFY_MODULE_EXTENSION, "called");
    if (!verifyTime.empty()) {
        int64_t currTs = GetSecondsSince1970ToNow();
        int64_t lastTs{};
        try {
            lastTs = static_cast<int64_t>(std::stoll(verifyTime));
        } catch (...) {
            APP_DOMAIN_VERIFY_HILOGE(APP_DOMAIN_VERIFY_MODULE_EXTENSION, "get verifyTime error");
            return false;
        }
        int64_t duration = currTs - lastTs;
        int64_t currRetryDuration = CalcRetryDuration(verifyCnt);
        if (duration <= currRetryDuration) {
            APP_DOMAIN_VERIFY_HILOGI(APP_DOMAIN_VERIFY_MODULE_EXTENSION,
                "last time:%{public}s, curr time:%{public}s, "
                "duration:%{public}s "
                "is less than max retry duration:%{public}s, not retry",
                std::to_string(lastTs).c_str(), std::to_string(currTs).c_str(), std::to_string(duration).c_str(),
                std::to_string(currRetryDuration).c_str());
            return false;
        }
    }
    return true;
}

bool VerifyTask::HandleStateSuccess(std::string verifyTime, int verifyCnt)
{
    APP_DOMAIN_VERIFY_HILOGD(APP_DOMAIN_VERIFY_MODULE_EXTENSION, "called");
    return false;
}

bool VerifyTask::HandleForbiddenForever(std::string verifyTime, int verifyCnt)
{
    APP_DOMAIN_VERIFY_HILOGD(APP_DOMAIN_VERIFY_MODULE_EXTENSION, "called");
    return false;
}

void VerifyTask::UpdateVerifyResultInfo(const std::string& uri, InnerVerifyStatus status)
{
    VerifyStatus verifyStatus = { .status = status,
        .retryCnt = 0,
        .verifyTime = std::to_string(GetSecondsSince1970ToNow()),
        .priority = appVerifyBaseInfo_.priority };
    auto& hostVerifyStatusMap = verifyResultInfo_.hostVerifyStatusMap;
    auto iter = hostVerifyStatusMap.find(uri);
    if (iter == hostVerifyStatusMap.end()) {
        hostVerifyStatusMap.insert_or_assign(uri, verifyStatus);
        return;
    }
    if (verifyStatus.status == InnerVerifyStatus::FAILURE_CLIENT_ERROR) {
        verifyStatus.retryCnt++;
        if (verifyStatus.retryCnt >= CLIENT_ERR_MAX_RETRY_COUNTS) {
            verifyStatus.status = InnerVerifyStatus::FORBIDDEN_FOREVER;
        }
    }
    hostVerifyStatusMap.insert_or_assign(uri, verifyStatus);
}

}
}