//
// Copyright 2026 gRPC authors.
//
// 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.
//

#ifndef GRPC_SRC_CORE_CREDENTIALS_CALL_REGIONAL_ACCESS_BOUNDARY_FETCHER_H
#define GRPC_SRC_CORE_CREDENTIALS_CALL_REGIONAL_ACCESS_BOUNDARY_FETCHER_H

#include <grpc/event_engine/event_engine.h>
#include <grpc/support/port_platform.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>

#include <optional>
#include <string>
#include <variant>
#include <vector>

#include "src/core/call/metadata.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/util/backoff.h"
#include "src/core/util/dual_ref_counted.h"
#include "src/core/util/http_client/httpcli.h"
#include "src/core/util/ref_counted_ptr.h"
#include "absl/status/statusor.h"

namespace grpc_core {

class RegionalAccessBoundaryFetcher final
    : public DualRefCounted<RegionalAccessBoundaryFetcher> {
 public:
  static RefCountedPtr<RegionalAccessBoundaryFetcher> Create(
      absl::string_view lookup_url,
      std::shared_ptr<grpc_event_engine::experimental::EventEngine>
          event_engine = nullptr,
      std::optional<BackOff::Options> backoff_options = std::nullopt);

  explicit RegionalAccessBoundaryFetcher(
      URI lookup_uri,
      std::shared_ptr<grpc_event_engine::experimental::EventEngine>
          event_engine = nullptr,
      std::optional<BackOff::Options> backoff_options = std::nullopt);

  // Attaches regional access boundary header (x-allowed-locations) to the
  // initial metadata if available, otherwise initiates non-blocking,
  // asynchronous fetch of regional access boundary if not already cached or in
  // flight.
  void Fetch(absl::string_view access_token, ClientMetadata& initial_metadata);

  // Cancels any pending fetch of regional access boundary which must be called
  // during destruction of any CallCredential which supports regional access
  // boundary to avoid memory leaks from pending http requests.
  void Orphaned() override;

 private:
  friend class RegionalAccessBoundaryFetcherTest;

  struct RegionalAccessBoundary {
    Slice encoded_locations;
    Timestamp expiration;
  };

  class Request;

  void OnFetchSuccess(Slice encoded_locations);
  void OnFetchFailure(grpc_error_handle error, int http_status,
                      absl::string_view response_body);

  std::shared_ptr<grpc_event_engine::experimental::EventEngine> event_engine_;
  const URI lookup_uri_;
  Mutex cache_mu_;
  std::optional<RegionalAccessBoundary> cache_ ABSL_GUARDED_BY(&cache_mu_);
  Timestamp next_fetch_time_ ABSL_GUARDED_BY(&cache_mu_) = Timestamp::InfPast();
  BackOff backoff_ ABSL_GUARDED_BY(&cache_mu_);
  OrphanablePtr<Request> pending_request_ ABSL_GUARDED_BY(&cache_mu_);
  bool shutdown_ ABSL_GUARDED_BY(&cache_mu_) = false;
};

class RegionalAccessBoundaryFetcher::Request final
    : public InternallyRefCounted<Request> {
 public:
  Request(WeakRefCountedPtr<RegionalAccessBoundaryFetcher> fetcher,
          absl::string_view access_token);

  ~Request() override;

  void Start();

  // Cancels any pending http request which must be called during
  // destruction to avoid memory leaks from pending http requests.
  void Orphan() override;

 private:
  static void OnResponseWrapper(void* arg, grpc_error_handle error);
  void OnResponse(grpc_error_handle error);

  grpc_http_response response_;
  OrphanablePtr<HttpRequest> http_request_;
  std::string access_token_;
  grpc_polling_entity pollent_;
  WeakRefCountedPtr<RegionalAccessBoundaryFetcher> fetcher_;
  grpc_closure closure_;
};

class EmailFetcher final : public DualRefCounted<EmailFetcher> {
 public:
  explicit EmailFetcher(
      std::shared_ptr<grpc_event_engine::experimental::EventEngine>
          event_engine = nullptr);

  ~EmailFetcher() override;

  static bool IsValidEmail(absl::string_view email);

  void StartEmailFetch();

  // Wrapper for RAB fetcher.
  void Fetch(absl::string_view token, ClientMetadata& metadata);

  void Orphaned() override;

 private:
  class EmailRequest;

  void OnEmailFetchComplete(absl::string_view email);
  void OnEmailFetchError(grpc_error_handle error);

  std::shared_ptr<grpc_event_engine::experimental::EventEngine> event_engine_;
  Mutex mu_;

  // Either a pending email request or an RAB fetcher.
  std::variant<OrphanablePtr<EmailRequest>,
               RefCountedPtr<RegionalAccessBoundaryFetcher>>
      state_ ABSL_GUARDED_BY(&mu_);
  BackOff backoff_ ABSL_GUARDED_BY(&mu_) =
      BackOff(BackOff::Options()
                  .set_initial_backoff(Duration::Seconds(15))
                  .set_multiplier(1.75)
                  .set_jitter(0.1)
                  .set_max_backoff(Duration::Minutes(2)));
  Timestamp next_fetch_earliest_time_ ABSL_GUARDED_BY(&mu_) =
      Timestamp::InfPast();
};

}  // namespace grpc_core

#endif  // GRPC_SRC_CORE_CREDENTIALS_CALL_REGIONAL_ACCESS_BOUNDARY_FETCHER_H