// Copyright 2025 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_EXTENSIONS_EXTERNAL_PROVIDER_MANAGER_H_
#define CHROME_BROWSER_EXTENSIONS_EXTERNAL_PROVIDER_MANAGER_H_

#include <optional>
#include <string>

#include "base/auto_reset.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "components/keyed_service/core/keyed_service.h"
#include "extensions/browser/external_provider_interface.h"
#include "extensions/buildflags/buildflags.h"

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

namespace content {
class BrowserContext;
}

namespace extensions {

class CrxInstallError;
class ExtensionErrorController;
class ExtensionPrefs;
class ExtensionRegistry;
class PendingExtensionManager;

// Class ExternalProviderManager manages the set of external extension
// providers, and installs/uninstalls the extensions they provide.
class ExternalProviderManager
    : public KeyedService,
      public ExternalProviderInterface::VisitorInterface {
 public:
  explicit ExternalProviderManager(content::BrowserContext* context);

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

  ~ExternalProviderManager() override;

  // KeyedService:
  void Shutdown() override;

  // Returns the instance for the given `browser_context`.
  static ExternalProviderManager* Get(content::BrowserContext* browser_context);

  // ExternalProviderInterface::VisitorInterface:
  bool OnExternalExtensionFileFound(
      const ExternalInstallInfoFile& info) override;
  bool OnExternalExtensionUpdateUrlFound(
      const ExternalInstallInfoUpdateUrl& info,
      bool force_update) override;
  void OnExternalProviderReady(
      const ExternalProviderInterface* provider) override;
  void OnExternalProviderUpdateComplete(
      const ExternalProviderInterface* provider,
      const std::vector<ExternalInstallInfoUpdateUrl>& update_url_extensions,
      const std::vector<ExternalInstallInfoFile>& file_extensions,
      const std::set<std::string>& removed_extensions) override;

  // Creates external extension providers.
  void CreateExternalProviders();

  // Checks for updates (or potentially new extensions from external providers)
  void CheckForExternalUpdates();

  // Ask each external extension provider to call
  // OnExternalExtension(File|UpdateUrl)Found() with their known extensions.
  // This will trigger an update/reinstall of the extensions saved in the
  // provider's prefs.
  void ReinstallProviderExtensions();

  // Clears all ExternalProviders.
  void ClearProvidersForTesting();

  // Adds an ExternalProviderInterface for the service to use during testing.
  void AddProviderForTesting(
      std::unique_ptr<ExternalProviderInterface> test_provider);

  // Sets a callback to be called when all external providers are ready and
  // their extensions have been installed.
  void set_external_updates_finished_callback_for_test(
      base::OnceClosure callback) {
    external_updates_finished_callback_ = std::move(callback);
  }

  // While disabled all calls to CheckForExternalUpdates() will bail out.
  static base::AutoReset<bool> DisableExternalUpdatesForTesting();

 private:
  // Returns true if all the external extension providers are ready.
  bool AreAllExternalProvidersReady() const;

  // Called once all external providers are ready. Checks for unclaimed
  // external extensions.
  void OnAllExternalProvidersReady();

  // For the extension in `version_path` with `id`, check to see if it's an
  // externally managed extension.  If so, uninstall it.
  void CheckExternalUninstall(const std::string& id);

  // Callback for installation finish of an extension from external file, since
  // we need to remove this extension from the pending extension manager in case
  // of installation failure. This is only a need for extensions installed
  // by file, since extensions installed by URL will be intentionally kept in
  // the manager and retried later.
  void InstallationFromExternalFileFinished(
      const std::string& extension_id,
      const std::optional<CrxInstallError>& error);

  // Are we expecting a reinstall of the extension due to corruption?
  bool IsReinstallForCorruptionExpected(const ExtensionId& id) const;

  // The BrowserContext with which the manager is associated.
  raw_ptr<content::BrowserContext> context_;

  // A collection of external extension providers.  Each provider reads
  // a source of external extension information.  Examples include the
  // windows registry and external_extensions.json.
  ProviderCollection external_extension_providers_;

  // Preferences for the owning profile.
  raw_ptr<ExtensionPrefs> extension_prefs_ = nullptr;

  // Sets of enabled/disabled/terminated/blocklisted extensions. Not owned.
  raw_ptr<ExtensionRegistry> registry_ = nullptr;

  // Hold the set of pending extensions. Not owned.
  raw_ptr<PendingExtensionManager> pending_extension_manager_ = nullptr;

  // The controller for the UI that alerts the user about any blocklisted
  // extensions.
  raw_ptr<ExtensionErrorController> error_controller_;

  // Set to true by OnExternalExtensionUpdateUrlFound() when an external
  // extension URL is found, and by CheckForUpdatesSoon() when an update check
  // has to wait for the external providers.  Used in
  // OnAllExternalProvidersReady() to determine if an update check is needed to
  // install pending extensions.
  bool update_once_all_providers_are_ready_ = false;

  // A callback to be called when all external providers are ready and their
  // extensions have been installed. This happens on initial load and whenever
  // a new entry is found. Normally this is a null callback, but is used in
  // external provider related tests.
  base::OnceClosure external_updates_finished_callback_;

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

}  // namespace extensions

#endif  // CHROME_BROWSER_EXTENSIONS_EXTERNAL_PROVIDER_MANAGER_H_
