// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/facilitated_payments/android/facilitated_payments_api_client_android.h"

#include <memory>
#include <utility>

#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "components/facilitated_payments/android/secure_payload_android.h"
#include "components/facilitated_payments/core/utils/facilitated_payments_utils.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/render_frame_host.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "components/facilitated_payments/android/java/jni_headers/AccountLinkingResult_jni.h"
#include "components/facilitated_payments/android/java/jni_headers/FacilitatedPaymentsApiClientBridge_jni.h"

namespace payments::facilitated {

std::unique_ptr<FacilitatedPaymentsApiClient>
LazyInitFacilitatedPaymentsApiClient(
    content::GlobalRenderFrameHostId render_frame_host_id) {
  content::RenderFrameHost* render_frame_host =
      content::RenderFrameHost::FromID(render_frame_host_id);
  return render_frame_host
             ? std::make_unique<FacilitatedPaymentsApiClientAndroid>(
                   render_frame_host)
             : nullptr;
}

// Declared in the cross-platform header
// `facilitated_payments_api_client_factory.h`.
FacilitatedPaymentsApiClientCreator GetFacilitatedPaymentsApiClientCreator(
    content::GlobalRenderFrameHostId render_frame_host_id) {
  return base::BindRepeating(&LazyInitFacilitatedPaymentsApiClient,
                             render_frame_host_id);
}

FacilitatedPaymentsApiClientAndroid::FacilitatedPaymentsApiClientAndroid(
    content::RenderFrameHost* render_frame_host)
    : java_bridge_(Java_FacilitatedPaymentsApiClientBridge_Constructor(
          base::android::AttachCurrentThread(),
          reinterpret_cast<intptr_t>(this),
          render_frame_host->GetJavaRenderFrameHost())) {}

FacilitatedPaymentsApiClientAndroid::~FacilitatedPaymentsApiClientAndroid() {
  Java_FacilitatedPaymentsApiClientBridge_resetNativePointer(
      base::android::AttachCurrentThread(), java_bridge_);
}

void FacilitatedPaymentsApiClientAndroid::IsAvailable(
    base::OnceCallback<void(bool)> callback) {
  DCHECK(!IsAnyCallbackPending());

  is_available_callback_ = std::move(callback);
  Java_FacilitatedPaymentsApiClientBridge_isAvailable(
      base::android::AttachCurrentThread(), java_bridge_);
}

bool FacilitatedPaymentsApiClientAndroid::IsAvailableSync() {
  return Java_FacilitatedPaymentsApiClientBridge_isAvailableSync(
      base::android::AttachCurrentThread(), java_bridge_);
}

void FacilitatedPaymentsApiClientAndroid::GetClientToken(
    base::OnceCallback<void(std::vector<uint8_t>)> callback) {
  DCHECK(!IsAnyCallbackPending());

  get_client_token_callback_ = std::move(callback);
  Java_FacilitatedPaymentsApiClientBridge_getClientToken(
      base::android::AttachCurrentThread(), java_bridge_);
}

void FacilitatedPaymentsApiClientAndroid::InvokePurchaseAction(
    CoreAccountInfo primary_account,
    const SecurePayload& secure_payload,
    base::OnceCallback<void(PurchaseActionResult)> callback) {
  DCHECK(!IsAnyCallbackPending());

  purchase_action_callback_ = std::move(callback);
  JNIEnv* env = base::android::AttachCurrentThread();
  Java_FacilitatedPaymentsApiClientBridge_invokePurchaseAction(
      env, java_bridge_, primary_account,
      ConvertSecurePayloadToJavaObject(secure_payload));
}

void FacilitatedPaymentsApiClientAndroid::InvokeInstrumentManager(
    CoreAccountInfo primary_account,
    const std::vector<uint8_t>& action_token,
    base::OnceCallback<void(AccountLinkingResult)> callback) {
  DCHECK(!IsAnyCallbackPending());

  invoke_instrument_manager_callback_ = std::move(callback);
  JNIEnv* env = base::android::AttachCurrentThread();
  Java_FacilitatedPaymentsApiClientBridge_invokeInstrumentManager(
      env, java_bridge_, primary_account,
      base::android::ToJavaByteArray(env, action_token));
}

void FacilitatedPaymentsApiClientAndroid::OnIsAvailable(JNIEnv* env,
                                                        bool is_api_available) {
  if (is_available_callback_) {
    std::move(is_available_callback_).Run(is_api_available);
  }
}

void FacilitatedPaymentsApiClientAndroid::OnGetClientToken(
    JNIEnv* env,
    const base::android::JavaRef<jbyteArray>& jclient_token_byte_array) {
  if (get_client_token_callback_) {
    std::vector<uint8_t> client_token;
    if (jclient_token_byte_array) {
      base::android::JavaByteArrayToByteVector(env, jclient_token_byte_array,
                                               &client_token);
    }
    std::move(get_client_token_callback_).Run(std::move(client_token));
  }
}

void FacilitatedPaymentsApiClientAndroid::OnPurchaseActionResultEnum(
    JNIEnv* env,
    int32_t purchase_action_result) {
  if (!purchase_action_callback_ ||
      purchase_action_result <
          static_cast<int>(PurchaseActionResult::kCouldNotInvoke) ||
      purchase_action_result >
          static_cast<int>(PurchaseActionResult::kResultCanceled)) {
    return;
  }

  std::move(purchase_action_callback_)
      .Run(static_cast<PurchaseActionResult>(purchase_action_result));
}

void FacilitatedPaymentsApiClientAndroid::OnInvokeInstrumentManagerResult(
    JNIEnv* env,
    const base::android::JavaRef<jobject>& jaccount_linking_result) {
  if (!invoke_instrument_manager_callback_ || !jaccount_linking_result) {
    return;
  }

  bool is_successful =
      Java_AccountLinkingResult_isSuccessful(env, jaccount_linking_result);
  int64_t instrument_id =
      Java_AccountLinkingResult_getInstrumentId(env, jaccount_linking_result);
  int error_code =
      Java_AccountLinkingResult_getErrorCode(env, jaccount_linking_result);

  if (error_code <
          static_cast<int>(AccountLinkingResultCode::kCouldNotInvoke) ||
      error_code > static_cast<int>(AccountLinkingResultCode::kResultError)) {
    return;
  }

  std::move(invoke_instrument_manager_callback_)
      .Run(AccountLinkingResult{
          is_successful, instrument_id,
          static_cast<AccountLinkingResultCode>(error_code)});
}

bool FacilitatedPaymentsApiClientAndroid::IsAnyCallbackPending() const {
  return !is_available_callback_.is_null() ||
         !get_client_token_callback_.is_null() ||
         !purchase_action_callback_.is_null() ||
         !invoke_instrument_manager_callback_.is_null();
}

}  // namespace payments::facilitated

DEFINE_JNI(FacilitatedPaymentsApiClientBridge)
