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

#ifndef CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_EXTENSIONS_DELEGATE_IMPL_H_
#define CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_EXTENSIONS_DELEGATE_IMPL_H_

#include <memory>
#include <optional>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/supervised_user/supervised_user_extensions_manager.h"
#include "chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.h"
#include "extensions/browser/supervised_user_extensions_delegate.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/supervised_user/chromeos/parent_access_extension_approvals_manager.h"
#endif

namespace content {
class BrowserContext;
class WebContents;
}  // namespace content

namespace gfx {
class ImageSkia;
}  // namespace gfx

class ParentPermissionDialog;

namespace extensions {

class ExtensionIconLoader;
enum class ExtensionInstalledBlockedByParentDialogAction;

// Handles extensions approvals for supervised users.
// Decides which version of the flow should be used and dispatches the calls
// accordingly. Currently we support two flow versions.
// - A browser dialog, which is shown for non-ChromeOS desktop platforms.
// - A system component dialog implemented for ChromeOS.
class SupervisedUserExtensionsDelegateImpl
    : public SupervisedUserExtensionsDelegate {
 public:
  explicit SupervisedUserExtensionsDelegateImpl(
      content::BrowserContext* context);
  ~SupervisedUserExtensionsDelegateImpl() override;

  // SupervisedUserExtensionsDelegate overrides
  bool IsChild() const override;
  bool IsExtensionAllowedByParent(const Extension& extension) const override;
  void RequestToAddExtensionOrShowError(
      const Extension& extension,
      content::WebContents* web_contents,
      const gfx::ImageSkia& icon,
      ExtensionApprovalDoneCallback extension_approval_callback) override;
  void RequestToEnableExtensionOrShowError(
      const Extension& extension,
      content::WebContents* web_contents,
      ExtensionApprovalDoneCallback extension_approval_callback) override;
  void UpdateManagementPolicyRegistration() override;
  bool CanInstallExtensions() const override;
  void AddExtensionApproval(const extensions::Extension& extension) override;
  void MaybeRecordPermissionsIncreaseMetrics(
      const extensions::Extension& extension) override;
  void RemoveExtensionApproval(const extensions::Extension& extension) override;
  void RecordExtensionEnablementUmaMetrics(bool enabled) const override;
  bool CanSkipExtensionParentApprovals() override;
  void RecordAskParentDialogUmaMetrics(AskParentDialogState state) override;
  void RecordEnablementUmaMetrics(EnablementState state) override;
  extensions::ExtensionInstallPromptClient::Observer* GetInstallPromptObserver()
      override;

#if BUILDFLAG(IS_CHROMEOS)
  // Inject test instance for testing.
  void SetParentAccessExtensionApprovalsManagerForTesting(
      std::unique_ptr<ParentAccessExtensionApprovalsManager> manager);
#endif  // BUILDFLAG(IS_CHROMEOS)

 private:
  // Shows a ParentPermissionDialog for |extension| and calls
  // |done_callback| when it completes. Called for non-ChromeOS desktop
  // platforms.
  void ShowParentPermissionDialogForExtension(
      const Extension& extension,
      content::WebContents* contents,
      const gfx::ImageSkia& icon);

  // Shows ParentPermissionDialog indicating that |extension| has been blocked
  // and call |done_callback| when it completes. Depending on the blocked_action
  // type, the UI of the dialog may differ. Called for desktop non-ChromeOS
  // platforms.
  void ShowInstallBlockedByParentDialogForExtension(
      const Extension& extension,
      content::WebContents* contents,
      ExtensionInstalledBlockedByParentDialogAction blocked_action);

  // This method is called after all async data are fetched.
  // Since `WebContents` that initiated the request could be destroyed during
  // the async fetch the method cancels the request if `contents` was specified,
  // but is not valid anymore. `contents` is an optional argument because the
  // request can be made from an entry point not associate with `WebContents`.
  // This method decides which version of the flow to start.
  // On non-ChromeOS desktop platforms, a browser permission dialog or
  // browser blocked dialog is shown. On ChromeOS, ParentAccessDialog is
  // shown. The widget handles blocked state internally.
  void RequestExtensionApproval(
      const Extension& extension,
      std::optional<base::WeakPtr<content::WebContents>> contents,
      const gfx::ImageSkia& icon);

  // The ParentPermissionDialog pointer is only destroyed when a new dialog is
  // created or the SupervisedUserExtensionsDelegate is destroyed. Therefore
  // there can only be one dialog opened at a time and the last dialog object
  // can have a pretty long lifetime.
  // TODO(b/278874130): Move non ChromeOS platform-specific code to its own
  // class for clearer distinction.
  std::unique_ptr<ParentPermissionDialog> parent_permission_dialog_;

  SupervisedUserExtensionsDelegate::ExtensionApprovalDoneCallback
      done_callback_;

  std::unique_ptr<ExtensionIconLoader> icon_loader_;

  const raw_ptr<content::BrowserContext> context_;

#if BUILDFLAG(IS_CHROMEOS)
  // Manages the ChromeOS-specific approval flow.
  // The extension approvals manager is destroyed when a new ParentAccessDialog
  // is created or this delegate is destroyed.
  std::unique_ptr<ParentAccessExtensionApprovalsManager>
      extension_approvals_manager_;
#endif  // BUILDFLAG(IS_CHROMEOS)
  SupervisedUserExtensionsManager extensions_manager_;
  SupervisedUserExtensionsMetricsRecorder metrics_recorder_;
};

}  // namespace extensions

#endif  // CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_EXTENSIONS_DELEGATE_IMPL_H_
