/*
 * Copyright (c) 2023 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 "push_callback_stub.h"

#include "ans_log_wrapper.h"
#include "event_handler.h"
#include "ipc_types.h"
#include "message_parcel.h"
#include "push_callback_proxy.h"
#include "ans_inner_errors.h"
#include "ans_service_errors.h"
#include "nlohmann/json.hpp"

using namespace OHOS::AppExecFwk;
namespace OHOS {
namespace Notification {
PushCallBackStub::PushCallBackStub() {}

PushCallBackStub::~PushCallBackStub() {}

enum PushCheckErrCode : int32_t {
    SUCCESS = 0,
    FIXED_PARAMETER_INVALID = 1,
    NETWORK_UNREACHABLE = 2,
    SPECIFIED_NOTIFICATIONS_FAILED = 3,
    SYSTEM_ERROR = 4,
    OPTIONAL_PARAMETER_INVALID = 5,
    PUSH_CHECK_WEAK_NETWORK = 6,
};
constexpr int32_t MAX_LIVEVIEW_CONFIG_SIZE = 60;

ErrCode PushCallBackStub::ConvertPushCheckCodeToErrCode(int32_t pushCheckCode)
{
    ErrCode errCode;
    PushCheckErrCode checkCode = static_cast<PushCheckErrCode>(pushCheckCode);

    switch (checkCode) {
        case PushCheckErrCode::SUCCESS:
            errCode = ERR_OK;
            break;
        case PushCheckErrCode::FIXED_PARAMETER_INVALID:
            errCode = ERR_ANS_INNER_TASK_ERR;
            break;
        case PushCheckErrCode::NETWORK_UNREACHABLE:
            errCode = ERR_ANS_INNER_PUSH_CHECK_NETWORK_UNREACHABLE;
            break;
        case PushCheckErrCode::SPECIFIED_NOTIFICATIONS_FAILED:
            errCode = ERR_ANS_INNER_PUSH_CHECK_FAILED;
            break;
        case PushCheckErrCode::SYSTEM_ERROR:
            errCode = ERR_ANS_INNER_TASK_ERR;
            break;
        case PushCheckErrCode::OPTIONAL_PARAMETER_INVALID:
            errCode = ERR_ANS_INNER_PUSH_CHECK_EXTRAINFO_INVALID;
            break;
        case PushCheckErrCode::PUSH_CHECK_WEAK_NETWORK:
            errCode = ERR_ANS_INNER_CHECK_WEAK_NETWORK;
            break;
        default:
            errCode = ERR_ANS_INNER_PUSH_CHECK_FAILED;
            break;
    }
    return errCode;
}

int PushCallBackStub::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)
{
    ANS_LOGD("called");
    if (data.ReadInterfaceToken() != GetDescriptor()) {
        ANS_LOGE("local descriptor is not equal to remote");
        return ERR_INVALID_STATE;
    }
    switch (code) {
        case static_cast<uint32_t>(NotificationInterfaceCode::ON_CHECK_NOTIFICATION): {
            auto notificationData = data.ReadString();
            int32_t checkResult = ERR_ANS_INNER_TASK_ERR;

            std::shared_ptr<PushCallBackParam> pushCallBackParam = std::make_shared<PushCallBackParam>();
            int32_t result = this->OnCheckNotification(notificationData, pushCallBackParam);
            checkResult = ConvertPushCheckCodeToErrCode(result);
            ANS_LOGI("Push check result:%{public}d %{public}d,eventControl:%{public}s",
                result, checkResult, pushCallBackParam->eventControl.c_str());

            if (!reply.WriteInt32(checkResult)) {
                ANS_LOGE("Failed to write reply ");
                return ERR_INVALID_REPLY;
            }
            if (!reply.WriteString(pushCallBackParam->eventControl)) {
                ANS_LOGE("Failed to write reply ");
                return ERR_INVALID_REPLY;
            }
            return NO_ERROR;
        }
        case static_cast<uint32_t>(NotificationInterfaceCode::ON_CHECK_LIVEVIEW): {
            std::string requestId = data.ReadString();
            auto vsize = data.ReadUint64();
            vsize = (vsize < MAX_LIVEVIEW_CONFIG_SIZE) ? vsize : MAX_LIVEVIEW_CONFIG_SIZE;
            std::vector<std::string> bundlesName;
            for (uint64_t it = 0; it < vsize; ++it) {
                std::string bundle = data.ReadString();
                bundlesName.emplace_back(bundle);
            }
            int32_t checkResult = this->OnCheckLiveView(requestId, bundlesName);
            ANS_LOGI("Push check liveview: %{public}zu %{public}d.", bundlesName.size(), checkResult);
            if (!reply.WriteInt32(checkResult)) {
                ANS_LOGE("Failed to write reply ");
                return ERR_INVALID_REPLY;
            }
            return NO_ERROR;
        }
        default: {
            return IPCObjectStub::OnRemoteRequest(code, data, reply, option);
        }
    }
}

int32_t PushCallBackProxy::OnCheckNotification(
    const std::string &notificationData, const std::shared_ptr<PushCallBackParam> &pushCallBackParam)
{
    MessageParcel data;
    MessageParcel reply;
    MessageOption option;

    if (!data.WriteInterfaceToken(PushCallBackProxy::GetDescriptor())) {
        ANS_LOGE("Write interface token failed.");
        return false;
    }

    if (!data.WriteString(notificationData)) {
        ANS_LOGE("Connect done element error.");
        return false;
    }

    auto remote = Remote();
    if (remote == nullptr) {
        ANS_LOGE("null remote");
        return false;
    }

    int error = remote->SendRequest(static_cast<uint32_t>(NotificationInterfaceCode::ON_CHECK_NOTIFICATION),
        data, reply, option);
    if (error != NO_ERROR) {
        ANS_LOGE("error: %{public}d", error);
        return false;
    }

    int result = reply.ReadInt32();
    std::string eventControl;
    if (reply.ReadString(eventControl)) {
        HandleEventControl(eventControl, pushCallBackParam);
    }
    return result;
}

int32_t PushCallBackProxy::OnCheckLiveView(const std::string& requestId, const std::vector<std::string>& bundles)
{
    MessageParcel data;
    MessageParcel reply;
    MessageOption option;

    if (!data.WriteInterfaceToken(PushCallBackProxy::GetDescriptor())) {
        ANS_LOGE("Write interface token failed.");
        return ERROR_IPC_ERROR;
    }

    if (!data.WriteString(requestId)) {
        ANS_LOGE("Connect done element error.");
        return ERROR_IPC_ERROR;
    }

    if (!data.WriteUint64(bundles.size())) {
        ANS_LOGE("Failed to write the size of bundles");
        return ERROR_IPC_ERROR;
    }

    for (auto item : bundles) {
        if (!data.WriteString(item)) {
            ANS_LOGE("Failed to write bundle name");
            return ERROR_IPC_ERROR;
        }
    }

    auto remote = Remote();
    if (remote == nullptr) {
        ANS_LOGE("null remote");
        return ERROR_IPC_ERROR;
    }

    int error = remote->SendRequest(static_cast<uint32_t>(NotificationInterfaceCode::ON_CHECK_LIVEVIEW),
        data, reply, option);
    if (error != NO_ERROR) {
        ANS_LOGE("error: %{public}d", error);
        return ERROR_IPC_ERROR;
    }

    int result = reply.ReadInt32();
    return result;
}

void PushCallBackProxy::HandleEventControl(
    std::string eventControl, const std::shared_ptr<PushCallBackParam> &pushCallBackParam)
{
    if (pushCallBackParam == nullptr) {
        ANS_LOGE("null pushCallBackParam");
        return;
    }
    std::string event = pushCallBackParam->event;
    ANS_LOGI("eventControl:%{public}s,event:%{public}s", eventControl.c_str(), event.c_str());
    if (eventControl.empty() || !nlohmann::json::accept(eventControl)) {
        return;
    }
    auto jsonObject = nlohmann::json::parse(eventControl);
    if (jsonObject.is_null() || !jsonObject.is_object()) {
        ANS_LOGE("jsonObject is not right");
        return;
    }
    std::string key = event;
    if (event.empty() || jsonObject.find(event) == jsonObject.cend()) {
        ANS_LOGW("Event is empty or not found, fallback to ALL");
        if (jsonObject.find("ALL") == jsonObject.cend()) {
            ANS_LOGW("ALL has not eventControl");
            return;
        }
        key = "ALL";
    }
    pushCallBackParam->eventControl = jsonObject.at(key).dump(-1, ' ',
        false, nlohmann::json::error_handler_t::replace);
}
} // namespace Notification
} // namespace OHOS