// 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/web_app_ui_manager.h"

#include <optional>

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "build/build_config.h"
#include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
#include "chrome/browser/web_applications/test/web_app_test_utils.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/browser/web_applications/web_app_ui_manager.h"
#include "chrome/common/chrome_switches.h"
#include "components/services/app_service/public/cpp/app_launch_params.h"
#include "components/services/app_service/public/cpp/app_launch_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

namespace web_app {
namespace {

#if BUILDFLAG(IS_WIN)
const base::FilePath::CharType kCurrentDirectory[] =
    FILE_PATH_LITERAL("\\path");
#else
const base::FilePath::CharType kCurrentDirectory[] = FILE_PATH_LITERAL("/path");
#endif  // BUILDFLAG(IS_WIN)

const char kTestAppId[] = "https://example.com/";

class WebAppUiManagerTest : public testing::Test {
 public:
  WebAppUiManagerTest() = default;
  ~WebAppUiManagerTest() override = default;

 protected:
  void InitAppWithDisplayMode(DisplayMode display_mode) {
    GURL test_app_url = GURL(kTestAppId);
    auto web_app = std::make_unique<WebApp>(webapps::ManifestId(test_app_url),
                                            test_app_url, test_app_url);
    web_app->SetDisplayMode(display_mode);
    if (display_mode == DisplayMode::kBrowser) {
      web_app->SetUserDisplayMode(mojom::UserDisplayMode::kBrowser);
    } else {
      web_app->SetUserDisplayMode(mojom::UserDisplayMode::kStandalone);
    }
    Registry map;
    map[kTestAppId] = std::move(web_app);
    registrar_.InitRegistry(std::move(map));
  }

  apps::AppLaunchParams CreateLaunchParams(
      const base::CommandLine& command_line,
      const std::vector<base::FilePath>& launch_files,
      const std::optional<GURL>& protocol_handler_launch_url) {
    apps::AppLaunchParams params(
        kTestAppId, apps::LaunchContainer::kLaunchContainerNone,
        WindowOpenDisposition::UNKNOWN, apps::LaunchSource::kFromCommandLine);

    params.current_directory = base::FilePath(kCurrentDirectory);
    params.command_line = command_line;
    params.launch_files = launch_files;
    params.protocol_handler_launch_url = protocol_handler_launch_url;

    return params;
  }

  base::CommandLine CreateCommandLine() {
    base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
    command_line.AppendSwitchASCII(switches::kAppId, kTestAppId);
    return command_line;
  }

  void ValidateOptionalGURL(const std::optional<GURL>& actual,
                            const std::optional<GURL>& expected) {
    ASSERT_EQ(actual.has_value(), expected.has_value());
    if (actual.has_value()) {
      EXPECT_EQ(actual.value(), expected.value());
    }
  }

  void ValidateLaunchParams(const apps::AppLaunchParams& actual_results,
                            const apps::AppLaunchParams& expected_results) {
    EXPECT_EQ(actual_results.app_id, expected_results.app_id);
    EXPECT_EQ(actual_results.command_line.GetArgs(),
              expected_results.command_line.GetArgs());
    EXPECT_EQ(actual_results.current_directory,
              expected_results.current_directory);
    EXPECT_EQ(actual_results.launch_source, expected_results.launch_source);
    EXPECT_EQ(actual_results.launch_files, expected_results.launch_files);
    ValidateOptionalGURL(actual_results.protocol_handler_launch_url,
                         expected_results.protocol_handler_launch_url);

    EXPECT_EQ(actual_results.protocol_handler_launch_url,
              expected_results.protocol_handler_launch_url);
  }

  WebAppRegistrarMutable registrar_{nullptr};
};

TEST_F(WebAppUiManagerTest, DefaultParamsTab) {
  base::CommandLine command_line = CreateCommandLine();

  InitAppWithDisplayMode(DisplayMode::kBrowser);

  apps::AppLaunchParams expected_results =
      CreateLaunchParams(command_line, std::vector<base::FilePath>(),
                         /*protocol_handler_launch_url=*/std::nullopt);

  ValidateLaunchParams(
      WebAppUiManager::CreateAppLaunchParamsWithoutWindowConfig(
          kTestAppId, command_line, base::FilePath(kCurrentDirectory),
          /*protocol_handler_launch_url=*/std::nullopt,
          /*file_launch_url=*/std::nullopt, /*launch_files=*/{}),
      expected_results);
}

TEST_F(WebAppUiManagerTest, DefaultParamsStandalone) {
  InitAppWithDisplayMode(DisplayMode::kStandalone);
  base::CommandLine command_line = CreateCommandLine();

  apps::AppLaunchParams expected_results =
      CreateLaunchParams(command_line, std::vector<base::FilePath>(),
                         /*protocol_handler_launch_url=*/std::nullopt);

  ValidateLaunchParams(
      WebAppUiManager::CreateAppLaunchParamsWithoutWindowConfig(
          kTestAppId, command_line, base::FilePath(kCurrentDirectory),
          /*protocol_handler_launch_url=*/std::nullopt,
          /*file_launch_url=*/std::nullopt, /*launch_files=*/{}),
      expected_results);
}

TEST_F(WebAppUiManagerTest, ProtocolHandlerUrl) {
  InitAppWithDisplayMode(DisplayMode::kStandalone);
  const std::optional<GURL> protocol_handler_launch_url(
      GURL("web+test://test"));
  base::CommandLine command_line = CreateCommandLine();
  command_line.AppendArg(protocol_handler_launch_url.value().spec());

  apps::AppLaunchParams expected_results = CreateLaunchParams(
      command_line, std::vector<base::FilePath>(), protocol_handler_launch_url);
  expected_results.launch_source = apps::LaunchSource::kFromProtocolHandler;

  ValidateLaunchParams(
      WebAppUiManager::CreateAppLaunchParamsWithoutWindowConfig(
          kTestAppId, command_line, base::FilePath(kCurrentDirectory),
          protocol_handler_launch_url,
          /*file_launch_url=*/std::nullopt, /*launch_files=*/{}),
      expected_results);
}

TEST_F(WebAppUiManagerTest, LaunchApplication_ProtocolMailTo) {
  InitAppWithDisplayMode(DisplayMode::kStandalone);
  const std::optional<GURL> protocol_handler_launch_url(
      GURL("mailto://test@test.com"));
  base::CommandLine command_line = CreateCommandLine();

  command_line.AppendArg(protocol_handler_launch_url.value().spec());

  apps::AppLaunchParams expected_results = CreateLaunchParams(
      command_line, std::vector<base::FilePath>(), protocol_handler_launch_url);
  expected_results.launch_source = apps::LaunchSource::kFromProtocolHandler;

  ValidateLaunchParams(
      WebAppUiManager::CreateAppLaunchParamsWithoutWindowConfig(
          kTestAppId, command_line, base::FilePath(kCurrentDirectory),
          protocol_handler_launch_url, std::nullopt, {}),
      expected_results);
}

// Apps are not allowed to handle https:// either as protocols or as file paths.
TEST_F(WebAppUiManagerTest, LaunchApplication_ProtocolDisallowed) {
  InitAppWithDisplayMode(DisplayMode::kStandalone);
  base::CommandLine command_line = CreateCommandLine();

  command_line.AppendArg("https://www.test.com/");

  apps::AppLaunchParams expected_results =
      CreateLaunchParams(command_line, {}, std::nullopt);

  ValidateLaunchParams(
      WebAppUiManager::CreateAppLaunchParamsWithoutWindowConfig(
          kTestAppId, command_line, base::FilePath(kCurrentDirectory),
          std::nullopt, std::nullopt, {}),
      expected_results);
}

}  // namespace
}  // namespace web_app
