// Copyright 2018 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_DOWNLOAD_PUBLIC_COMMON_IN_PROGRESS_DOWNLOAD_MANAGER_H_
#define COMPONENTS_DOWNLOAD_PUBLIC_COMMON_IN_PROGRESS_DOWNLOAD_MANAGER_H_

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

#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "build/build_config.h"
#include "components/download/public/common/download_create_info.h"
#include "components/download/public/common/download_export.h"
#include "components/download/public/common/download_file_factory.h"
#include "components/download/public/common/download_item_impl_delegate.h"
#include "components/download/public/common/download_job.h"
#include "components/download/public/common/download_utils.h"
#include "components/download/public/common/simple_download_manager.h"
#include "components/download/public/common/url_download_handler.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "services/device/public/mojom/wake_lock_provider.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom-forward.h"
#include "url/gurl.h"

namespace network {
class SharedURLLoaderFactory;
}  // namespace network

namespace leveldb_proto {
class ProtoDatabaseProvider;
}  // namespace leveldb_proto

namespace download {

class DownloadDBCache;
class DownloadStartObserver;
class DownloadUrlParameters;
struct DownloadDBEntry;

// Manager for handling all active downloads.
class COMPONENTS_DOWNLOAD_EXPORT InProgressDownloadManager
    : public UrlDownloadHandler::Delegate,
      public DownloadItemImplDelegate,
      public SimpleDownloadManager {
 public:
  using StartDownloadItemCallback =
      base::OnceCallback<void(std::unique_ptr<DownloadCreateInfo> info,
                              DownloadItemImpl*,
                              const base::FilePath&,
                              bool /* should_persist_new_download */)>;
  using DisplayNames = std::unique_ptr<
      std::map<std::string /*content URI*/, base::FilePath /* display name*/>>;

  // Class to be notified when download starts/stops.
  class COMPONENTS_DOWNLOAD_EXPORT Delegate {
   public:
    // Called when in-progress downloads are initialized.
    virtual void OnDownloadsInitialized() {}

    // Intercepts the download to another system if applicable. Returns true if
    // the download was intercepted.
    virtual bool InterceptDownload(
        const DownloadCreateInfo& download_create_info);

    // Gets the default download directory.
    virtual base::FilePath GetDefaultDownloadDirectory();

    // Gets the download item for the given |download_create_info|.
    // TODO(qinmin): remove this method and let InProgressDownloadManager
    // create the DownloadItem from in-progress cache.
    virtual void StartDownloadItem(
        std::unique_ptr<DownloadCreateInfo> info,
        DownloadUrlParameters::OnStartedCallback on_started,
        StartDownloadItemCallback callback) {}
  };

  using IsOriginSecureCallback = base::RepeatingCallback<bool(const GURL&)>;
  using WakeLockProviderBinder = base::RepeatingCallback<void(
      mojo::PendingReceiver<device::mojom::WakeLockProvider>)>;
  // Creates a new InProgressDownloadManager instance. If |in_progress_db_dir|
  // is empty then it will use an empty database and no history will be saved.
  // |db_provider| can be nullptr if |in_progress_db_dir| is empty.
  // |wake_lock_provider_binder| may be null.
  InProgressDownloadManager(Delegate* delegate,
                            const base::FilePath& in_progress_db_dir,
                            leveldb_proto::ProtoDatabaseProvider* db_provider,
                            const IsOriginSecureCallback& is_origin_secure_cb,
                            const URLSecurityPolicy& url_security_policy,
                            WakeLockProviderBinder wake_lock_provider_binder);

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

  ~InProgressDownloadManager() override;

  // SimpleDownloadManager implementation.
  void DownloadUrl(std::unique_ptr<DownloadUrlParameters> params) override;
  bool CanDownload(DownloadUrlParameters* params) override;
  void GetAllDownloads(
      SimpleDownloadManager::DownloadVector* downloads) override;
  DownloadItem* GetDownloadByGuid(const std::string& guid) override;

  // Called to start a download.
  void BeginDownload(std::unique_ptr<DownloadUrlParameters> params,
                     std::unique_ptr<network::PendingSharedURLLoaderFactory>
                         pending_url_loader_factory,
                     bool is_new_download,
                     const std::string& serialized_embedder_download_data,
                     const GURL& tab_url,
                     const GURL& tab_referrer_url);

  // Intercepts a download from navigation.
  void InterceptDownloadFromNavigation(
      std::unique_ptr<network::ResourceRequest> resource_request,
      int render_process_id,
      int render_frame_id,
      const std::string& serialized_embedder_download_data,
      const GURL& tab_url,
      const GURL& tab_referrer_url,
      std::vector<GURL> url_chain,
      net::CertStatus cert_status,
      network::mojom::URLResponseHeadPtr response_head,
      mojo::ScopedDataPipeConsumerHandle response_body,
      network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
      std::unique_ptr<network::PendingSharedURLLoaderFactory>
          pending_url_loader_factory,
      bool is_transient);

  void StartDownload(std::unique_ptr<DownloadCreateInfo> info,
                     std::unique_ptr<InputStream> stream,
                     URLLoaderFactoryProvider::URLLoaderFactoryProviderPtr
                         url_loader_factory_provider,
                     DownloadJob::CancelRequestCallback cancel_request_callback,
                     DownloadUrlParameters::OnStartedCallback on_started);

  // Shutting down the manager and stop all downloads.
  void ShutDown();

  // DownloadItemImplDelegate implementations.
  void DetermineDownloadTarget(DownloadItemImpl* download,
                               DownloadTargetCallback callback) override;
  void ResumeInterruptedDownload(
      std::unique_ptr<DownloadUrlParameters> params,
      const std::string& serialized_embedder_download_data) override;
  bool ShouldOpenDownload(DownloadItemImpl* item,
                          ShouldOpenDownloadCallback callback) override;
  void ReportBytesWasted(DownloadItemImpl* download) override;

  // Called to remove an in-progress download.
  void RemoveInProgressDownload(const std::string& guid);

  void set_download_start_observer(DownloadStartObserver* observer) {
    download_start_observer_ = observer;
  }

#if BUILDFLAG(IS_ANDROID)
  // Callback to generate an intermediate file path from the given target file
  // path;
  using IntermediatePathCallback =
      base::RepeatingCallback<base::FilePath(const base::FilePath&)>;
  void set_intermediate_path_cb(
      const IntermediatePathCallback& intermediate_path_cb) {
    intermediate_path_cb_ = intermediate_path_cb;
  }

  void set_default_download_dir(base::FilePath default_download_dir) {
    default_download_dir_ = default_download_dir;
  }
#endif

  // Called to get all in-progress DownloadItemImpl.
  // TODO(qinmin): remove this method once InProgressDownloadManager owns
  // all in-progress downloads.
  virtual std::vector<std::unique_ptr<download::DownloadItemImpl>>
  TakeInProgressDownloads();

  // Called when all the DownloadItem is loaded.
  // TODO(qinmin): remove this once features::kDownloadDBForNewDownloads is
  // enabled by default.
  void OnAllInprogressDownloadsLoaded();

  // Gets the display name for a download. For non-android platforms, this
  // always returns an empty path.
  base::FilePath GetDownloadDisplayName(const base::FilePath& path);

  void set_file_factory(std::unique_ptr<DownloadFileFactory> file_factory) {
    file_factory_ = std::move(file_factory);
  }
  DownloadFileFactory* file_factory() { return file_factory_.get(); }

  void set_url_loader_factory(
      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
    url_loader_factory_ = url_loader_factory;
  }

  void SetDelegate(Delegate* delegate);

  void set_is_origin_secure_cb(
      const IsOriginSecureCallback& is_origin_secure_cb) {
    is_origin_secure_cb_ = is_origin_secure_cb;
  }

  // Called to insert an in-progress download for testing purpose.
  void AddInProgressDownloadForTest(
      std::unique_ptr<download::DownloadItemImpl> download);

 private:
  void Initialize(const base::FilePath& in_progress_db_dir,
                  leveldb_proto::ProtoDatabaseProvider* db_provider);

  // UrlDownloadHandler::Delegate implementations.
  void OnUrlDownloadStarted(
      std::unique_ptr<DownloadCreateInfo> download_create_info,
      std::unique_ptr<InputStream> input_stream,
      URLLoaderFactoryProvider::URLLoaderFactoryProviderPtr
          url_loader_factory_provider,
      UrlDownloadHandlerID downloader,
      DownloadUrlParameters::OnStartedCallback callback) override;
  void OnUrlDownloadStopped(UrlDownloadHandlerID downloader) override;
  void OnUrlDownloadHandlerCreated(
      UrlDownloadHandler::UniqueUrlDownloadHandlerPtr downloader) override;

  // Called when the in-progress DB is initialized.
  void OnDBInitialized(bool success,
                       std::unique_ptr<std::vector<DownloadDBEntry>> entries);

  // Called when download display names are retrieved,
  void OnDownloadNamesRetrieved(
      std::unique_ptr<std::vector<DownloadDBEntry>> entries,
      DisplayNames display_names);

  // Start a DownloadItemImpl.
  void StartDownloadWithItem(
      std::unique_ptr<InputStream> stream,
      URLLoaderFactoryProvider::URLLoaderFactoryProviderPtr
          url_loader_factory_provider,
      DownloadJob::CancelRequestCallback cancel_request_callback,
      std::unique_ptr<DownloadCreateInfo> info,
      DownloadItemImpl* download,
      const base::FilePath& duplicate_download_file_path,
      bool should_persist_new_download);

  // Called when downloads are initialized.
  void OnDownloadsInitialized();

  // Called to notify |delegate_| that downloads are initialized.
  void NotifyDownloadsInitialized();

  // Cancels the given UrlDownloadHandler.
  void CancelUrlDownload(UrlDownloadHandlerID downloader, bool user_cancel);

  // Active download handlers.
  std::vector<UrlDownloadHandler::UniqueUrlDownloadHandlerPtr>
      url_download_handlers_;

  // Delegate to provide information to create a new download. Can be null.
  raw_ptr<Delegate> delegate_;

  // Factory for the creation of download files.
  std::unique_ptr<DownloadFileFactory> file_factory_;

  // Cache for DownloadDB.
  std::unique_ptr<DownloadDBCache> download_db_cache_;


  // listens to information about in-progress download items.
  std::unique_ptr<DownloadItem::Observer> in_progress_download_observer_;

  // Observer to notify when a download starts.
  raw_ptr<DownloadStartObserver> download_start_observer_;

  // callback to check if an origin is secure.
  IsOriginSecureCallback is_origin_secure_cb_;

#if BUILDFLAG(IS_ANDROID)
  // Callback to generate the intermediate file path.
  IntermediatePathCallback intermediate_path_cb_;

  // Default download directory.
  base::FilePath default_download_dir_;
#endif

  // A list of in-progress download items, could be null if DownloadManagerImpl
  // is managing all downloads.
  std::vector<std::unique_ptr<DownloadItemImpl>> in_progress_downloads_;

  // A list of download GUIDs that should not be persisted.
  std::set<std::string> non_persistent_download_guids_;

  // URLLoaderFactory for issuing network request when DownloadManagerImpl
  // is not available.
  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;

  // Mapping between download URIs and display names.
  // TODO(qinmin): move display name to history and in-progress DB.
  DisplayNames display_names_;

  // Used to check if the URL is safe.
  URLSecurityPolicy url_security_policy_;

  // Callback used to bind WakeLockProvider receivers, if not null.
  const WakeLockProviderBinder wake_lock_provider_binder_;

  base::WeakPtrFactory<InProgressDownloadManager> weak_factory_{this};
};

}  // namespace download

#endif  // COMPONENTS_DOWNLOAD_PUBLIC_COMMON_IN_PROGRESS_DOWNLOAD_MANAGER_H_
