// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/office_web_app/office_web_app.h"

#include <string_view>

#include "base/check_deref.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_macros.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.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/mojom/user_display_mode.mojom.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app_icon_generator.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/office_web_app_resources.h"
#include "components/webapps/browser/install_result_code.h"
#include "components/webapps/browser/installable/installable_metrics.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image.h"
#include "url/gurl.h"

namespace chromeos {

const char kMicrosoft365WebAppUrl[] =
    "https://www.microsoft365.com/?from=Homescreen";
static constexpr std::string_view kMicrosoft365WebAppScope =
    "https://www.microsoft365.com/";
const int kMicrosoft365WebAppIconSize = 192;

namespace {
constexpr char kMicrosoft365FallbackName[] = "Microsoft 365";

void OnOfficeWebAppInstalledOffline(
    Profile* profile,
    base::OnceCallback<void(webapps::InstallResultCode)> callback,
    const GURL& install_url,
    web_app::ExternallyManagedAppManager::InstallResult result) {
  UMA_HISTOGRAM_ENUMERATION(
      "FileBrowser.OfficeFiles.Setup.OfficeWebAppOfflineInstallation",
      result.code);
  if (webapps::IsSuccess(result.code)) {
    auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
    proxy->SetSupportedLinksPreference(*result.app_id);
  } else {
    LOG(ERROR) << "Office web app offline installation failure: "
               << result.code;
  }

  std::move(callback).Run(result.code);
}

void InstallMicrosoft365Offline(
    Profile* profile,
    base::OnceCallback<void(webapps::InstallResultCode)> callback) {
  auto* provider = web_app::WebAppProvider::GetForWebApps(profile);
  DCHECK(provider);

  // Manually install using the manifest at
  // https://www.microsoft365.com/webmanifest.json.
  web_app::ExternalInstallOptions options(
      GURL(kMicrosoft365WebAppUrl),
      web_app::mojom::UserDisplayMode::kStandalone,
      web_app::ExternalInstallSource::kInternalMicrosoft365Setup);
  options.fallback_app_name = kMicrosoft365FallbackName;
  options.add_to_quick_launch_bar = false;
  options.only_use_app_info_factory = true;
  options.app_info_factory =
      base::BindRepeating([]() -> std::unique_ptr<web_app::WebAppInstallInfo> {
        GURL start_url = GURL(kMicrosoft365WebAppUrl);
        webapps::ManifestId manifest_id =
            web_app::GenerateManifestIdFromStartUrlOnly(start_url);
        auto info = std::make_unique<web_app::WebAppInstallInfo>(manifest_id,
                                                                 start_url);
        info->manifest_url =
            GURL("https://www.microsoft365.com/webmanifest.json");
        info->title =
            l10n_util::GetStringUTF16(IDS_OFFICE_FILE_HANDLER_APP_MICROSOFT);
        info->scope = GURL(kMicrosoft365WebAppScope);
        info->display_mode = web_app::DisplayMode::kStandalone;

        gfx::Image image =
            ui::ResourceBundle::GetSharedInstance().GetImageNamed(
                IDR_OFFICE_WEB_APP_ICONS_OFFICE_192_PNG);
        info->icon_bitmaps.any[kMicrosoft365WebAppIconSize] = image.AsBitmap();
        info->background_color = 0xFFD53A00;
        info->theme_color = 0xFFD53A00;
        return info;
      });

  provider->externally_managed_app_manager().InstallNow(
      std::move(options), base::BindOnce(&OnOfficeWebAppInstalledOffline,
                                         profile, std::move(callback)));
}

void OnOfficeWebAppInstalled(
    Profile* profile,
    base::OnceCallback<void(webapps::InstallResultCode)> callback,
    const GURL& install_url,
    web_app::ExternallyManagedAppManager::InstallResult result) {
  UMA_HISTOGRAM_ENUMERATION(
      "FileBrowser.OfficeFiles.Setup.OfficeWebAppInstallation", result.code);
  if (webapps::IsSuccess(result.code)) {
    auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
    proxy->SetSupportedLinksPreference(*result.app_id);
    std::move(callback).Run(result.code);
    return;
  }

  // Fallback to an offline install.
  LOG(ERROR) << "Office web app installation failure: " << result.code
             << " attempting offline installation";
  InstallMicrosoft365Offline(profile, std::move(callback));
}

}  // namespace

void InstallMicrosoft365(
    Profile* profile,
    base::OnceCallback<void(webapps::InstallResultCode)> callback) {
  auto* provider = web_app::WebAppProvider::GetForWebApps(profile);
  DCHECK(provider);

  web_app::ExternalInstallOptions options(
      GURL(kMicrosoft365WebAppUrl),
      web_app::mojom::UserDisplayMode::kStandalone,
      web_app::ExternalInstallSource::kInternalMicrosoft365Setup);
  options.fallback_app_name = kMicrosoft365FallbackName;
  options.add_to_quick_launch_bar = false;
  // Remove the app with the unexpected start URL.
  if (auto* app =
          provider->registrar_unsafe().LookUpAppByInstallSourceInstallUrl(
              web_app::WebAppManagement::Type::kOneDriveIntegration,
              GURL(kMicrosoft365WebAppUrl));
      app && app->start_url() != kMicrosoft365WebAppUrl) {
    LOG(WARNING)
        << "replacing previously installed app with the unexpected URL";
    options.force_reinstall = true;
    options.uninstall_and_replace = {app->app_id()};
  }

  // Attempt an online install first.
  provider->externally_managed_app_manager().InstallNow(
      std::move(options),
      base::BindOnce(&OnOfficeWebAppInstalled, profile, std::move(callback)));
}

}  // namespace chromeos
