// 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.

#include <memory>
#include <optional>

#include "base/json/json_reader.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind.h"
#include "chrome/browser/ash/growth/install_web_app_action_performer.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/web_applications/test/fake_os_integration_manager.h"
#include "chrome/browser/web_applications/test/fake_web_app_provider.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/user.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {
constexpr char kTestProfileName[] = "user@gmail.com";

constexpr char kAppInstallTemplate[] = R"(
    {
      "appTitle": "Test App 1",
      "%s": "%s",
      "iconPath": "https://www.test.com/icon",
      "launchInStandaloneWindow": true
    }
)";

constexpr char kValidURL[] = "https://www.news.com";
constexpr char kExpectedAppId[] = "pelbleffcebihhhneibojlacmlmlnjhm";
constexpr char kInvalidURL[] = "www";
constexpr char kValidURLKey[] = "url";
constexpr char kInvalidURLKey[] = "urlInvalid";

}  // namespace

class InstallWebAppActionPerformerTest : public testing::Test {
 public:
  InstallWebAppActionPerformerTest() = default;
  InstallWebAppActionPerformerTest(const InstallWebAppActionPerformerTest&) =
      delete;
  InstallWebAppActionPerformerTest& operator=(
      const InstallWebAppActionPerformerTest&) = delete;
  ~InstallWebAppActionPerformerTest() override = default;

  void SetUp() override {
    user_manager_.Reset(std::make_unique<ash::FakeChromeUserManager>());

    profile_manager_ = std::make_unique<TestingProfileManager>(
        TestingBrowserProcess::GetGlobal());
    ASSERT_TRUE(profile_manager_->SetUp());

    const user_manager::User* user =
        user_manager_->AddUser(AccountId::FromUserEmail(kTestProfileName));
    user_manager_->LoginUser(user->GetAccountId());
    user_manager_->SwitchActiveUser(user->GetAccountId());

    // Note that user profiles are created after user login in reality.
    profile_ = profile_manager_->CreateTestingProfile(kTestProfileName);

    auto* provider = web_app::FakeWebAppProvider::Get(profile_);
    provider->SetOsIntegrationManager(
        std::make_unique<web_app::FakeOsIntegrationManager>(
            profile_,
            /*file_handler_manager=*/nullptr,
            /*protocol_handler_manager=*/nullptr));
    web_app::test::AwaitStartWebAppProviderAndSubsystems(profile_);
    install_action_ = std::make_unique<InstallWebAppActionPerformer>();
  }

  void TearDown() override {
    profile_ = nullptr;
    profile_manager_->DeleteAllTestingProfiles();
    profile_manager_.reset();
  }

  bool EnsureWebAppInstalled(const std::string& url) {
    apps_installed_run_loop_.Run();

    // Let's verify that the pwa was installed.
    web_app::WebAppProvider* provider =
        web_app::WebAppProvider::GetForWebApps(profile_);

    // note registrar_unsafe() is fine since we are just
    // running a simple test here.
    const auto& registrar = provider->registrar_unsafe();
    return registrar.GetAppLaunchUrl(kExpectedAppId) == GURL(url);
  }

  bool EnsureAppsInstallActionFailed() {
    action_failed_run_loop_.Run();
    return true;
  }

  void InstallWebAppActionPerformerCallback(
      growth::ActionResult result,
      std::optional<growth::ActionResultReason> reason) {
    if (result == growth::ActionResult::kSuccess) {
      std::move(app_installed_closure_).Run();
    } else {
      std::move(install_action_failed_closure_).Run();
    }
  }

  InstallWebAppActionPerformer& action() { return *install_action_; }

 private:
  content::BrowserTaskEnvironment task_environment_;

  base::RunLoop apps_installed_run_loop_;
  base::RunLoop action_failed_run_loop_;

  base::OnceClosure app_installed_closure_ =
      apps_installed_run_loop_.QuitClosure();
  base::OnceClosure install_action_failed_closure_ =
      action_failed_run_loop_.QuitClosure();

  raw_ptr<Profile> profile_ = nullptr;
  user_manager::TypedScopedUserManager<ash::FakeChromeUserManager>
      user_manager_;
  std::unique_ptr<InstallWebAppActionPerformer> install_action_;
  std::unique_ptr<TestingProfileManager> profile_manager_;
};

TEST_F(InstallWebAppActionPerformerTest, TestValidInstallation) {
  const auto validInstallDictString =
      base::StringPrintf(kAppInstallTemplate, kValidURLKey, kValidURL);
  auto value = base::JSONReader::Read(validInstallDictString,
                                      base::JSON_PARSE_CHROMIUM_EXTENSIONS);
  ASSERT_TRUE(value.has_value());
  action().Run(/*campaign_id=*/1, /*group_id=*/std::nullopt, &value->GetDict(),
               base::BindOnce(&InstallWebAppActionPerformerTest::
                                  InstallWebAppActionPerformerCallback,
                              base::Unretained(this)));
  EXPECT_TRUE(EnsureWebAppInstalled(kValidURL));
}

TEST_F(InstallWebAppActionPerformerTest, TestInvalidInstallationInvalidURL) {
  const auto invalidInstallDictString =
      base::StringPrintf(kAppInstallTemplate, kValidURLKey, kInvalidURL);
  auto value = base::JSONReader::Read(invalidInstallDictString,
                                      base::JSON_PARSE_CHROMIUM_EXTENSIONS);
  ASSERT_TRUE(value.has_value());

  action().Run(/*campaign_id=*/1, /*group_id=*/std::nullopt, &value->GetDict(),
               base::BindOnce(&InstallWebAppActionPerformerTest::
                                  InstallWebAppActionPerformerCallback,
                              base::Unretained(this)));
  EXPECT_TRUE(EnsureAppsInstallActionFailed());
}

TEST_F(InstallWebAppActionPerformerTest, TestInvalidUrlKey) {
  const auto invalidInstallDictString =
      base::StringPrintf(kAppInstallTemplate, kInvalidURLKey, kValidURL);
  auto value = base::JSONReader::Read(invalidInstallDictString,
                                      base::JSON_PARSE_CHROMIUM_EXTENSIONS);
  ASSERT_TRUE(value.has_value());

  action().Run(/*campaign_id=*/1, /*group_id=*/std::nullopt, &value->GetDict(),
               base::BindOnce(&InstallWebAppActionPerformerTest::
                                  InstallWebAppActionPerformerCallback,
                              base::Unretained(this)));
  EXPECT_TRUE(EnsureAppsInstallActionFailed());
}

TEST_F(InstallWebAppActionPerformerTest, InvalidRequest) {
  constexpr char kInvalidParams[] = R"({
      "invalidInstallWebAppParams" : {
        "param" : "param"
      }
    })";
  auto value = base::JSONReader::Read(kInvalidParams,
                                      base::JSON_PARSE_CHROMIUM_EXTENSIONS);
  ASSERT_TRUE(value.has_value());
  action().Run(/*campaign_id=*/1, /*group_id=*/std::nullopt, &value->GetDict(),
               base::BindOnce(&InstallWebAppActionPerformerTest::
                                  InstallWebAppActionPerformerCallback,
                              base::Unretained(this)));
  EXPECT_TRUE(EnsureAppsInstallActionFailed());
}
