// Copyright (c) 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef QUICHE_BLIND_SIGN_AUTH_CACHED_BLIND_SIGN_AUTH_H_
#define QUICHE_BLIND_SIGN_AUTH_CACHED_BLIND_SIGN_AUTH_H_

#include <string>
#include <vector>

#include "absl/status/statusor.h"
#include "absl/types/span.h"
#include "quiche/blind_sign_auth/blind_sign_auth_interface.h"
#include "quiche/common/platform/api/quiche_export.h"
#include "quiche/common/platform/api/quiche_mutex.h"
#include "quiche/common/quiche_circular_deque.h"

namespace quiche {

inline constexpr int kBlindSignAuthRequestMaxTokens = 1024;

// CachedBlindSignAuth caches signed tokens generated by BlindSignAuth.
// This class does not guarantee that tokens returned are fresh.
// Tokens may be stale if the backend has rotated its signing key since tokens
// were generated.
// This class is thread-safe.
class QUICHE_EXPORT CachedBlindSignAuth : public BlindSignAuthInterface {
 public:
  CachedBlindSignAuth(
      BlindSignAuthInterface* blind_sign_auth,
      int max_tokens_per_request = kBlindSignAuthRequestMaxTokens)
      : blind_sign_auth_(blind_sign_auth),
        max_tokens_per_request_(max_tokens_per_request) {}

  // Returns signed unblinded tokens and expiration time in a callback.
  // Tokens are single-use. They will not be usable after the expiration time.
  //
  // The GetTokens callback may be called synchronously on the calling thread,
  // or asynchronously on BlindSignAuth's BlindSignHttpInterface thread.
  // The GetTokens callback must not acquire any locks that the calling thread
  // owns, otherwise the callback will deadlock.
  void GetTokens(std::string oauth_token, int num_tokens,
                 ProxyLayer proxy_layer, SignedTokenCallback callback) override;

  // Removes all tokens in the cache.
  void ClearCache() {
    QuicheWriterMutexLock lock(&mutex_);
    cached_tokens_.clear();
  }

 private:
  void HandleGetTokensResponse(
      SignedTokenCallback callback, int num_tokens,
      absl::StatusOr<absl::Span<BlindSignToken>> tokens);
  std::vector<BlindSignToken> CreateOutputTokens(int num_tokens)
      QUICHE_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
  void RemoveExpiredTokens() QUICHE_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  BlindSignAuthInterface* blind_sign_auth_;
  int max_tokens_per_request_;
  QuicheMutex mutex_;
  QuicheCircularDeque<BlindSignToken> cached_tokens_ QUICHE_GUARDED_BY(mutex_);
};

}  // namespace quiche

#endif  // QUICHE_BLIND_SIGN_AUTH_CACHED_BLIND_SIGN_AUTH_H_
