// 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.

#ifndef CHROME_BROWSER_WEBAUTHN_ENCLAVE_MANAGER_INTERFACE_H_
#define CHROME_BROWSER_WEBAUTHN_ENCLAVE_MANAGER_INTERFACE_H_

#include "base/functional/callback_forward.h"
#include "base/observer_list.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/trusted_vault/trusted_vault_connection.h"

class EnclaveManager;
class GaiaId;

class EnclaveManagerInterface : public KeyedService {
 public:
  // Many actions report results using a `Callback`. The boolean argument
  // is true if the operation is successful and false otherwise.
  // These callbacks never hairpin. (I.e. are never called before the function
  // that they were passed to returns.)
  using Callback = base::OnceCallback<void(bool)>;

  // LINT.IfChange(GpmPinAvailability)
  //
  // An enum that expresses whether a GPM PIN is set on an account.
  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  enum class GpmPinAvailability {
    // The PIN is set but not usable (there are 0 remaining attempts for
    // entering the pin).
    kGpmPinSetButNotUsable = 0,
    // The PIN is set and usable (there are > 0 remaining attempts for entering
    // the pin).
    kGpmPinSetAndUsable = 1,
    // The PIN is unset.
    kGpmPinUnset = 2,
    kMaxValue = kGpmPinUnset,
  };
  // LINT.ThenChange(//tools/metrics/histograms/metadata/webauthn/enums.xml:GpmPinAvailability)

  using GpmPinAvailabilityCallback =
      base::OnceCallback<void(GpmPinAvailability)>;

  // An enum that expresses the outcome of the operation of storing the
  // opportunistically retrieved keys.
  enum class OutOfContextRecoveryOutcome {
    // The key has been successfully stored and the device has been added to
    // account.
    kStoreKeysFromOpportunisticFlowSucceeded,
    // There was a failure while adding the device to account.
    kStoreKeysFromOpportunisticFlowFailed,
    // The key has been ignored because the device has been already registered
    // with the enclave.
    kStoreKeysFromOpportunisticFlowIgnoredRedundant,
  };

  class Observer : public base::CheckedObserver {
   public:
    // `OnKeysStored` is called when MagicArch provides keys to the
    // EnclaveManager by calling `StoreKeys`. `gaia_id` is the account
    // identifier for the keys that were stored. Clients should verify this
    // matches the account they expect.
    virtual void OnKeysStored(const GaiaId& gaia_id) {}

    // `OnStateUpdated` is called from `EnclaveManager::Stopped()` - indicating
    // that the state machine reached its final state (so the state of the
    // enclave manager might be updated now, e.g. it might become ready).
    virtual void OnStateUpdated() {}

    // `OnOutOfContextRecoveryCompletion` informs observers about the outcome of
    // the operation of storing the opportunistically retrieved keys.
    virtual void OnOutOfContextRecoveryCompletion(
        OutOfContextRecoveryOutcome outcome) {}
  };

  EnclaveManagerInterface() = default;
  EnclaveManagerInterface(const EnclaveManagerInterface&) = delete;
  EnclaveManagerInterface& operator=(const EnclaveManagerInterface&) = delete;
  ~EnclaveManagerInterface() override = default;

  // Return the full `EnclaveManager` interface. This will crash the address
  // space if run on an `EnclaveManagerInterface` instance that is not backed
  // by a real `EnclaveManager`, i.e. when it's a mock.
  virtual EnclaveManager* GetEnclaveManager();

  // Returns true if the current user has been registered with the enclave.
  virtual bool IsRegistered() const = 0;

  // Returns true if the persistent state has been loaded from the disk. (Or
  // else the loading failed and an empty state is being used.)
  virtual bool IsLoaded() const = 0;

  // Returns true if the current user has joined the security domain and has one
  // or more wrapped security domain secrets available. (This implies
  // `IsRegistered`.)
  virtual bool IsReady() const = 0;

  // Send a request to the enclave to delete the registration for the current
  // user, erase local keys, and erase local state for the user. Safe to call in
  // any state and is a no-op if no registration exists.
  virtual void Unenroll(Callback callback) = 0;

  // Asynchronously checks if the current user has a GPM PIN. The caller must
  // keep the returned Request object alive until the callback is run.
  // Destroying the Request object will cancel the operation.
  [[nodiscard]] virtual std::unique_ptr<
      trusted_vault::TrustedVaultConnection::Request>
  CheckGpmPinAvailability(GpmPinAvailabilityCallback callback) = 0;

  virtual void LoadAfterDelay(base::TimeDelta delay,
                              base::OnceClosure closure) = 0;

  virtual void AddObserver(Observer* observer) = 0;
  virtual void RemoveObserver(Observer* observer) = 0;
};

#endif  // CHROME_BROWSER_WEBAUTHN_ENCLAVE_MANAGER_INTERFACE_H_
