/*
 * 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 "ability_manager_client.h"
#include "application_context.h"
#include "hisysevent_adapter_impl.h"
#include "hisysevent.h"
#include "ohos_resource_adapter_impl.h"
#include "arkweb_utils.h"
#include "parameters.h"

namespace OHOS::NWeb {
namespace {
const HiviewDFX::HiSysEvent::EventType EVENT_TYPES[] = {
    OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
    OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC,
    OHOS::HiviewDFX::HiSysEvent::EventType::SECURITY,
    OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
};
}

const int kBase10 = 10;
bool ConvertToLongLong(const std::string& str, long long& value)
{
    char* end;
    errno = 0;
    value = std::strtoll(str.c_str(), &end, kBase10);
    if (end == str.c_str()) {
        return false;
    }
    if (errno == ERANGE && (value == LLONG_MAX || value == LLONG_MIN)) {
        return false;
    }
    if (*end != '\0') {
        return false;
    }
    return true;
}

int64_t GetValueInt64(const std::string& input, const std::string& key1, const std::string& key2)
{
    long long result = 0;
    std::string key = "";
    if(key2 == key) {
        std::string::size_type keyPosition1 = input.find(key1, 0);
        if (keyPosition1 != std::string::npos) {
            std::string waitConvertString = input.substr(keyPosition1 + key1.size());
            if (ConvertToLongLong(waitConvertString, result) == true) {
                return result;
            }
        }
        return -1;
    }
    std::string::size_type keyPosition1 = input.find(key1, 0);
    std::string::size_type keyPosition2 = input.find(key2, 0);
    if (keyPosition1 != std::string::npos && keyPosition2 != std::string::npos &&
        keyPosition1 + key1.size() < keyPosition2) {
        std::string waitConvertString = input.substr(keyPosition1 + key1.size(),
            keyPosition2 - keyPosition1 - key1.size());
        if (ConvertToLongLong(waitConvertString, result) == true) {
            return result;
        }
    }
    return -1;
}

const static std::string PAGE_LOAD_KEY_LISTS[] = {
    "NAVIGATION_ID",
    "NAVIGATION_START",
    "REDIRECT_COUNT",
    "REDIRECT_START",
    "REDIRECT_END",
    "FETCH_START",
    "WORKER_START",
    "DOMAIN_LOOKUP_START",
    "DOMAIN_LOOKUP_END",
    "CONNECT_START",
    "SECURE_CONNECT_START",
    "CONNECT_END",
    "REQUEST_START",
    "RESPONSE_START",
    "RESPONSE_END",
    "DOM_INTERACTIVE",
    "DOM_CONTENT_LOADED_EVENT_START",
    "DOM_CONTENT_LOADED_EVENT_END",
    "LOAD_EVENT_START",
    "LOAD_EVENT_END",
    "FIRST_PAINT",
    "FIRST_CONTENTFUL_PAINT",
    "LARGEST_CONTENTFUL_PAINT",
    "RENDER_INIT_BLOCK",
    "INPUT_TIME",
    "IS_PAINT_DONE",
    "FIRST_MEANINGFUL_PAINT"
};
static std::string g_versionCode = "";
static std::string g_webEngineType = "";
static std::string g_defaultWebEngineType = "";
HiSysEventAdapterImpl& HiSysEventAdapterImpl::GetInstance()
{
    static HiSysEventAdapterImpl instance;
    return instance;
}

template<typename... Args>
static int ForwardToHiSysEvent(const std::string& eventName, HiSysEventAdapter::EventType type,
    const std::tuple<Args...>& tp)
{
    if (g_versionCode.empty()) {
        g_versionCode = OhosResourceAdapterImpl::GetArkWebVersion();
    }

    if (g_webEngineType.empty()) {
        g_webEngineType = std::to_string(static_cast<int>(OHOS::ArkWeb::getActiveWebEngineType()));
    }

    if (g_defaultWebEngineType.empty()) {
        g_defaultWebEngineType = std::to_string(OHOS::system::GetIntParameter("web.engine.default",
            static_cast<int>(OHOS::ArkWeb::ArkWebEngineType::EVERGREEN)));
    }

    auto sysData = std::make_tuple("BUNDLE_NAME", OHOS::ArkWeb::GetBundleName(),
                                   "VERSION_CODE", g_versionCode,
                                   "API_COMPATIBLE_VERSION", OHOS::ArkWeb::GetApiVersion(),
                                   "WEB_ENGINE_TYPE", g_webEngineType,
                                   "DEFAULT_WEB_ENGINE_TYPE", g_defaultWebEngineType);
    auto mergeData = std::tuple_cat(sysData, tp);

    if (type == HiSysEventAdapter::EventType::BEHAVIOR) {
        auto ueData = std::make_tuple("PNAMEID", OHOS::ArkWeb::GetBundleName(),
                                      "PVERSIONID", OHOS::ArkWeb::GetAppVersion());
        return std::apply(
            [&](auto&&... args) {
                return HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::ARKWEB_UE,
                                       eventName, EVENT_TYPES[type], args...);
            },
            std::tuple_cat(ueData, mergeData));
    } else {
        return std::apply(
            [&](auto&&... args) {
                return HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::WEBVIEW,
                                       eventName, EVENT_TYPES[type], args...);
            },
            mergeData);
    }
}

int ProcessEventPageLoadTime(const std::string& eventName, HiSysEventAdapter::EventType type,
    const std::tuple<const std::string, const std::string>& data)
{
    auto appInfo = AbilityRuntime::ApplicationContext::GetInstance()->GetApplicationInfo();
    if (appInfo == nullptr) {
        return -1;
    }
    AppExecFwk::ElementName elementName = AAFwk::AbilityManagerClient::GetInstance()->GetTopAbility();

    std::tuple<const std::string, const std::string> sysData = {
        "ABILITY_NAME", elementName.GetAbilityName(),
    };

    const std::string input = std::get<0>(data);

    const std::int64_t value1 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[0], PAGE_LOAD_KEY_LISTS[1]);
    const std::int64_t value2 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[1], PAGE_LOAD_KEY_LISTS[2]);
    const std::uint32_t value3 = (std::uint32_t)GetValueInt64(input, PAGE_LOAD_KEY_LISTS[2], PAGE_LOAD_KEY_LISTS[3]);
    const std::int64_t value4 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[3], PAGE_LOAD_KEY_LISTS[4]);
    const std::int64_t value5 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[4], PAGE_LOAD_KEY_LISTS[5]);
    const std::int64_t value6 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[5], PAGE_LOAD_KEY_LISTS[6]);
    const std::int64_t value7 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[6], PAGE_LOAD_KEY_LISTS[7]);
    const std::int64_t value8 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[7], PAGE_LOAD_KEY_LISTS[8]);
    const std::int64_t value9 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[8], PAGE_LOAD_KEY_LISTS[9]);
    const std::int64_t value10 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[9], PAGE_LOAD_KEY_LISTS[10]);
    const std::int64_t value11 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[10], PAGE_LOAD_KEY_LISTS[11]);
    const std::int64_t value12 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[11], PAGE_LOAD_KEY_LISTS[12]);
    const std::int64_t value13 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[12], PAGE_LOAD_KEY_LISTS[13]);
    const std::int64_t value14 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[13], PAGE_LOAD_KEY_LISTS[14]);
    const std::int64_t value15 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[14], PAGE_LOAD_KEY_LISTS[15]);
    const std::int64_t value16 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[15], PAGE_LOAD_KEY_LISTS[16]);
    const std::int64_t value17 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[16], PAGE_LOAD_KEY_LISTS[17]);
    const std::int64_t value18 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[17], PAGE_LOAD_KEY_LISTS[18]);
    const std::int64_t value19 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[18], PAGE_LOAD_KEY_LISTS[19]);
    const std::int64_t value20 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[19], PAGE_LOAD_KEY_LISTS[20]);
    const std::int64_t value21 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[20], PAGE_LOAD_KEY_LISTS[21]);
    const std::int64_t value22 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[21], PAGE_LOAD_KEY_LISTS[22]);
    const std::int64_t value23 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[22], PAGE_LOAD_KEY_LISTS[23]);
    const bool value24 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[23], "");

    auto newData = std::make_tuple(
        PAGE_LOAD_KEY_LISTS[0], value1, PAGE_LOAD_KEY_LISTS[1], value2,
        PAGE_LOAD_KEY_LISTS[2], value3, PAGE_LOAD_KEY_LISTS[3], value4,
        PAGE_LOAD_KEY_LISTS[4], value5, PAGE_LOAD_KEY_LISTS[5], value6,
        PAGE_LOAD_KEY_LISTS[6], value7, PAGE_LOAD_KEY_LISTS[7], value8,
        PAGE_LOAD_KEY_LISTS[8], value9, PAGE_LOAD_KEY_LISTS[9], value10,
        PAGE_LOAD_KEY_LISTS[10], value11, PAGE_LOAD_KEY_LISTS[11], value12,
        PAGE_LOAD_KEY_LISTS[12], value13, PAGE_LOAD_KEY_LISTS[13], value14,
        PAGE_LOAD_KEY_LISTS[14], value15, PAGE_LOAD_KEY_LISTS[15], value16,
        PAGE_LOAD_KEY_LISTS[16], value17, PAGE_LOAD_KEY_LISTS[17], value18,
        PAGE_LOAD_KEY_LISTS[18], value19, PAGE_LOAD_KEY_LISTS[19], value20,
        PAGE_LOAD_KEY_LISTS[20], value21, PAGE_LOAD_KEY_LISTS[21], value22,
        PAGE_LOAD_KEY_LISTS[22], value23, PAGE_LOAD_KEY_LISTS[23], value24);

    auto mergeData = std::tuple_cat(newData, sysData);
    return ForwardToHiSysEvent(eventName, type, mergeData);
}

int ProcessEventFirstMeaningfulPaintDone(const std::string& eventName, HiSysEventAdapter::EventType type,
    const std::tuple<const std::string, const std::string>& data)
{
    auto appInfo = AbilityRuntime::ApplicationContext::GetInstance()->GetApplicationInfo();
    if (appInfo == nullptr) {
        return -1;
    }
    AppExecFwk::ElementName elementName = AAFwk::AbilityManagerClient::GetInstance()->GetTopAbility();

    std::tuple<const std::string, const std::string> sysData = {
        "ABILITY_NAME", elementName.GetAbilityName(),
    };

    const std::string input = std::get<0>(data);

    const std::int64_t value1 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[0], PAGE_LOAD_KEY_LISTS[1]);
    const std::int64_t value2 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[1], PAGE_LOAD_KEY_LISTS[2]);
    const std::uint32_t value3 = (uint32_t)GetValueInt64(input, PAGE_LOAD_KEY_LISTS[2], PAGE_LOAD_KEY_LISTS[24]);
    const std::int64_t value4 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[24], PAGE_LOAD_KEY_LISTS[20]);
    const std::int64_t value5 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[20], PAGE_LOAD_KEY_LISTS[21]);
    const std::int64_t value6 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[21], PAGE_LOAD_KEY_LISTS[26]);
    const std::int64_t value7 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[26], PAGE_LOAD_KEY_LISTS[25]);
    const bool value8 = GetValueInt64(input, PAGE_LOAD_KEY_LISTS[25], "");

    auto newData = std::make_tuple(
        PAGE_LOAD_KEY_LISTS[0], value1, PAGE_LOAD_KEY_LISTS[1], value2,
        PAGE_LOAD_KEY_LISTS[2], value3, PAGE_LOAD_KEY_LISTS[24], value4,
        PAGE_LOAD_KEY_LISTS[20], value5, PAGE_LOAD_KEY_LISTS[21], value6,
        PAGE_LOAD_KEY_LISTS[26], value7, PAGE_LOAD_KEY_LISTS[25], value8);

    auto mergeData = std::tuple_cat(newData, sysData);
    return ForwardToHiSysEvent(eventName, type, mergeData);
}

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const std::string>& data)
{
    const std::string eventNamePageLoadTime = "PAGE_LOAD_TIME";
    const std::string eventNameFirstMeaningfulPaintDone = "FIRST_MEANINGFUL_PAINT_DONE";
    if (eventName == eventNamePageLoadTime) {
        return ProcessEventPageLoadTime(eventName, type, data);
    }
    if (eventName == eventNameFirstMeaningfulPaintDone) {
        int result = ProcessEventFirstMeaningfulPaintDone(eventName, type, data);
        return result;
    }
#ifdef ARKWEB_HISTOGRAM_METRICS_ENABLE
    if (eventName == "UMA_METRICS_LOG_UPLOAD") {
        auto sysData = std::make_tuple("HISTOGRAM_TYPE", "arkweb.histogram.uma",
            "HISTOGRAM_CONTENT", std::get<1>(data));
        return ForwardToHiSysEvent("HISTOGRAM_UMA_UKM", type, sysData);
    } else if (eventName == "UKM_METRICS_LOG_UPLOAD") {
        auto sysData = std::make_tuple("HISTOGRAM_TYPE", "arkweb.histogram.ukm",
            "HISTOGRAM_CONTENT", std::get<1>(data));
        return ForwardToHiSysEvent("HISTOGRAM_UMA_UKM", type, sysData);
    }
#endif
    return ForwardToHiSysEvent(eventName, type, data);
}

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const std::string, const std::string, const std::string>& data)
{
    return ForwardToHiSysEvent(eventName, type, data);
}

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string>& data)
{
    return ForwardToHiSysEvent(eventName, type, data);
}

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string, const std::string, const std::string>& data)
{
    return ForwardToHiSysEvent(eventName, type, data);
}

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string>& data)
{
    return ForwardToHiSysEvent(eventName, type, data);
}

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string, const std::string, const std::string>& data)
{
    return ForwardToHiSysEvent(eventName, type, data);
}

using systemData = std::tuple<const std::string, const int, const std::string, const std::string,
                              const std::string, const std::string>;

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const int64_t, const std::string, const int,
    const std::string, const std::vector<uint16_t>, const std::string, const int>& data)
{
    auto appInfo = AbilityRuntime::ApplicationContext::GetInstance()->GetApplicationInfo();
    if (appInfo == nullptr) {
        return -1;
    }
    AppExecFwk::ElementName elementName = AAFwk::AbilityManagerClient::GetInstance()->GetTopAbility();

    systemData sysData = {
        "VERSION_CODE", appInfo->versionCode,
        "VERSION_NAME", appInfo->versionName.c_str(),
        "ABILITY_NAME", elementName.GetAbilityName()
    };

    auto mergeData = std::tuple_cat(data, sysData);
    return ForwardToHiSysEvent(eventName, type, mergeData);
}

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const int64_t, const std::string, const int64_t,
    const std::string, const int, const std::string, const int,
    const std::string, const int64_t, const std::string, const int>& data)
{
    auto appInfo = AbilityRuntime::ApplicationContext::GetInstance()->GetApplicationInfo();
    if (appInfo == nullptr) {
        return -1;
    }
    AppExecFwk::ElementName elementName = AAFwk::AbilityManagerClient::GetInstance()->GetTopAbility();

    std::tuple<const std::string, const std::string, const std::string, const std::string,
        const std::string, const std::string> sysData = {
        "SCENE_ID", "WEB_LIST_FLING",
        "ABILITY_NAME", elementName.GetAbilityName(),
        "PAGE_URL", ""
    };

    auto mergeData = std::tuple_cat(data, sysData);
    return ForwardToHiSysEvent(eventName, type, mergeData);
}

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const int64_t,
    const std::string, const int64_t, const std::string, const uint32_t,
    const std::string, const int64_t, const std::string, const int64_t,
    const std::string, const int64_t, const std::string, const int64_t,
    const std::string, const int64_t, const std::string, const int64_t,
    const std::string, const int64_t, const std::string, const int64_t,
    const std::string, const int64_t, const std::string, const int64_t,
    const std::string, const int64_t, const std::string, const int64_t,
    const std::string, const int64_t, const std::string, const int64_t,
    const std::string, const int64_t, const std::string, const int64_t,
    const std::string, const int64_t, const std::string, const int64_t,
    const std::string, const int64_t, const std::string, const int64_t>& data)
{
    auto appInfo = AbilityRuntime::ApplicationContext::GetInstance()->GetApplicationInfo();
    if (appInfo == nullptr) {
        return -1;
    }
    AppExecFwk::ElementName elementName = AAFwk::AbilityManagerClient::GetInstance()->GetTopAbility();

    std::tuple<const std::string, const std::string> sysData = {
        "ABILITY_NAME", elementName.GetAbilityName(),
    };

    auto mergeData = std::tuple_cat(data, sysData);
    return ForwardToHiSysEvent(eventName, type, mergeData);
}

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string>& data)
{
    return ForwardToHiSysEvent(eventName, type, data);
}

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string, const std::string, const std::string>& data)
{
   return ForwardToHiSysEvent(eventName, type, data);
}

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const uint32_t, const std::string, const uint64_t>& data)
{
    return ForwardToHiSysEvent(eventName, type, data);
}

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const int32_t, const std::string, const std::string,
                     const std::string, const std::string, const std::string, const std::string,
                     const std::string, const int32_t>& data)
{
    return ForwardToHiSysEvent(eventName, type, data);
}

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const int32_t>& data)
{
    return ForwardToHiSysEvent(eventName, type, data);
}

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string, const std::string, const std::string,
                     const std::string, const std::string, const std::string, const std::string>& data)
{
   return ForwardToHiSysEvent(eventName, type, data);
}

int HiSysEventAdapterImpl::Write(const std::string& eventName, EventType type,
    const std::tuple<const std::string, const int32_t, const int32_t>& data)
{
    std::tuple<const std::string, const std::string,
        const std::string, const int32_t, const std::string, const int32_t> reportData = {
        "BLANK_SCREEN_URL", std::get<0>(data),
        "BLANK_SCREEN_REASON", std::get<1>(data),
        "BLANK_SCREEN_COUNT", std::get<2>(data)
    };
    return ForwardToHiSysEvent(eventName, type, reportData);
}
} // namespace OHOS::NWeb