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

#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/test/test_extension_dir.h"

namespace extensions {

class DelayedSettingChangeTest
    : public ExtensionBrowserTest,
      public testing::WithParamInterface<std::tuple<std::string, bool>> {
 protected:
  const Extension* InstallTestExtension(mojom::ManifestLocation location) {
    TestExtensionDir extension_dir;
    extension_dir.WriteFile(
        FILE_PATH_LITERAL("index.html"),
        "<html><body><div>Just a Chrome Extension</div></body></html>");

    extension_dir.WriteManifest(
        base::DictValue()
            .Set("manifest_version", 3)
            .Set("name", "Test Managed Extension")
            .Set("version", "1")
            .Set("host_permissions", base::ListValue().Append("file://*/*")));

    auto* extension = InstallExtensionWithSourceAndFlags(
        extension_dir.Pack(), 1, location,
        Extension::InitFromValueFlags::NO_FLAGS);
    CHECK(extension);

    return extension;
  }

  std::string GetScript_WarningMessageIsHidden(std::string_view setting) {
    // NOTE: This relies on the layout of the chrome://extensions page, and may
    // need to be updated if that layout changes.
    static constexpr std::string_view kScriptTemplate =
        R"((function() {
          return document.
            querySelector('extensions-manager').shadowRoot.
            querySelector('extensions-detail-view').shadowRoot.
            querySelector('%s').
            querySelector('%s-warning').hidden;
    })();)";

    return base::StringPrintf(kScriptTemplate, setting.data(), setting.data());
  }

  std::string GetScript_SwitchIsOn(std::string_view setting) {
    // NOTE: This relies on the layout of the chrome://extensions page, and may
    // need to be updated if that layout changes.
    static constexpr std::string_view kScriptTemplate =
        R"((function() {
          return document.
            querySelector('extensions-manager').shadowRoot.
            querySelector('extensions-detail-view').shadowRoot.
            querySelector('%s').shadowRoot.
            querySelector('cr-toggle').ariaPressed;
    })();)";

    return base::StringPrintf(kScriptTemplate, setting.data());
  }

  std::string GetScript_ClickSwitch(std::string_view setting) {
    // NOTE: This relies on the layout of the chrome://extensions page, and may
    // need to be updated if that layout changes.
    static constexpr std::string_view kScriptTemplate =
        R"((function() {
          document.
            querySelector('extensions-manager').shadowRoot.
            querySelector('extensions-detail-view').shadowRoot.
            querySelector('%s').shadowRoot.
            querySelector('cr-toggle').click();
    })();)";

    return base::StringPrintf(kScriptTemplate, setting.data());
  }
};

INSTANTIATE_TEST_SUITE_P(
    DelayedSetting,
    DelayedSettingChangeTest,
    ::testing::Combine(::testing::Values("#allow-incognito",
                                         "#allow-on-file-urls"),
                       ::testing::Bool()));

IN_PROC_BROWSER_TEST_P(DelayedSettingChangeTest,
                       DetailsPageOfManagedExtension) {
  scoped_refptr<const Extension> extension =
      InstallTestExtension(mojom::ManifestLocation::kExternalPolicy);
  ASSERT_TRUE(extension.get());

  auto [setting_under_test, initial_value] = GetParam();

  ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile());
  if (setting_under_test == "#allow-incognito") {
    extension_prefs->SetIsIncognitoEnabled(extension->id(), initial_value);
  } else {
    extension_prefs->SetAllowFileAccess(extension->id(), initial_value);
  }

  // Go to the extension details page.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), GURL("chrome://extensions?id=" + extension->id())));

  content::WebContents* tab = GetActiveWebContents();
  EXPECT_TRUE(content::WaitForLoadStop(tab));

  // Initial state after installation.
  EXPECT_EQ(true, content::EvalJs(tab, GetScript_WarningMessageIsHidden(
                                           setting_under_test)));
  EXPECT_EQ(initial_value,
            content::EvalJs(tab, GetScript_SwitchIsOn(setting_under_test))
                    .ExtractString() == "true");

  // Change the value of the setting.
  EXPECT_EQ(true,
            content::ExecJs(tab, GetScript_ClickSwitch(setting_under_test)));

  // The warning message should now be visible.
  EXPECT_EQ(false, content::EvalJs(tab, GetScript_WarningMessageIsHidden(
                                            setting_under_test)));

  // The status of the switch should have changed.
  EXPECT_NE(initial_value,
            content::EvalJs(tab, GetScript_SwitchIsOn(setting_under_test))
                    .ExtractString() == "true");
}

IN_PROC_BROWSER_TEST_P(DelayedSettingChangeTest, DetailsPageOfOtherExtension) {
  scoped_refptr<const Extension> managed_extension =
      InstallTestExtension(mojom::ManifestLocation::kExternalPolicy);
  ASSERT_TRUE(managed_extension.get());

  scoped_refptr<const Extension> second_extension =
      InstallTestExtension(mojom::ManifestLocation::kExternalPolicy);
  ASSERT_TRUE(second_extension.get());
  ASSERT_NE(managed_extension->id(), second_extension->id());

  auto [setting_under_test, initial_value] = GetParam();

  ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile());
  if (setting_under_test == "#allow-incognito") {
    extension_prefs->SetIsIncognitoEnabled(managed_extension->id(),
                                           initial_value);
  } else {
    extension_prefs->SetAllowFileAccess(managed_extension->id(), initial_value);
  }

  // Go to the managed extension details page.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), GURL("chrome://extensions?id=" + managed_extension->id())));
  content::WebContents* tab = GetActiveWebContents();
  EXPECT_TRUE(content::WaitForLoadStop(tab));

  // Initial state after installation.
  EXPECT_EQ(true, content::EvalJs(tab, GetScript_WarningMessageIsHidden(
                                           setting_under_test)));
  EXPECT_EQ(initial_value,
            content::EvalJs(tab, GetScript_SwitchIsOn(setting_under_test))
                    .ExtractString() == "true");

  // Change the value of the setting.
  EXPECT_EQ(true,
            content::ExecJs(tab, GetScript_ClickSwitch(setting_under_test)));

  // The warning message should now be visible.
  EXPECT_EQ(false, content::EvalJs(tab, GetScript_WarningMessageIsHidden(
                                            setting_under_test)));

  // Go to the 2nd extension details page.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), GURL("chrome://extensions?id=" + second_extension->id())));
  tab = GetActiveWebContents();
  EXPECT_TRUE(content::WaitForLoadStop(tab));

  EXPECT_EQ(true, content::EvalJs(tab, GetScript_WarningMessageIsHidden(
                                           setting_under_test)));

  // Return to the managed extension details page.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), GURL("chrome://extensions?id=" + managed_extension->id())));
  tab = GetActiveWebContents();
  EXPECT_TRUE(content::WaitForLoadStop(tab));

  // The warning message for the managed extension should be visible.
  EXPECT_EQ(false, content::EvalJs(tab, GetScript_WarningMessageIsHidden(
                                            setting_under_test)));

  // The status of the switch should have changed.
  EXPECT_NE(initial_value,
            content::EvalJs(tab, GetScript_SwitchIsOn(setting_under_test))
                    .ExtractString() == "true");
}
}  // namespace extensions
