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

#include <cstdint>
#include <new>
#include <numeric>
#include <string>

#include "mac_address_info.h"
#include "ethernet_client.h"
#include "napi_utils.h"
#include "net_manager_constants.h"
#include "netmanager_ext_log.h"

namespace OHOS {
namespace NetManagerStandard {
namespace EthernetExec {
namespace {
constexpr const char *MODE = "mode";
constexpr const char *IP_ADDR = "ipAddr";
constexpr const char *ROUTE = "route";
constexpr const char *NET_MASK = "netMask";
constexpr const char *GATEWAY = "gateway";
constexpr const char *DNS_SERVERS = "dnsServers";
constexpr const char *DOMAIN = "domain";
constexpr const char *HTTP_PROXY = "httpProxy";
constexpr const char *HOST = "host";
constexpr const char *PORT = "port";
constexpr const char *EXCLUSION_LIST = "exclusionList";
constexpr const char *DEFAULT_SEPARATOR = ",";
constexpr const char *MAC_ADDR = "macAddress";
constexpr const char *IFACE = "iface";
constexpr const char *IFACE_NAME = "ifaceName";
constexpr const char *DEVICE_NAME = "deviceName";
constexpr const char *CONNECTION_MODE = "connectionMode";
constexpr const char *SUPPLIER_NAME = "supplierName";
constexpr const char *SUPPLIER_ID = "supplierId";
constexpr const char *PRODUCT_NAME = "productName";
constexpr const char *MAX_RATE = "maximumRate";

std::string AccumulateNetAddress(const std::vector<INetAddr> &netAddrList)
{
    return std::accumulate(
        netAddrList.begin(), netAddrList.end(), std::string(), [](const std::string &addr, const INetAddr &iter) {
            return addr.empty() ? (addr + iter.address_) : (addr + DEFAULT_SEPARATOR + iter.address_);
        });
}
} // namespace

#ifdef NETMANAGER_EXT_ETHERNET_ENABLE_DISABLE
// Error code mapping for ethernet APIs
constexpr int32_t NETMANAGER_EXT_ERR_PERMISSION_DENIED = 201;
constexpr int32_t NETMANAGER_EXT_ERR_NOT_SYSTEM_CALL = 202;
constexpr int32_t NETMANAGER_EXT_ERR_IPC_CONNECT_STUB_FAIL = 2200002;
constexpr int32_t NETMANAGER_EXT_ERR_INTERNAL = 2200003;
#endif

#ifdef NETMANAGER_EXT_ETHERNET_ENABLE_DISABLE
// Map internal error code to NAPI error code
int32_t MapToNapiErrorCode(int32_t internalCode)
{
    switch (internalCode) {
        case NETMANAGER_EXT_ERR_PERMISSION_DENIED:
            return NETMANAGER_EXT_ERR_PERMISSION_DENIED;
        case NETMANAGER_EXT_ERR_NOT_SYSTEM_CALL:
            return NETMANAGER_EXT_ERR_NOT_SYSTEM_CALL;
        case NETMANAGER_EXT_ERR_IPC_CONNECT_STUB_FAIL:
            return NETMANAGER_EXT_ERR_IPC_CONNECT_STUB_FAIL;
        default:
            return NETMANAGER_EXT_ERR_INTERNAL;
    }
}

// Get error message for NAPI error code
std::string GetNapiErrorMessage(int32_t napiCode)
{
    switch (napiCode) {
        case NETMANAGER_EXT_ERR_PERMISSION_DENIED:
            return "Permission denied.";
        case NETMANAGER_EXT_ERR_NOT_SYSTEM_CALL:
            return "Non-system applications use system APIs.";
        case NETMANAGER_EXT_ERR_IPC_CONNECT_STUB_FAIL:
            return "Failed to connect to the service.";
        case NETMANAGER_EXT_ERR_INTERNAL:
            return "System internal error.";
        default:
            return "Unknown error.";
    }
}
#endif
bool ExecGetMacAddress(GetMacAddressContext *context)
{
    int32_t result = DelayedSingleton<EthernetClient>::GetInstance()->GetMacAddress(context->macAddrInfo_);
    if (result != NETMANAGER_EXT_SUCCESS) {
        NETMANAGER_EXT_LOGE("ExecGetMacAddress error, errorCode: %{public}d", result);
        context->SetErrorCode(result);
        return false;
    }
    return true;
}

napi_value GetMacAddressCallback(GetMacAddressContext *context)
{
    napi_value macAddressList = NapiUtils::CreateArray(context->GetEnv(), context->macAddrInfo_.size());
    uint32_t index = 0;
    for (auto &eachInfo : context->macAddrInfo_) {
        napi_value macAddrInfo = NapiUtils::CreateObject(context->GetEnv());
        NapiUtils::SetStringPropertyUtf8(
            context->GetEnv(), macAddrInfo, IFACE, eachInfo.iface_);
        NapiUtils::SetStringPropertyUtf8(
            context->GetEnv(), macAddrInfo, MAC_ADDR, eachInfo.macAddress_);
        NapiUtils::SetArrayElement(context->GetEnv(), macAddressList, index++, macAddrInfo);
    }
    return macAddressList;
}

bool ExecGetIfaceConfig(GetIfaceConfigContext *context)
{
    int32_t result = DelayedSingleton<EthernetClient>::GetInstance()->GetIfaceConfig(context->iface_, context->config_);
    if (context->config_ == nullptr || result != NETMANAGER_EXT_SUCCESS) {
        NETMANAGER_EXT_LOGE("ExecGetIfaceConfig error, errorCode: %{public}d", result);
        context->SetErrorCode(result);
        return false;
    }
    return true;
}

napi_value GetIfaceConfigCallback(GetIfaceConfigContext *context)
{
    napi_value interfaceConfiguration = NapiUtils::CreateObject(context->GetEnv());
    NapiUtils::SetInt32Property(context->GetEnv(), interfaceConfiguration, MODE, context->config_->mode_);

    std::string ipAddresses = AccumulateNetAddress(context->config_->ipStatic_.ipAddrList_);
    NapiUtils::SetStringPropertyUtf8(context->GetEnv(), interfaceConfiguration, IP_ADDR, ipAddresses);

    std::string routeAddresses = AccumulateNetAddress(context->config_->ipStatic_.routeList_);
    NapiUtils::SetStringPropertyUtf8(context->GetEnv(), interfaceConfiguration, ROUTE, routeAddresses);

    std::string gatewayAddresses = AccumulateNetAddress(context->config_->ipStatic_.gatewayList_);
    NapiUtils::SetStringPropertyUtf8(context->GetEnv(), interfaceConfiguration, GATEWAY, gatewayAddresses);

    std::string maskAddresses = AccumulateNetAddress(context->config_->ipStatic_.netMaskList_);
    NapiUtils::SetStringPropertyUtf8(context->GetEnv(), interfaceConfiguration, NET_MASK, maskAddresses);

    std::string dnsServers = AccumulateNetAddress(context->config_->ipStatic_.dnsServers_);
    NapiUtils::SetStringPropertyUtf8(context->GetEnv(), interfaceConfiguration, DNS_SERVERS, dnsServers);

    NapiUtils::SetStringPropertyUtf8(context->GetEnv(), interfaceConfiguration, DOMAIN,
                                     context->config_->ipStatic_.domain_);

    std::string host = context->config_->httpProxy_.GetHost();
    if (!host.empty()) {
        napi_value httpProxy = NapiUtils::CreateObject(context->GetEnv());
        NapiUtils::SetStringPropertyUtf8(context->GetEnv(), httpProxy, HOST, host);

        NapiUtils::SetInt32Property(context->GetEnv(), httpProxy, PORT,
                                    context->config_->httpProxy_.GetPort());

        auto lists = context->config_->httpProxy_.GetExclusionList();
        napi_value exclusionList = NapiUtils::CreateArray(context->GetEnv(), lists.size());
        size_t index = 0;
        for (auto list : lists) {
            napi_value jsList = NapiUtils::CreateStringUtf8(context->GetEnv(), list);
            NapiUtils::SetArrayElement(context->GetEnv(), exclusionList, index++, jsList);
        }
        NapiUtils::SetNamedProperty(context->GetEnv(), httpProxy, EXCLUSION_LIST, exclusionList);

        NapiUtils::SetNamedProperty(context->GetEnv(), interfaceConfiguration, HTTP_PROXY, httpProxy);
    }
    return interfaceConfiguration;
}

bool ExecSetIfaceConfig(SetIfaceConfigContext *context)
{
    int32_t result = DelayedSingleton<EthernetClient>::GetInstance()->SetIfaceConfig(context->iface_, context->config_);
    if (result != NETMANAGER_EXT_SUCCESS) {
        NETMANAGER_EXT_LOGE("ExecSetIfaceConfig error, errorCode: %{public}d", result);
        context->SetErrorCode(result);
        return false;
    }
    return true;
}

napi_value SetIfaceConfigCallback(SetIfaceConfigContext *context)
{
    return NapiUtils::GetUndefined(context->GetEnv());
}

bool ExecIsIfaceActive(IsIfaceActiveContext *context)
{
    int32_t result =
        DelayedSingleton<EthernetClient>::GetInstance()->IsIfaceActive(context->iface_, context->ifActivate_);
    if (result != NETMANAGER_EXT_SUCCESS) {
        context->SetErrorCode(result);
        NETMANAGER_EXT_LOGE("ExecIsIfaceActive error, errorCode: %{public}d", result);
        return false;
    }
    return true;
}

napi_value IsIfaceActiveCallback(IsIfaceActiveContext *context)
{
    return NapiUtils::CreateInt32(context->GetEnv(), context->ifActivate_);
}

bool ExecGetAllActiveIfaces(GetAllActiveIfacesContext *context)
{
    int32_t result = DelayedSingleton<EthernetClient>::GetInstance()->GetAllActiveIfaces(context->ethernetNameList_);
    if (result != NETMANAGER_EXT_SUCCESS) {
        context->SetErrorCode(result);
        NETMANAGER_EXT_LOGE("ExecIsIfaceActive error, errorCode: %{public}d", result);
        return false;
    }
    return true;
}

napi_value GetAllActiveIfacesCallback(GetAllActiveIfacesContext *context)
{
    napi_value ifaces = NapiUtils::CreateArray(context->GetEnv(), context->ethernetNameList_.size());
    uint32_t index = 0;
    for (const auto &iface : context->ethernetNameList_) {
        napi_value ifaceStr = NapiUtils::CreateStringUtf8(context->GetEnv(), iface);
        NapiUtils::SetArrayElement(context->GetEnv(), ifaces, index++, ifaceStr);
    }
    return ifaces;
}

bool ExecGetDeviceInformation(GetDeviceInformationContext *context)
{
    int32_t result = DelayedSingleton<EthernetClient>::GetInstance()->GetDeviceInformation(context->deviceInfo_);
    if (result != NETMANAGER_EXT_SUCCESS) {
        NETMANAGER_EXT_LOGE("ExecGetDeviceInformation, err: %{public}d", result);
        context->SetErrorCode(result);
        return false;
    }
    return true;
}
 
napi_value GetDeviceInformationCallback(GetDeviceInformationContext *context)
{
    napi_value deviceInfoList = NapiUtils::CreateArray(context->GetEnv(), context->deviceInfo_.size());
    uint32_t index = 0;
    for (auto &eachInfo : context->deviceInfo_) {
        napi_value deviceInfo = NapiUtils::CreateObject(context->GetEnv());
        NapiUtils::SetStringPropertyUtf8(context->GetEnv(), deviceInfo, IFACE_NAME, eachInfo.ifaceName_);
        NapiUtils::SetStringPropertyUtf8(context->GetEnv(), deviceInfo, DEVICE_NAME, eachInfo.deviceName_);
        NapiUtils::SetInt32Property(context->GetEnv(), deviceInfo, CONNECTION_MODE, eachInfo.connectionMode_);
        NapiUtils::SetStringPropertyUtf8(context->GetEnv(), deviceInfo, SUPPLIER_NAME, eachInfo.supplierName_);
        NapiUtils::SetStringPropertyUtf8(context->GetEnv(), deviceInfo, SUPPLIER_ID, eachInfo.supplierId_);
        NapiUtils::SetStringPropertyUtf8(context->GetEnv(), deviceInfo, PRODUCT_NAME, eachInfo.productName_);
        NapiUtils::SetStringPropertyUtf8(context->GetEnv(), deviceInfo, MAX_RATE, eachInfo.maximumRate_);
        NapiUtils::SetArrayElement(context->GetEnv(), deviceInfoList, index++, deviceInfo);
    }
    return deviceInfoList;
}

#ifdef NETMANAGER_EXT_ETHERNET_ENABLE_DISABLE
bool ExecEnableEthernet(EnableEthernetContext *context)
{
    int32_t result = DelayedSingleton<EthernetClient>::GetInstance()->EnableEthernetInterface();
    if (result != NETMANAGER_EXT_SUCCESS) {
        NETMANAGER_EXT_LOGE("ExecEnableEthernet error, errorCode: %{public}d", result);
        int32_t napiCode = MapToNapiErrorCode(result);
        context->SetErrorCode(napiCode);
        return false;
    }
    return true;
}

napi_value EnableEthernetCallback(EnableEthernetContext *context)
{
    return NapiUtils::GetUndefined(context->GetEnv());
}

bool ExecDisableEthernet(DisableEthernetContext *context)
{
    int32_t result = DelayedSingleton<EthernetClient>::GetInstance()->DisableEthernetInterface();
    if (result != NETMANAGER_EXT_SUCCESS) {
        NETMANAGER_EXT_LOGE("ExecDisableEthernet error, errorCode: %{public}d", result);
        int32_t napiCode = MapToNapiErrorCode(result);
        context->SetErrorCode(napiCode);
        return false;
    }
    return true;
}

napi_value DisableEthernetCallback(DisableEthernetContext *context)
{
    return NapiUtils::GetUndefined(context->GetEnv());
}
#endif
} // namespace EthernetExec
} // namespace NetManagerStandard
} // namespace OHOS