// Copyright 2020 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_WEB_APPLICATIONS_OS_INTEGRATION_OS_INTEGRATION_MANAGER_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_OS_INTEGRATION_OS_INTEGRATION_MANAGER_H_

#include <bitset>
#include <memory>
#include <optional>
#include <string_view>
#include <vector>

#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/types/pass_key.h"
#include "chrome/browser/web_applications/os_integration/os_integration_sub_manager.h"
#include "chrome/browser/web_applications/os_integration/web_app_file_handler_manager.h"
#include "chrome/browser/web_applications/os_integration/web_app_protocol_handler_manager.h"
#include "chrome/browser/web_applications/os_integration/web_app_run_on_os_login.h"
#include "chrome/browser/web_applications/os_integration/web_app_shortcut.h"
#include "chrome/browser/web_applications/proto/web_app_os_integration_state.pb.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "components/custom_handlers/protocol_handler.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/services/app_service/public/cpp/file_handler.h"
#include "components/webapps/common/web_app_id.h"

class Profile;
class ScopedProfileKeepAlive;

namespace web_app {

class FakeOsIntegrationManager;
class WebAppProvider;
class WebAppProfileDeletionManager;

using ShortcutLocationCallback =
    base::OnceCallback<void(ShortcutLocations shortcut_locations)>;

// Returns the ShortcutInfo for an app.
using GetShortcutInfoCallback =
    base::OnceCallback<void(std::unique_ptr<ShortcutInfo>)>;

// OsIntegrationManager is responsible of creating/updating/deleting
// all OS hooks during Web App lifecycle.
// It contains individual OS integration managers and takes
// care of inter-dependencies among them.
class OsIntegrationManager {
 public:
  using UpdateShortcutsForAllAppsCallback =
      base::RepeatingCallback<void(Profile*, base::OnceClosure)>;

  // Used to suppress OS hooks during this object's lifetime.
  class ScopedSuppressForTesting {
   public:
    ScopedSuppressForTesting();
    ~ScopedSuppressForTesting();
  };
  static bool AreOsHooksSuppressedForTesting();

  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);

  // Sets a callback to be called when this class determines that all shortcuts
  // for a particular profile need to be rebuild, for example because the app
  // shortcut version has changed since the last time these were created.
  // This is used by the legacy extensions based app code in
  // chrome/browser/web_applications/extensions to ensure those app shortcuts
  // also get updated. Calling out to that code directly would violate
  // dependency layering.
  static void SetUpdateShortcutsForAllAppsCallback(
      UpdateShortcutsForAllAppsCallback callback);

  static base::OnceClosure& OnSetCurrentAppShortcutsVersionCallbackForTesting();

  OsIntegrationManager(
      Profile* profile,
      std::unique_ptr<WebAppFileHandlerManager> file_handler_manager,
      std::unique_ptr<WebAppProtocolHandlerManager> protocol_handler_manager);
  virtual ~OsIntegrationManager();

  // Sets internal WebAppProvider reference and threads it through to all sub
  // managers.
  virtual void SetProvider(base::PassKey<WebAppProvider>,
                           WebAppProvider& provider);

  virtual void Start();

  // Start OS Integration synchronization from external callsites. This should
  // be the only point of call into OsIntegrationManager from external places
  // after the OS integration sub managers have been implemented.
  virtual void Synchronize(
      const webapps::AppId& app_id,
      base::OnceClosure callback,
      std::optional<SynchronizeOsOptions> options = std::nullopt);

  // Asynchronously gathers existing shortcut locations according to
  // `shortcut_info`, the results of which will be passed into `callback`.
  // Virtual for testing.
  virtual void GetAppExistingShortCutLocation(
      ShortcutLocationCallback callback,
      std::unique_ptr<ShortcutInfo> shortcut_info);

  // Asynchronously gets the information required to create a shortcut for
  // `app_id` from the WebAppRegistrar along with the icon bitmaps. Do note that
  // this information is obtained from fields other than the web app's
  // `current_os_integration_state_` field, so there is still a slight chance
  // that the information returned from the registrar might not match the web
  // app's current OS integration state (for example if this API is triggered in
  // between the registrar being updated and OS integration being completed).
  //
  // If ShortcutInfo creation requires using the current OS integration state of
  // the web_app, prefer calling `web_app::BuildShortcutInfoWithoutFavicon()`
  // instead.
  virtual void GetShortcutInfoForAppFromRegistrar(
      const webapps::AppId& app_id,
      GetShortcutInfoCallback callback);

  const apps::FileHandlers* GetEnabledFileHandlers(
      const webapps::AppId& app_id) const;

  // Proxy calls for WebAppProtocolHandlerManager.
  virtual std::optional<GURL> TranslateProtocolUrl(const webapps::AppId& app_id,
                                                   const GURL& protocol_url);
  std::vector<custom_handlers::ProtocolHandler> GetAppProtocolHandlers(
      const webapps::AppId& app_id) const;
  virtual std::vector<custom_handlers::ProtocolHandler>
  GetAllowedHandlersForProtocol(const std::string& protocol);
  virtual std::vector<custom_handlers::ProtocolHandler>
  GetDisallowedHandlersForProtocol(const std::string& protocol);

  WebAppFileHandlerManager& file_handler_manager() {
    return *file_handler_manager_;
  }

  WebAppProtocolHandlerManager& protocol_handler_manager_for_testing();

  virtual FakeOsIntegrationManager* AsTestOsIntegrationManager();

  void SetForceUnregisterCalledForTesting(
      base::RepeatingCallback<void(const webapps::AppId&)> on_force_unregister);

  // If a profile is marked for deletion, remove all OS integration for an app
  // installed for that profile.
  void UnregisterOsIntegrationOnProfileMarkedForDeletion(
      base::PassKey<WebAppProfileDeletionManager>,
      const webapps::AppId& app_id);

 protected:
  WebAppProtocolHandlerManager* protocol_handler_manager() {
    return protocol_handler_manager_.get();
  }
  bool has_file_handler_manager() { return !!file_handler_manager_; }
  void set_file_handler_manager(
      std::unique_ptr<WebAppFileHandlerManager> file_handler_manager) {
    file_handler_manager_ = std::move(file_handler_manager);
  }
  void set_protocol_handler_manager(
      std::unique_ptr<WebAppProtocolHandlerManager> protocol_handler_manager) {
    protocol_handler_manager_ = std::move(protocol_handler_manager);
  }

 private:
  // Synchronize:
  void StartSubManagerExecutionIfRequired(
      const webapps::AppId& app_id,
      std::optional<SynchronizeOsOptions> options,
      std::unique_ptr<proto::os_state::WebAppOsIntegration> desired_states,
      base::OnceClosure on_all_execution_done);

  // Use to call Execute() on each sub manager recursively through callbacks
  // so as to ensure that execution happens serially in the order the sub
  // managers are stored inside the sub_managers_ vector, and that consecutive
  // sub managers execute only if the one before it has finished executing.
  void ExecuteNextSubmanager(
      const webapps::AppId& app_id,
      std::optional<SynchronizeOsOptions> options,
      proto::os_state::WebAppOsIntegration* desired_state,
      const proto::os_state::WebAppOsIntegration current_state,
      size_t index,
      base::OnceClosure on_all_execution_done_db_write);

  void WriteStateToDB(
      const webapps::AppId& app_id,
      std::unique_ptr<proto::os_state::WebAppOsIntegration> desired_states,
      base::OnceClosure callback);

  // Called when ForceUnregisterOsIntegrationSubManager has finished
  // unregistering sub managers. `keep_alive` is reset to allow the
  // profile to be deleted.
  void SubManagersUnregistered(
      const webapps::AppId& app_id,
      std::unique_ptr<ScopedProfileKeepAlive> keep_alive);

  // Used to call ForceUnregister() on all sub managers to remove
  // any OS integrations from the OS. This runs synchronously in the order that
  // the sub managers are stored inside the sub_managers_ vector.
  void ForceUnregisterOsIntegrationOnSubManager(
      const webapps::AppId& app_id,
      size_t index,
      base::OnceClosure final_callback);

  // Schedules a call to UpdateShortcutsForAllAppsNow() if kAppShortcutsVersion
  // in prefs is less than kCurrentAppShortcutsVersion.
  void UpdateShortcutsForAllAppsIfNeeded();
  void UpdateShortcutsForAllAppsNow();
  void SetCurrentAppShortcutsVersion();

  void OnIconsRead(const webapps::AppId& app_id,
                   GetShortcutInfoCallback callback,
                   OrderedSizeToBitmap icon_bitmaps);

  std::unique_ptr<ShortcutInfo> BuildShortcutInfoForWebApp(const WebApp* app);

  const raw_ptr<Profile> profile_;
  raw_ptr<WebAppProvider> provider_ = nullptr;

  std::unique_ptr<WebAppFileHandlerManager> file_handler_manager_;
  std::unique_ptr<WebAppProtocolHandlerManager> protocol_handler_manager_;

  std::vector<std::unique_ptr<OsIntegrationSubManager>> sub_managers_;
  bool set_provider_called_ = false;
  bool first_synchronize_called_ = false;

  base::RepeatingCallback<void(const webapps::AppId&)>
      force_unregister_callback_for_testing_ = base::DoNothing();

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

}  // namespace web_app

#endif  // CHROME_BROWSER_WEB_APPLICATIONS_OS_INTEGRATION_OS_INTEGRATION_MANAGER_H_
