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

#ifndef COMPONENTS_TRUSTED_VAULT_ANDROID_TRUSTED_VAULT_CLIENT_ANDROID_H_
#define COMPONENTS_TRUSTED_VAULT_ANDROID_TRUSTED_VAULT_CLIENT_ANDROID_H_

#include <map>
#include <memory>
#include <string>
#include <variant>
#include <vector>

#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/functional/callback.h"
#include "base/observer_list.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/trusted_vault/trusted_vault_client.h"

// JNI bridge for a Java implementation of the TrustedVaultClient interface,
// used on Android.
//
// This class must be accessed from the UI thread.
class TrustedVaultClientAndroid : public trusted_vault::TrustedVaultClient {
 public:
  // Callback that returns account information identified by |gaia_id| or an
  // empty CoreAccountInfo if the account is not found.
  using GetAccountInfoByGaiaIdCallback =
      base::RepeatingCallback<CoreAccountInfo(const GaiaId& gaia_id)>;

  explicit TrustedVaultClientAndroid(
      const GetAccountInfoByGaiaIdCallback& gaia_account_info_by_gaia_id_cb);
  ~TrustedVaultClientAndroid() override;

  TrustedVaultClientAndroid(const TrustedVaultClientAndroid&) = delete;
  TrustedVaultClientAndroid& operator=(const TrustedVaultClientAndroid&) =
      delete;

  // Called from Java to notify the completion of a FetchKeys() operation
  // previously initiated from C++ and identified by |request_id|. |gaia_id|
  // must match the user's ID.
  void FetchKeysCompleted(JNIEnv* env,
                          int32_t request_id,
                          const std::string& gaia_id,
                          const base::android::JavaRef<jobjectArray>& keys);

  // Called from Java to notify the completion of a MarkLocalKeysAsStale()
  // operation previously initiated from C++ and identified by |request_id|.
  void MarkLocalKeysAsStaleCompleted(JNIEnv* env,
                                     int32_t request_id,
                                     bool succeeded);

  // Called from Java to notify the completion of a
  // GetIsRecoverabilityDegraded() operation previously initiated from C++ and
  // identified by |request_id|.
  void GetIsRecoverabilityDegradedCompleted(JNIEnv* env,
                                            int32_t request_id,
                                            bool is_degraded);

  // Called from Java to notify the completion of a
  // AddTrustedRecoveryMethod() operation previously initiated from C++ and
  // identified by |request_id|.
  void AddTrustedRecoveryMethodCompleted(JNIEnv* env, int32_t request_id);

  // Called from Java to notify that the keys in the vault may have changed.
  void NotifyKeysChanged(JNIEnv* env, std::optional<int32_t> trigger);

  // Called from Java to notify that the recoverability of the vault may have
  // changed.
  void NotifyRecoverabilityChanged(JNIEnv* env);

  // TrustedVaultClient implementation.
  void AddObserver(Observer* observer) override;
  void RemoveObserver(Observer* observer) override;
  void FetchKeys(
      const CoreAccountInfo& account_info,
      base::OnceCallback<void(const std::vector<std::vector<uint8_t>>&)> cb)
      override;
  void StoreKeys(
      const GaiaId& gaia_id,
      const std::vector<std::vector<uint8_t>>& keys,
      int last_key_version,
      std::optional<trusted_vault::TrustedVaultUserActionTriggerForUMA> trigger)
      override;
  void MarkLocalKeysAsStale(const CoreAccountInfo& account_info,
                            base::OnceCallback<void(bool)> cb) override;
  void GetIsRecoverabilityDegraded(const CoreAccountInfo& account_info,
                                   base::OnceCallback<void(bool)> cb) override;
  void AddTrustedRecoveryMethod(const GaiaId& gaia_id,
                                const std::vector<uint8_t>& public_key,
                                int method_type_hint,
                                base::OnceClosure cb) override;
  void ClearLocalDataForAccount(const CoreAccountInfo& account_info) override;

 private:
  // Struct representing an in-flight FetchKeys() call invoked from C++.
  struct OngoingFetchKeys {
    OngoingFetchKeys(
        const CoreAccountInfo& account_info,
        base::OnceCallback<void(const std::vector<std::vector<uint8_t>>&)>
            callback);
    OngoingFetchKeys(OngoingFetchKeys&&);
    ~OngoingFetchKeys();

    const CoreAccountInfo account_info;
    base::OnceCallback<void(const std::vector<std::vector<uint8_t>>&)> callback;
  };

  // Struct representing an in-flight MarkLocalKeysAsStale() call invoked from
  // C++.
  struct OngoingMarkLocalKeysAsStale {
    explicit OngoingMarkLocalKeysAsStale(
        base::OnceCallback<void(bool)> callback);
    OngoingMarkLocalKeysAsStale(OngoingMarkLocalKeysAsStale&&);
    ~OngoingMarkLocalKeysAsStale();

    base::OnceCallback<void(bool)> callback;
  };

  // Struct representing an in-flight GetIsRecoverabilityDegraded() invoked from
  // C++.
  struct OngoingGetIsRecoverabilityDegraded {
    explicit OngoingGetIsRecoverabilityDegraded(
        base::OnceCallback<void(bool)> callback);
    OngoingGetIsRecoverabilityDegraded(OngoingGetIsRecoverabilityDegraded&&);
    ~OngoingGetIsRecoverabilityDegraded();

    base::OnceCallback<void(bool)> callback;
  };

  // Struct representing an in-flight AddTrustedRecoveryMethod() invoked from
  // C++.
  struct OngoingAddTrustedRecoveryMethod {
    explicit OngoingAddTrustedRecoveryMethod(base::OnceClosure callback);
    OngoingAddTrustedRecoveryMethod(OngoingAddTrustedRecoveryMethod&&);
    ~OngoingAddTrustedRecoveryMethod();

    base::OnceClosure callback;
  };

  using RequestId = int32_t;
  using OngoingRequest = std::variant<OngoingFetchKeys,
                                      OngoingMarkLocalKeysAsStale,
                                      OngoingGetIsRecoverabilityDegraded,
                                      OngoingAddTrustedRecoveryMethod>;

  RequestId RegisterNewOngoingRequest(OngoingRequest request);
  OngoingRequest GetAndUnregisterOngoingRequest(RequestId id);

  const GetAccountInfoByGaiaIdCallback gaia_account_info_by_gaia_id_cb_;

  // Each in-flight request gets assigned an auto-increment ID and gets cached
  // in a map, until the response from Java is received.
  RequestId last_request_id_ = 0;
  std::map<RequestId, OngoingRequest> ongoing_requests_;

  base::ObserverList<Observer> observer_list_;
};

#endif  // COMPONENTS_TRUSTED_VAULT_ANDROID_TRUSTED_VAULT_CLIENT_ANDROID_H_
