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

#ifndef CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_SCHEDULER_H_
#define CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_SCHEDULER_H_

#include <map>
#include <string>
#include <utility>
#include <vector>

#include "base/containers/circular_deque.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "content/browser/background_fetch/background_fetch_data_manager_observer.h"
#include "content/browser/background_fetch/background_fetch_event_dispatcher.h"
#include "content/browser/background_fetch/background_fetch_registration_id.h"
#include "content/browser/service_worker/service_worker_context_core_observer.h"
#include "content/common/content_export.h"
#include "third_party/blink/public/mojom/background_fetch/background_fetch.mojom.h"

namespace blink {
class StorageKey;
}  // namespace blink

namespace content {

class BackgroundFetchDataManager;
class BackgroundFetchDelegateProxy;
class BackgroundFetchJobController;
class BackgroundFetchRegistrationId;
class BackgroundFetchRegistrationNotifier;
class BackgroundFetchRequestInfo;
class DevToolsBackgroundServicesContextImpl;

// Maintains a list of Controllers and chooses which ones should launch new
// downloads.
class CONTENT_EXPORT BackgroundFetchScheduler
    : public BackgroundFetchDataManagerObserver,
      public ServiceWorkerContextCoreObserver {
 public:
  BackgroundFetchScheduler(
      BackgroundFetchContext* background_fetch_context,
      BackgroundFetchDataManager* data_manager,
      BackgroundFetchRegistrationNotifier* registration_notifier,
      BackgroundFetchDelegateProxy* delegate_proxy,
      DevToolsBackgroundServicesContextImpl& devtools_context,
      scoped_refptr<ServiceWorkerContextWrapper> service_worker_context);

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

  ~BackgroundFetchScheduler() override;

  // Aborts the background fetch identified by |registration_id|.
  // Must only be used for background fetches aborted by the developer,
  // other cases are handled elsewhere.
  void Abort(
      const BackgroundFetchRegistrationId& registration_id,
      blink::mojom::BackgroundFetchFailureReason failure_reason,
      blink::mojom::BackgroundFetchRegistrationService::AbortCallback callback);

  // BackgroundFetchDataManagerObserver implementation.
  void OnRegistrationCreated(
      const BackgroundFetchRegistrationId& registration_id,
      const blink::mojom::BackgroundFetchRegistrationData& registration_data,
      blink::mojom::BackgroundFetchOptionsPtr options,
      const SkBitmap& icon,
      int num_requests,
      bool start_paused,
      net::IsolationInfo isolation_info) override;
  void OnRegistrationLoadedAtStartup(
      const BackgroundFetchRegistrationId& registration_id,
      const blink::mojom::BackgroundFetchRegistrationData& registration_data,
      blink::mojom::BackgroundFetchOptionsPtr options,
      const SkBitmap& icon,
      int num_completed_requests,
      int num_requests,
      std::vector<scoped_refptr<BackgroundFetchRequestInfo>>
          active_fetch_requests,
      std::optional<net::IsolationInfo> isolation_info) override;
  void OnServiceWorkerDatabaseCorrupted(
      int64_t service_worker_registration_id) override;
  void OnRegistrationQueried(
      const BackgroundFetchRegistrationId& registration_id,
      blink::mojom::BackgroundFetchRegistrationData* registration_data)
      override;
  void OnRequestCompleted(const std::string& unique_id,
                          blink::mojom::FetchAPIRequestPtr request,
                          blink::mojom::FetchAPIResponsePtr response) override;

  // ServiceWorkerContextCoreObserver implementation.
  void OnRegistrationDeleted(int64_t registration_id,
                             const GURL& pattern,
                             const blink::StorageKey& key) override;
  void OnStorageWiped() override;

  // Called by BackgroundFetchContext during shutdown.
  void Shutdown();

 private:
  friend class BackgroundFetchJobControllerTest;
  friend class BackgroundFetchSchedulerTest;
  enum class Event;

  // Schedules a download, if possible, and returns whether successful.
  bool ScheduleDownload();

  // Helper method to abandon ongoing fetches for a given service worker.
  // Abandons all of them if |service_worker_registration_id| is set to
  // blink::mojom::kInvalidServiceWorkerRegistrationId.
  void AbortFetches(int64_t service_worker_registration_id);

  // Returns the active job controller, if any, for |registration_id| or
  // |unique_id|. Returns nullptr if the job isn't currently active.
  BackgroundFetchJobController* GetActiveController(
      const BackgroundFetchRegistrationId& registration_id);
  BackgroundFetchJobController* GetActiveController(
      const std::string& unique_id);

  std::unique_ptr<BackgroundFetchJobController> CreateInitializedController(
      const BackgroundFetchRegistrationId& registration_id,
      const blink::mojom::BackgroundFetchRegistrationData& registration_data,
      blink::mojom::BackgroundFetchOptionsPtr options,
      const SkBitmap& icon,
      int num_completed_requests,
      int num_requests,
      std::vector<scoped_refptr<BackgroundFetchRequestInfo>>
          active_fetch_requests,
      bool start_paused,
      std::optional<net::IsolationInfo> isolation_info);

  void DidStartRequest(const BackgroundFetchRegistrationId& registration_id,
                       const BackgroundFetchRequestInfo* request_info);
  void DidCompleteRequest(
      const BackgroundFetchRegistrationId& registration_id,
      scoped_refptr<BackgroundFetchRequestInfo> request_info);

  void FinishJob(
      const BackgroundFetchRegistrationId& registration_id,
      blink::mojom::BackgroundFetchFailureReason failure_reason,
      base::OnceCallback<void(blink::mojom::BackgroundFetchError)> callback);

  void DidMarkForDeletion(
      const BackgroundFetchRegistrationId& registration_id,
      bool job_started,
      base::OnceCallback<void(blink::mojom::BackgroundFetchError)> callback,
      blink::mojom::BackgroundFetchError error,
      blink::mojom::BackgroundFetchFailureReason failure_reason);

  void CleanupRegistration(
      const BackgroundFetchRegistrationId& registration_id);

  void DispatchClickEvent(const std::string& unique_id);

  // Information needed to send over to the
  // DevToolsBackgroundServicesContextImpl. |event| is an enum describing the
  // stage of the fetch. |request_info| is nullptr if not available at the
  // moment. Any additional data to log can be passed through the |metadata|
  // map.
  void LogBackgroundFetchEventForDevTools(
      Event event,
      const BackgroundFetchRegistrationId& registration_id,
      const BackgroundFetchRequestInfo* request_info,
      std::map<std::string, std::string> metadata = {});

  // Owned by BackgroundFetchContext.
  raw_ptr<BackgroundFetchDataManager, DanglingUntriaged> data_manager_;
  raw_ptr<BackgroundFetchRegistrationNotifier> registration_notifier_;
  raw_ptr<BackgroundFetchDelegateProxy, DanglingUntriaged> delegate_proxy_;
  raw_ptr<DevToolsBackgroundServicesContextImpl> devtools_context_;

  BackgroundFetchEventDispatcher event_dispatcher_;

  // The order in which to process the job controllers.
  base::circular_deque<BackgroundFetchRegistrationId> controller_ids_;

  // Map from background fetch registration |unique_id|s to active job
  // controllers. Must be destroyed before |data_manager_|, |scheduler_| and
  // |registration_notifier_|.
  std::map<std::string, std::unique_ptr<BackgroundFetchJobController>>
      job_controllers_;

  // The current fetch job controllers that are being processed.
  base::circular_deque<raw_ptr<BackgroundFetchJobController>>
      active_controllers_;

  struct RegistrationData {
    RegistrationData(
        const BackgroundFetchRegistrationId& registration_id,
        blink::mojom::BackgroundFetchRegistrationDataPtr registration);
    ~RegistrationData();

    BackgroundFetchRegistrationId registration_id;
    blink::mojom::BackgroundFetchRegistrationDataPtr registration;
    // Wheter all processing is completed and this data is safe to erase now.
    bool processing_completed = false;
  };

  // Map from |unique_id|s to the registration data.
  // An entry in here means the fetch has completed. This information is needed
  // after the fetch has completed to dispatch the backgroundfetchclick event.
  // TODO(crbug.com/41397180): Clean this up when the UI is no longer showing.
  std::map<std::string, std::unique_ptr<RegistrationData>> completed_fetches_;

  // Scheduling params - Finch configurable.
  int max_running_downloads_;
  int max_active_registrations_;
  int num_active_registrations_ = 0;
  int num_running_downloads_ = 0;

  base::WeakPtrFactory<BackgroundFetchScheduler> weak_ptr_factory_{this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_SCHEDULER_H_
