// Copyright 2023 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_COMMANDS_EXTERNAL_APP_RESOLUTION_COMMAND_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_COMMANDS_EXTERNAL_APP_RESOLUTION_COMMAND_H_

#include <memory>
#include <optional>
#include <vector>

#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/web_applications/commands/web_app_command.h"
#include "chrome/browser/web_applications/external_install_options.h"
#include "chrome/browser/web_applications/externally_managed_app_manager.h"
#include "chrome/browser/web_applications/install_bounce_metric.h"
#include "chrome/browser/web_applications/jobs/install_from_info_job.h"
#include "chrome/browser/web_applications/jobs/install_placeholder_job.h"
#include "chrome/browser/web_applications/jobs/manifest_to_web_app_install_info_job.h"
#include "chrome/browser/web_applications/jobs/uninstall/remove_install_source_job.h"
#include "chrome/browser/web_applications/locks/all_apps_lock.h"
#include "chrome/browser/web_applications/locks/shared_web_contents_lock.h"
#include "chrome/browser/web_applications/locks/shared_web_contents_with_app_lock.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chrome/browser/web_applications/web_app_install_utils.h"
#include "chrome/browser/web_applications/web_app_logging.h"
#include "components/services/app_service/public/cpp/app_launch_util.h"
#include "components/webapps/browser/installable/installable_logging.h"
#include "components/webapps/browser/installable/installable_metrics.h"
#include "components/webapps/browser/uninstall_result_code.h"

class BrowserWindowInterface;
class Profile;

namespace content {
class WebContents;
}

namespace webapps {
class WebAppUrlLoader;
enum class WebAppUrlLoaderResult;
}  // namespace webapps

namespace web_app {

class CustomIconFetcher;
class FinalizeInstallOrUpdateJob;
class InstallPlaceholderJob;
class WebAppDataRetriever;
class WebAppUninstallAndReplaceJob;

struct WebAppInstallInfo;

// Command to install a web app from an external source, such as a policy or
// default app. This command handles loading the install URL, fetching the
// manifest, and creating the web app. It can also install a placeholder if the
// full installation fails, and can replace existing placeholders.
//
// Invariant: This command assumes that a new placeholder app for the given
// `install_url` cannot be installed between the scheduling and running of this
// command.
class ExternalAppResolutionCommand
    : public WebAppCommand<SharedWebContentsLock,
                           ExternallyManagedAppManagerInstallResult> {
 public:
  using InstallResult = ExternallyManagedAppManagerInstallResult;
  using InstalledCallback = base::OnceCallback<void(InstallResult)>;

  ExternalAppResolutionCommand(Profile& profile,
                               const ExternalInstallOptions& install_options,
                               std::optional<webapps::AppId> placeholder_app_id,
                               InstalledCallback installed_callback);
  ~ExternalAppResolutionCommand() override;

  void SetDataRetrieverForTesting(
      std::unique_ptr<WebAppDataRetriever> data_retriever);
  void SetOnLockUpgradedCallbackForTesting(base::OnceClosure callback);

  // WebAppCommand:
  void OnShutdown(base::PassKey<WebAppCommandManager>) const override;

 protected:
  // WebAppCommand:
  void StartWithLock(std::unique_ptr<SharedWebContentsLock> lock) override;

 private:
  WebAppProvider& provider() const;

  void Abort(webapps::InstallResultCode code);

  // This function loads the URL and based on the result branches off to try
  // either
  // * regular web app installation (if loading was successful).
  // * placeholder installation (in case loading was successful but the user
  // agent got redirected to a different origin and a placeholder installation
  // is configured as fallback).
  // * offline installation from install info (in all other cases).
  void OnUrlLoadedAndBranchInstallation(webapps::WebAppUrlLoaderResult result);

  // Regular installation path:
  void OnGetWebAppInstallInfoInCommand(
      std::unique_ptr<WebAppInstallInfo> web_app_info);
  void OnDidPerformInstallableCheck(blink::mojom::ManifestPtr opt_manifest,
                                    bool valid_manifest_for_web_app,
                                    webapps::InstallableStatusCode error_code);

  // Installation flow followed by path where the `WebAppInstallInfo` is
  // generated from the `opt_manifest`.
  void RetrieveWebAppInfoFromManifest(blink::mojom::ManifestPtr opt_manifest);
  void OnWebAppInstallInfoParsedFromManifest(
      std::unique_ptr<WebAppInstallInfo> install_info);

  // Installation flow followed by path where the `WebAppInstallInfo` is
  // generated from the web page metadata,
  void OnPreparedForIconRetrievingForFallbackInfo(
      IconUrlSizeSet icon_urls,
      webapps::WebAppUrlLoaderResult result);
  void OnIconsRetrievedUpgradeLockDescription(
      IconsDownloadedResult result,
      IconsMap icons_map,
      DownloadedIconsHttpResults icons_http_results);

  void UpdateInfoWithParamsAndUpgradeLock(bool icon_download_failed);
  void OnCustomIconDecodedPopulateBitmaps(std::optional<SkBitmap> bitmap);
  void ContinueUpdateInfoWithParamsAndUpgradeLock(bool icon_download_failed);

  void OnLockUpgradedFinalizeInstall(bool icon_download_failed);
  void OnInstallFinalized(const webapps::AppId& app_id,
                          webapps::InstallResultCode code);
  void OnUninstallAndReplaceCompletedUninstallPlaceholder(
      bool uninstall_triggered);
  void OnAllAppsLockGrantedRemovePlaceholder();
  void OnPlaceholderUninstalledMaybeRelaunch(
      webapps::UninstallResultCode result);

  void OnLaunch(base::WeakPtr<BrowserWindowInterface> browser,
                base::WeakPtr<content::WebContents> web_contents,
                apps::LaunchContainer container,
                base::Value debug_value);

  // Placeholder installation path:
  void OnPlaceHolderAppLockAcquired();
  void OnPlaceHolderInstalled(webapps::InstallResultCode code,
                              webapps::AppId app_id);

  // Offline installation path:
  void InstallFromInfo();
  void OnInstallFromInfoAppLockAcquired();
  void OnInstallFromInfoCompleted(webapps::AppId app_id,
                                  webapps::InstallResultCode code);
  void OnUninstallAndReplaceCompleted(bool is_offline_install,
                                      bool uninstall_triggered);

  void TryAppInfoFactoryOnFailure(
      ExternallyManagedAppManager::InstallResult result);

  ExternallyManagedAppManager::InstallResult PrepareResult(
      bool is_offline_install,
      ExternallyManagedAppManager::InstallResult result);

  // SharedWebContentsLock is held while loading the app contents (and manifest,
  // if possible).
  std::unique_ptr<SharedWebContentsLock> web_contents_lock_;

  // SharedWebContentsWithAppLock is held when the affected app ids are known
  // (i.e. after deciding whether a placeholder install is needed or when the
  // manifest is loaded).
  std::unique_ptr<SharedWebContentsWithAppLock> apps_lock_;

  std::unique_ptr<AllAppsLock> all_apps_lock_;
  std::unique_ptr<AllAppsLockDescription> all_apps_lock_description_;

  // Populated after an install completes.
  webapps::AppId app_id_;
  webapps::InstallResultCode install_code_;
  bool uninstalled_for_replace_ = false;
  bool relaunch_app_after_placeholder_uninstall_ = false;

  // `this` must be owned by `profile_`.
  raw_ref<Profile> profile_;

  raw_ptr<content::WebContents> web_contents_ = nullptr;
  std::unique_ptr<webapps::WebAppUrlLoader> url_loader_;
  std::unique_ptr<WebAppDataRetriever> data_retriever_;
  std::unique_ptr<WebAppInstallInfo> web_app_info_;
  std::unique_ptr<ManifestToWebAppInstallInfoJob> manifest_to_install_info_job_;

  ExternalInstallOptions install_options_;

  std::optional<webapps::AppId> installed_placeholder_app_id_;

  webapps::WebappInstallSource install_surface_;
  std::optional<WebAppInstallParams> install_params_;

  std::optional<WebAppUninstallAndReplaceJob> uninstall_and_replace_job_;
  std::optional<InstallPlaceholderJob> install_placeholder_job_;
  std::optional<InstallFromInfoJob> install_from_info_job_;
  std::optional<RemoveInstallSourceJob> remove_placeholder_job_;
  std::optional<FinalizeInstallOrUpdateJob> install_job_;
  std::unique_ptr<CustomIconFetcher> custom_icon_fetcher_;

  base::OnceClosure on_lock_upgraded_callback_for_testing_;

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

}  // namespace web_app

#endif  // CHROME_BROWSER_WEB_APPLICATIONS_COMMANDS_EXTERNAL_APP_RESOLUTION_COMMAND_H_
