// 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 "chrome/browser/extensions/api/management/chrome_management_api_delegate.h"

#include <memory>
#include <string>
#include <utility>

#include "chrome/browser/extensions/chrome_extension_function_details.h"
#include "chrome/browser/extensions/extension_management.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/launch_util.h"
#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "extensions/browser/api/management/management_api.h"
#include "extensions/browser/extension_registrar.h"
#include "extensions/browser/launch_util.h"
#include "extensions/browser/supervised_user_extensions_delegate.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/extension_urls.h"
#include "extensions/common/mojom/context_type.mojom.h"

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

namespace extensions {
namespace {

class ManagementSetEnabledFunctionInstallPromptDelegate
    : public InstallPromptDelegate {
 public:
  ManagementSetEnabledFunctionInstallPromptDelegate(
      content::WebContents* web_contents,
      content::BrowserContext* browser_context,
      const Extension* extension,
      base::OnceCallback<void(bool)> callback)
      : callback_(std::move(callback)) {
    InstallPromptData::PromptType type =
        ExtensionInstallPrompt::GetReEnablePromptTypeForExtension(
            browser_context, extension);
    install_prompt_ = std::make_unique<ExtensionInstallPrompt>(
        web_contents, std::make_unique<InstallPromptData>(type));
    install_prompt_->ShowDialog(
        base::BindOnce(&ManagementSetEnabledFunctionInstallPromptDelegate::
                           OnInstallPromptDone,
                       weak_factory_.GetWeakPtr()),
        extension, nullptr,
        ExtensionInstallPrompt::GetDefaultShowDialogCallback());
  }

  ManagementSetEnabledFunctionInstallPromptDelegate(
      const ManagementSetEnabledFunctionInstallPromptDelegate&) = delete;
  ManagementSetEnabledFunctionInstallPromptDelegate& operator=(
      const ManagementSetEnabledFunctionInstallPromptDelegate&) = delete;

  ~ManagementSetEnabledFunctionInstallPromptDelegate() override = default;

 private:
  void OnInstallPromptDone(
      ExtensionInstallPrompt::DoneCallbackPayload payload) {
    // This dialog doesn't support the "withhold permissions" checkbox.
    DCHECK_NE(
        payload.result,
        ExtensionInstallPrompt::Result::ACCEPTED_WITH_WITHHELD_PERMISSIONS);
    std::move(callback_).Run(payload.result ==
                             ExtensionInstallPrompt::Result::ACCEPTED);
  }

  // Used for prompting to re-enable items with permissions escalation updates.
  std::unique_ptr<ExtensionInstallPrompt> install_prompt_;

  base::OnceCallback<void(bool)> callback_;

  base::WeakPtrFactory<ManagementSetEnabledFunctionInstallPromptDelegate>
      weak_factory_{this};
};

class ManagementUninstallFunctionUninstallDialogDelegate
    : public ExtensionUninstallDialog::Delegate,
      public UninstallDialogDelegate {
 public:
  ManagementUninstallFunctionUninstallDialogDelegate(
      ManagementUninstallFunctionBase* function,
      const Extension* target_extension,
      bool show_programmatic_uninstall_ui)
      : function_(function) {
    ChromeExtensionFunctionDetails details(function);
    extension_uninstall_dialog_ = ExtensionUninstallDialog::Create(
        Profile::FromBrowserContext(function->browser_context()),
        details.GetNativeWindowForUI(), this);
    bool uninstall_from_webstore =
        (function->extension() &&
         function->extension()->id() == kWebStoreAppId) ||
        function->source_url().DomainIs(
            extension_urls::GetNewWebstoreLaunchURL().GetHost());
    UninstallSource source;
    UninstallReason reason;
    if (uninstall_from_webstore) {
      source = UNINSTALL_SOURCE_CHROME_WEBSTORE;
      reason = UNINSTALL_REASON_CHROME_WEBSTORE;
    } else if (function->source_context_type() == mojom::ContextType::kWebUi) {
      source = UNINSTALL_SOURCE_CHROME_EXTENSIONS_PAGE;
      // TODO: Update this to a new reason; it shouldn't be lumped in with
      // other uninstalls if it's from the chrome://extensions page.
      reason = UNINSTALL_REASON_MANAGEMENT_API;
    } else {
      source = UNINSTALL_SOURCE_EXTENSION;
      reason = UNINSTALL_REASON_MANAGEMENT_API;
    }
    if (show_programmatic_uninstall_ui) {
      extension_uninstall_dialog_->ConfirmUninstallByExtension(
          target_extension, function->extension(), reason, source);
    } else {
      extension_uninstall_dialog_->ConfirmUninstall(target_extension, reason,
                                                    source);
    }
  }

  ManagementUninstallFunctionUninstallDialogDelegate(
      const ManagementUninstallFunctionUninstallDialogDelegate&) = delete;
  ManagementUninstallFunctionUninstallDialogDelegate& operator=(
      const ManagementUninstallFunctionUninstallDialogDelegate&) = delete;

  ~ManagementUninstallFunctionUninstallDialogDelegate() override = default;

  // ExtensionUninstallDialog::Delegate implementation.
  void OnExtensionUninstallDialogClosed(bool did_start_uninstall,
                                        const std::u16string& error) override {
    function_->OnExtensionUninstallDialogClosed(did_start_uninstall, error);
  }

 private:
  raw_ptr<ManagementUninstallFunctionBase> function_;
  std::unique_ptr<ExtensionUninstallDialog> extension_uninstall_dialog_;
};

SupervisedUserExtensionsDelegate*
GetSupervisedUserExtensionsDelegateFromContext(
    content::BrowserContext* context) {
  SupervisedUserExtensionsDelegate* supervised_user_extensions_delegate =
      ManagementAPI::GetFactoryInstance()
          ->Get(context)
          ->GetSupervisedUserExtensionsDelegate();
  CHECK(supervised_user_extensions_delegate);
  return supervised_user_extensions_delegate;
}

}  // namespace

ChromeManagementAPIDelegate::ChromeManagementAPIDelegate() = default;

ChromeManagementAPIDelegate::~ChromeManagementAPIDelegate() = default;

GURL ChromeManagementAPIDelegate::GetFullLaunchURL(
    const Extension* extension) const {
  return AppLaunchInfo::GetFullLaunchURL(extension);
}

LaunchType ChromeManagementAPIDelegate::GetLaunchType(
    const ExtensionPrefs* prefs,
    const Extension* extension) const {
  return ::extensions::GetLaunchType(prefs, extension);
}

std::unique_ptr<InstallPromptDelegate>
ChromeManagementAPIDelegate::SetEnabledFunctionDelegate(
    content::WebContents* web_contents,
    content::BrowserContext* browser_context,
    const Extension* extension,
    base::OnceCallback<void(bool)> callback) const {
  return std::make_unique<ManagementSetEnabledFunctionInstallPromptDelegate>(
      web_contents, browser_context, extension, std::move(callback));
}

std::unique_ptr<UninstallDialogDelegate>
ChromeManagementAPIDelegate::UninstallFunctionDelegate(
    ManagementUninstallFunctionBase* function,
    const Extension* target_extension,
    bool show_programmatic_uninstall_ui) const {
  return std::make_unique<ManagementUninstallFunctionUninstallDialogDelegate>(
      function, target_extension, show_programmatic_uninstall_ui);
}

void ChromeManagementAPIDelegate::EnableExtension(
    content::BrowserContext* context,
    const ExtensionId& extension_id) const {
  const Extension* extension =
      ExtensionRegistry::Get(context)->GetExtensionById(
          extension_id, ExtensionRegistry::EVERYTHING);
  // The extension must exist as this method is invoked on enabling an extension
  // from the extensions management page (see `ManagementSetEnabledFunction`).
  CHECK(extension);

  SupervisedUserExtensionsDelegate* extensions_delegate =
      GetSupervisedUserExtensionsDelegateFromContext(context);
  extensions_delegate->MaybeRecordPermissionsIncreaseMetrics(*extension);
  extensions_delegate->RecordExtensionEnablementUmaMetrics(/*enabled=*/true);

  // If the extension was disabled for a permissions increase, the Management
  // API will have displayed a re-enable prompt to the user, so we know it's
  // safe to grant permissions here.
  ExtensionRegistrar::Get(context)->GrantPermissionsAndEnableExtension(
      *extension);
}

void ChromeManagementAPIDelegate::DisableExtension(
    content::BrowserContext* context,
    const Extension* source_extension,
    const ExtensionId& extension_id,
    disable_reason::DisableReason disable_reason) const {
  SupervisedUserExtensionsDelegate* extensions_delegate =
      GetSupervisedUserExtensionsDelegateFromContext(context);
  extensions_delegate->RecordExtensionEnablementUmaMetrics(/*enabled=*/false);
  ExtensionRegistrar::Get(context)->DisableExtensionWithSource(
      source_extension, extension_id, disable_reason);
}

bool ChromeManagementAPIDelegate::UninstallExtension(
    content::BrowserContext* context,
    const ExtensionId& transient_extension_id,
    UninstallReason reason,
    std::u16string* error) const {
  return extensions::ExtensionRegistrar::Get(context)->UninstallExtension(
      transient_extension_id, reason, error);
}

void ChromeManagementAPIDelegate::SetLaunchType(
    content::BrowserContext* context,
    const ExtensionId& extension_id,
    LaunchType launch_type) const {
  ::extensions::SetLaunchType(context, extension_id, launch_type);
}

GURL ChromeManagementAPIDelegate::GetIconURL(const Extension* extension,
                                             int icon_size,
                                             ExtensionIconSet::Match match,
                                             bool grayscale) const {
  return ExtensionIconSource::GetIconURL(extension, icon_size, match,
                                         grayscale);
}

GURL ChromeManagementAPIDelegate::GetEffectiveUpdateURL(
    const Extension& extension,
    content::BrowserContext* context) const {
  ExtensionManagement* extension_management =
      ExtensionManagementFactory::GetForBrowserContext(context);
  return extension_management->GetEffectiveUpdateURL(extension);
}

}  // namespace extensions
