// 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/web_applications/commands/install_from_info_command.h"

#include <memory>
#include <utility>

#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_future.h"
#include "build/build_config.h"
#include "chrome/browser/ui/web_applications/web_app_browsertest_base.h"
#include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
#include "chrome/browser/web_applications/proto/web_app_install_state.pb.h"
#include "chrome/browser/web_applications/test/fake_os_integration_manager.h"
#include "chrome/browser/web_applications/test/web_app_icon_test_utils.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_command_scheduler.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_filter.h"
#include "chrome/browser/web_applications/web_app_icon_generator.h"
#include "chrome/browser/web_applications/web_app_icon_manager.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chrome/browser/web_applications/web_app_install_params.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "components/webapps/browser/install_result_code.h"
#include "content/public/test/browser_test.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"

using base::BucketsAre;

namespace web_app {

class InstallFromInfoCommandTest : public WebAppBrowserTestBase {
 public:
  InstallFromInfoCommandTest() = default;
  OrderedSizeToBitmap ReadIcons(const webapps::AppId& app_id,
                                IconPurpose purpose,
                                const SortedSizesPx& sizes_px) {
    OrderedSizeToBitmap result;
    base::RunLoop run_loop;
    provider().icon_manager().ReadTrustedIconsWithFallbackToManifestIcons(
        app_id, sizes_px, purpose,
        base::BindLambdaForTesting(
            [&](IconMetadataFromDisk icon_metadata_from_disk) {
              result = std::move(icon_metadata_from_disk.icons_map);
              run_loop.Quit();
            }));
    run_loop.Run();
    return result;
  }
};

IN_PROC_BROWSER_TEST_F(InstallFromInfoCommandTest, SuccessInstall) {
  base::HistogramTester tester;
  auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
      GURL("http://test.com/path"));
  info->title = u"Test name";

  const webapps::WebappInstallSource install_source =
#if BUILDFLAG(IS_CHROMEOS)
      webapps::WebappInstallSource::SYSTEM_DEFAULT;
#else
      webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON;
#endif

  base::RunLoop loop;
  webapps::AppId result_app_id;
  provider().scheduler().InstallFromInfoWithParams(
      std::move(info),
      /*overwrite_existing_manifest_fields=*/false, install_source,
      base::BindLambdaForTesting(
          [&](const webapps::AppId& app_id, webapps::InstallResultCode code) {
            EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
            result_app_id = app_id;
            loop.Quit();
          }),
      WebAppInstallParams());
  loop.Run();

  EXPECT_TRUE(provider().registrar_unsafe().AppMatches(
      result_app_id, WebAppFilter::InstalledInOperatingSystemForTesting()));

  // Ensure histogram is only measured once.

  EXPECT_THAT(tester.GetAllSamples("WebApp.Install.Result"),
              BucketsAre(base::Bucket(true, 1)));
  EXPECT_THAT(tester.GetAllSamples("WebApp.Install.Source.Success"),
              BucketsAre(base::Bucket(install_source, 1)));

  const WebApp* web_app =
      provider().registrar_unsafe().GetAppById(result_app_id);
  ASSERT_TRUE(web_app);

  OrderedSizeToBitmap icon_bitmaps =
      ReadIcons(result_app_id, IconPurpose::ANY,
                web_app->downloaded_icon_sizes(IconPurpose::ANY));

  // Make sure that icons have been generated for all sub sizes.
  EXPECT_TRUE(ContainsOneIconOfEachSize(icon_bitmaps));
}

IN_PROC_BROWSER_TEST_F(InstallFromInfoCommandTest, InstallWithParams) {
  auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
      GURL("http://test.com/path"));
  info->title = u"Test name";

  WebAppInstallParams install_params;
  install_params.add_to_applications_menu = true;
  install_params.add_to_desktop = true;

  base::RunLoop loop;
  webapps::AppId result_app_id;
  provider().scheduler().InstallFromInfoWithParams(
      std::move(info),
      /*overwrite_existing_manifest_fields=*/false,
      webapps::WebappInstallSource::MENU_BROWSER_TAB,
      base::BindLambdaForTesting(
          [&](const webapps::AppId& app_id, webapps::InstallResultCode code) {
            EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
            EXPECT_TRUE(provider().registrar_unsafe().AppMatches(
                app_id, WebAppFilter::InstalledInOperatingSystemForTesting()));
            result_app_id = app_id;
            loop.Quit();
          }),
      install_params);
  loop.Run();
  std::optional<proto::os_state::WebAppOsIntegration> os_state =
      provider().registrar_unsafe().GetAppCurrentOsIntegrationState(
          result_app_id);
  ASSERT_TRUE(os_state.has_value());
  EXPECT_TRUE(os_state->has_shortcut());
  EXPECT_EQ(os_state->run_on_os_login().run_on_os_login_mode(),
            proto::os_state::RunOnOsLogin::MODE_NOT_RUN);
}

IN_PROC_BROWSER_TEST_F(InstallFromInfoCommandTest,
                       TrustedInstallPopulatesTrustedIcons) {
  auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(
      GURL("http://test.com/path"));
  info->title = u"Test name";
  SkBitmap bitmap;
  bitmap.allocN32Pixels(32, 32);
  bitmap.eraseColor(SK_ColorRED);
  info->icon_bitmaps.any[32] = bitmap;

  base::RunLoop loop;
  webapps::AppId result_app_id;
  provider().scheduler().InstallFromInfoWithParams(
      std::move(info),
      /*overwrite_existing_manifest_fields=*/false,
      webapps::WebappInstallSource::EXTERNAL_DEFAULT,
      base::BindLambdaForTesting(
          [&](const webapps::AppId& app_id, webapps::InstallResultCode code) {
            EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
            result_app_id = app_id;
            loop.Quit();
          }),
      WebAppInstallParams());
  loop.Run();

  const WebApp* web_app =
      provider().registrar_unsafe().GetAppById(result_app_id);
  ASSERT_TRUE(web_app);
  // The exact number of icons that should exist in the web app system should
  // be expected here.
  EXPECT_EQ(web_app::SizesToGenerate().size(),
            web_app->stored_trusted_icon_sizes(IconPurpose::ANY).size());
}

}  // namespace web_app
