// Copyright 2021 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_APP_SERVICE_WEB_APP_PUBLISHER_HELPER_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_APP_SERVICE_WEB_APP_PUBLISHER_HELPER_H_

#include <stdint.h>

#include <map>
#include <optional>
#include <string>
#include <vector>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/types/id_type.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "chrome/browser/web_applications/web_app_command_scheduler.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chrome/browser/web_applications/web_app_install_manager.h"
#include "chrome/browser/web_applications/web_app_install_manager_observer.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/browser/web_applications/web_app_registrar_observer.h"
#include "components/content_settings/core/browser/content_settings_observer.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/services/app_service/public/cpp/app.h"
#include "components/services/app_service/public/cpp/app_launch_util.h"
#include "components/services/app_service/public/cpp/icon_types.h"
#include "components/services/app_service/public/cpp/intent.h"
#include "components/services/app_service/public/cpp/intent_filter.h"
#include "components/services/app_service/public/cpp/launch_result.h"
#include "components/services/app_service/public/cpp/permission.h"
#include "components/webapps/common/web_app_id.h"
#include "ui/gfx/native_ui_types.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/apps/app_service/app_notifications.h"
#include "chrome/browser/apps/app_service/media_requests.h"
#include "chrome/browser/apps/app_service/paused_apps.h"
#include "chrome/browser/badging/badge_manager_delegate.h"
#include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
#include "chrome/browser/notifications/notification_common.h"
#include "chrome/browser/notifications/notification_display_service.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
#include "components/services/app_service/public/cpp/preferred_apps_list_handle.h"
#endif

class BrowserWindowInterface;
class ContentSettingsPattern;
class ContentSettingsTypeSet;
class Profile;
class GURL;

namespace apps {
struct AppLaunchParams;
enum class RunOnOsLoginMode;
enum IconEffects : uint32_t;
}  // namespace apps

namespace badging {
class BadgeManager;
}

namespace base {
class FilePath;
class Time;
}  // namespace base

namespace blink::mojom {
enum class DisplayMode : int32_t;
}

namespace content {
class WebContents;
}

#if BUILDFLAG(IS_CHROMEOS)
namespace message_center {
class Notification;
}
#endif

namespace ui {
enum ResourceScaleFactor : int;
}

namespace webapps {
enum class WebappUninstallSource;
}

namespace web_app {

class ComputedAppSizeWithOrigin;
class WebApp;
class WebAppProvider;
enum class RunOnOsLoginMode;

namespace mojom {
enum class UserDisplayMode : int32_t;
}

struct ShortcutIdTypeMarker {};

typedef base::IdTypeU32<ShortcutIdTypeMarker> ShortcutId;

void UninstallImpl(WebAppProvider* provider,
                   const std::string& app_id,
                   apps::UninstallSource uninstall_source,
                   gfx::NativeWindow parent_window);

class WebAppPublisherHelper : public WebAppRegistrarObserver,
                              public WebAppInstallManagerObserver,
#if BUILDFLAG(IS_CHROMEOS)
                              public NotificationDisplayService::Observer,
                              public MediaStreamCaptureIndicator::Observer,
                              public apps::PreferredAppsListHandle::Observer,
                              public apps::AppRegistryCache::Observer,
#endif
                              public content_settings::Observer {
 public:
  class Delegate {
   public:
    Delegate();
    Delegate(const Delegate&) = delete;
    Delegate& operator=(const Delegate&) = delete;
    ~Delegate();

    virtual void PublishWebApps(std::vector<apps::AppPtr> apps) = 0;
    virtual void PublishWebApp(apps::AppPtr app) = 0;

    virtual void ModifyWebAppCapabilityAccess(
        const std::string& app_id,
        std::optional<bool> accessing_camera,
        std::optional<bool> accessing_microphone) = 0;
  };

  using LoadIconCallback = base::OnceCallback<void(apps::IconValuePtr)>;

  WebAppPublisherHelper(Profile* profile,
                        WebAppProvider* provider,
                        Delegate* delegate);
  WebAppPublisherHelper(const WebAppPublisherHelper&) = delete;
  WebAppPublisherHelper& operator=(const WebAppPublisherHelper&) = delete;
  ~WebAppPublisherHelper() override;

  // Indicates if |permission_type| is supported by Web Applications.
  static bool IsSupportedWebAppPermissionType(
      ContentSettingsType permission_type);

  // Must be called before profile keyed services are destroyed.
  void Shutdown();

  // Populates the various show_in_* fields of |app|.
  void SetWebAppShowInFields(const WebApp* web_app, apps::App& app);

  // Creates permissions for `web_app`.
  apps::Permissions CreatePermissions(const WebApp* web_app);

  // Creates an |apps::AppPtr| describing |web_app|.
  // Note: migration in progress. Changes should be made to both |CreateWebApp|
  // and |ConvertWebApp| until complete.
  apps::AppPtr CreateWebApp(const WebApp* web_app);

  // Constructs an App with only the information required to identify an
  // uninstallation.
  apps::AppPtr ConvertUninstalledWebApp(
      const webapps::AppId& app_id,
      webapps::WebappUninstallSource uninstall_source);

  // Constructs an App with only the information required to update
  // last launch time.
  apps::AppPtr ConvertLaunchedWebApp(const WebApp* web_app);

  // Directly uninstalls |web_app| without prompting the user.
  // If |clear_site_data| is true, any site data associated with the app will
  // be removed.
  // If |report_abuse| is true, the app will be reported for abuse to the Web
  // Store.
  void UninstallWebApp(const WebApp* web_app,
                       apps::UninstallSource uninstall_source,
                       bool clear_site_data,
                       bool report_abuse);

  void SetIconEffect(const std::string& app_id);

#if BUILDFLAG(IS_CHROMEOS)
  void PauseApp(const std::string& app_id);

  void UnpauseApp(const std::string& app_id);

  bool IsPaused(const std::string& app_id);

  void StopApp(const std::string& app_id);

  void GetCompressedIconData(const std::string& app_id,
                             int32_t size_in_dip,
                             ui::ResourceScaleFactor scale_factor,
                             apps::LoadIconCallback callback);
#endif  // BUILDFLAG(IS_CHROMEOS)

  void LoadIcon(const std::string& app_id,
                apps::IconType icon_type,
                int32_t size_hint_in_dip,
                apps::IconEffects icon_effects,
                apps::LoadIconCallback callback);

  void Launch(const std::string& app_id,
              int32_t event_flags,
              apps::LaunchSource launch_source,
              apps::WindowInfoPtr window_info,
              base::OnceCallback<void(content::WebContents*)> on_complete);

  void LaunchAppWithFiles(const std::string& app_id,
                          int32_t event_flags,
                          apps::LaunchSource launch_source,
                          std::vector<base::FilePath> file_paths);

  void LaunchAppWithIntent(const std::string& app_id,
                           int32_t event_flags,
                           apps::IntentPtr intent,
                           apps::LaunchSource launch_source,
                           apps::WindowInfoPtr window_info,
                           apps::LaunchCallback callback);

  void LaunchAppWithParams(
      apps::AppLaunchParams params,
      base::OnceCallback<void(content::WebContents*)> on_complete);

  void SetPermission(const std::string& app_id, apps::PermissionPtr permission);

  void OpenNativeSettings(const std::string& app_id);

  apps::WindowMode GetWindowMode(const std::string& app_id);

  void UpdateAppSize(const std::string& app_id);

  void SetWindowMode(const std::string& app_id, apps::WindowMode window_mode);

  // Converts |display_mode| to a |window_mode|.
  apps::WindowMode ConvertDisplayModeToWindowMode(
      blink::mojom::DisplayMode display_mode);

  void PublishWindowModeUpdate(const std::string& app_id,
                               blink::mojom::DisplayMode display_mode);

  void PublishRunOnOsLoginModeUpdate(const std::string& app_id,
                                     RunOnOsLoginMode run_on_os_login_mode);

  std::string GenerateShortcutId();

  void StoreShortcutId(const std::string& shortcut_id,
                       const WebAppShortcutsMenuItemInfo& menu_item_info);

  // Execute the user command from the context menu items. Currently
  // on the web app shortcut need to be execute in the publisher.
  // The |app_id| represent the app that user selected, the |shortcut_id|
  // represents which shortcut item that user selected. The |display_id|
  // represent where to display the app.
  void ExecuteContextMenuCommand(
      const std::string& app_id,
      const std::string& shortcut_id,
      int64_t display_id,
      base::OnceCallback<void(content::WebContents*)> on_complete);

  // Checks that the user permits the app launch (possibly presenting a blocking
  // user choice dialog). Launches the app with read access to the files in
  // `params.launch_files` and returns all the created WebContentses via
  // `callback`, or doesn't launch the app and returns an empty vector in
  // `callback`.
  void LaunchAppWithFilesCheckingUserPermission(
      const std::string& app_id,
      apps::AppLaunchParams params,
      base::OnceCallback<void(std::vector<content::WebContents*>)> callback);

  Profile* profile() const { return profile_; }

  WebAppRegistrar& registrar() const;
  WebAppInstallManager& install_manager() const;

  bool IsShuttingDown() const;

  static apps::IntentFilters CreateIntentFiltersForWebApp(
      const WebAppProvider& provider,
      const web_app::WebApp& app);

 private:
#if BUILDFLAG(IS_CHROMEOS)
  class BadgeManagerDelegate : public badging::BadgeManagerDelegate {
   public:
    explicit BadgeManagerDelegate(
        const base::WeakPtr<WebAppPublisherHelper>& publisher_helper);

    ~BadgeManagerDelegate() override;

    void OnAppBadgeUpdated(const webapps::AppId& app_id) override;

   private:
    base::WeakPtr<WebAppPublisherHelper> publisher_helper_;
  };
#endif  // BUILDFLAG(IS_CHROMEOS)

  // WebAppInstallManagerObserver:
  void OnWebAppInstalled(const webapps::AppId& app_id) override;
  void OnWebAppInstalledWithOsHooks(const webapps::AppId& app_id) override;
  void OnWebAppManifestUpdated(const webapps::AppId& app_id) override;
  void OnWebAppUninstalled(
      const webapps::AppId& app_id,
      webapps::WebappUninstallSource uninstall_source) override;
  // Dynamically update the AppService on an app being migrated from being the
  // `source_app` to being the `destination_app`.
  void OnWebAppMigrated(const webapps::AppId& source_app_id,
                        const webapps::AppId& target_app_id) override;
  void OnWebAppInstallManagerDestroyed() override;

  // WebAppRegistrarObserver:
  void OnAppRegistrarDestroyed() override;
  void OnWebAppProtocolSettingsChanged(const webapps::AppId& app_id) override;
  void OnWebAppFileHandlerApprovalStateChanged(
      const webapps::AppId& app_id) override;
  void OnWebAppLastLaunchTimeChanged(
      const std::string& app_id,
      const std::optional<base::Time>& last_launch_time) override;
  void OnWebAppUserDisplayModeChanged(
      const webapps::AppId& app_id,
      mojom::UserDisplayMode user_display_mode) override;
  void OnWebAppEffectiveScopeChanged(const webapps::AppId& app_id,
                                     const WebAppScope& new_scope) override;
  void OnWebAppRunOnOsLoginModeChanged(
      const webapps::AppId& app_id,
      RunOnOsLoginMode run_on_os_login_mode) override;
  void OnWebAppSettingsPolicyChanged() override;

#if BUILDFLAG(IS_CHROMEOS)
  void OnWebAppDisabledStateChanged(const webapps::AppId& app_id,
                                    bool is_disabled) override;
  void OnWebAppsDisabledModeChanged() override;

  // NotificationDisplayService::Observer overrides.
  void OnNotificationDisplayed(
      const message_center::Notification& notification,
      const NotificationCommon::Metadata* metadata) override;
  void OnNotificationClosed(const std::string& notification_id) override;
  void OnNotificationDisplayServiceDestroyed(
      NotificationDisplayService* service) override;
#endif  // BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_CHROMEOS)
  // MediaStreamCaptureIndicator::Observer:
  void OnIsCapturingVideoChanged(content::WebContents* web_contents,
                                 bool is_capturing_video) override;
  void OnIsCapturingAudioChanged(content::WebContents* web_contents,
                                 bool is_capturing_audio) override;

  // apps::PreferredAppsListHandle::Observer:
  void OnPreferredAppsListInitialized() override;
  void OnPreferredAppChanged(const std::string& app_id,
                             bool is_preferred_app) override;
  void OnPreferredAppsListWillBeDestroyed(
      apps::PreferredAppsListHandle* handle) override;

  // apps::AppRegistryCache::Observer:
  void OnAppTypeInitialized(apps::AppType app_type) override;
  void OnAppRegistryCacheWillBeDestroyed(
      apps::AppRegistryCache* cache) override;
#endif  // BUILDFLAG(IS_CHROMEOS)

  // content_settings::Observer:
  void OnContentSettingChanged(
      const ContentSettingsPattern& primary_pattern,
      const ContentSettingsPattern& secondary_pattern,
      ContentSettingsTypeSet content_type_set) override;

  void Init();

  void ObserveWebAppSubsystems();

  apps::IconEffects GetIconEffects(const WebApp* web_app);

  const WebApp* GetWebApp(const webapps::AppId& app_id) const;

  // Returns all the WebContents instances launched via `callback`. This value
  // may be empty if the launch fails. There may be more than one `WebContents`
  // if each file is launched in a different window.
  void LaunchAppWithIntentImpl(
      const std::string& app_id,
      int32_t event_flags,
      apps::IntentPtr intent,
      apps::LaunchSource launch_source,
      int64_t display_id,
      base::OnceCallback<void(std::vector<content::WebContents*>)> callback);

  // Checks that the user permits the app launch (possibly presenting a blocking
  // user choice dialog).
  void LaunchAppFromProtocolCheckingUserPermission(
      apps::AppLaunchParams params,
      base::OnceCallback<void(content::WebContents*)> callback);

  apps::PackageId GetPackageId(const WebApp& web_app) const;

#if BUILDFLAG(IS_CHROMEOS)
  // Updates app visibility.
  void UpdateAppDisabledMode(apps::App& app);

  bool MaybeAddNotification(const std::string& app_id,
                            const std::string& notification_id);
  void MaybeAddWebPageNotifications(
      const message_center::Notification& notification,
      const NotificationCommon::Metadata* metadata);

  // Returns whether the app should show a badge.
  bool ShouldShowBadge(const std::string& app_id,
                       bool has_notification_indicator);

  // Sets a web app as supported for capturing links if there are no other
  // non-web apps that captures the similar set of links. Call this after
  // `CreateWebApp()` has been called and the web app has been published to the
  // AppService so that the intent_filters are set on the corresponding
  // `AppPtr`.
  void MaybeSetSupportedLinksPreference(const WebApp* web_app,
                                        bool app_had_supported_links);
#endif  // BUILDFLAG(IS_CHROMEOS)

  // Called after the user has allowed or denied an app launch with files.
  void OnFileHandlerDialogCompleted(
      std::string app_id,
      apps::AppLaunchParams params,
      base::OnceCallback<void(std::vector<content::WebContents*>)> callback,
      bool allowed,
      bool remember_user_choice);

  // Called after the user has allowed or denied an app launch from protocol
  // url.
  void OnProtocolHandlerDialogCompleted(
      apps::AppLaunchParams params,
      base::OnceCallback<void(content::WebContents*)> on_complete,
      bool allowed,
      bool remember_user_choice);

  void OnLaunchCompleted(
      apps::AppLaunchParams params_for_restore,
      bool is_system_web_app,
      std::optional<GURL> override_url,
      base::OnceCallback<void(content::WebContents*)> on_complete,
      base::WeakPtr<BrowserWindowInterface> browser,
      base::WeakPtr<content::WebContents> web_contents,
      apps::LaunchContainer container);

  void OnGetWebAppSize(webapps::AppId app_id,
                       std::optional<ComputedAppSizeWithOrigin> size);

#if BUILDFLAG(IS_CHROMEOS)
  // Triggers one-time migration or rollback of web app link capturing
  // preferences with respect to other types of apps on ChromeOS startup when
  // the navigation capturing feature flag state changes.
  void MaybeMigrateLinkCapturingPreferences();
#endif

  const raw_ptr<Profile, DanglingUntriaged> profile_;

  const raw_ptr<WebAppProvider, DanglingUntriaged> provider_;

  const raw_ptr<Delegate, DanglingUntriaged> delegate_;

  base::ScopedObservation<WebAppRegistrar, WebAppRegistrarObserver>
      registrar_observation_{this};

  base::ScopedObservation<WebAppInstallManager, WebAppInstallManagerObserver>
      install_manager_observation_{this};

  base::ScopedObservation<HostContentSettingsMap, content_settings::Observer>
      content_settings_observation_{this};

  bool is_shutting_down_ = false;

#if BUILDFLAG(IS_CHROMEOS)
  apps::PausedApps paused_apps_;

  base::ScopedObservation<NotificationDisplayService,
                          NotificationDisplayService::Observer>
      notification_display_service_{this};

  apps::AppNotifications app_notifications_;

  raw_ptr<badging::BadgeManager, DanglingUntriaged> badge_manager_ = nullptr;

  base::ScopedObservation<MediaStreamCaptureIndicator,
                          MediaStreamCaptureIndicator::Observer>
      media_indicator_observation_{this};

  apps::MediaRequests media_requests_;

  base::ScopedObservation<apps::PreferredAppsListHandle,
                          apps::PreferredAppsListHandle::Observer>
      preferred_apps_list_observation_{this};

  base::ScopedObservation<apps::AppRegistryCache,
                          apps::AppRegistryCache::Observer>
      app_registry_cache_observation_{this};
#endif  // BUILDFLAG(IS_CHROMEOS)

  std::map<std::string, WebAppShortcutsMenuItemInfo> shortcut_id_map_;
  ShortcutId::Generator shortcut_id_generator_;

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

}  // namespace web_app

#endif  // CHROME_BROWSER_WEB_APPLICATIONS_APP_SERVICE_WEB_APP_PUBLISHER_HELPER_H_
