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

#ifndef COMPONENTS_PERMISSIONS_PERMISSION_PROMPT_H_
#define COMPONENTS_PERMISSIONS_PERMISSION_PROMPT_H_

#include <memory>
#include <optional>
#include <vector>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "components/permissions/features.h"
#include "components/permissions/permission_request_data.h"
#include "components/permissions/permission_uma_constants.h"
#include "components/permissions/prediction_service/permission_ui_selector.h"
#include "components/permissions/resolvers/permission_prompt_options.h"
#include "ui/gfx/geometry/rect.h"
#include "url/gurl.h"

namespace content {
class WebContents;
}

namespace ui {
class Event;
}  // namespace ui

namespace permissions {
enum class PermissionPromptDisposition;

class EmbeddedPermissionPromptFlowModel;
class PermissionRequest;

// This class is the platform-independent interface through which the permission
// request managers (which are one per tab) communicate to the UI surface.
// When the visible tab changes, the UI code must provide an object of this type
// to the manager for the visible tab.
class PermissionPrompt {
 public:
  // Permission prompt behavior on tab switching.
  enum TabSwitchingBehavior {
    // The prompt should be kept as-is on tab switching (usually because it's
    // part of the containing tab so it will be hidden automatically when
    // switching from said tab)
    kKeepPromptAlive,
    // Destroy the prompt but keep the permission request pending. When the user
    // revisits the tab, the permission prompt is re-displayed.
    kDestroyPromptButKeepRequestPending,
    // Destroy the prompt and treat the permission request as being resolved
    // with the PermissionAction::IGNORED result.
    kDestroyPromptAndIgnoreRequest,
  };

  // The delegate will receive events caused by user action which need to
  // be persisted in the per-tab UI state.
  class Delegate {
   public:
    virtual ~Delegate() = default;

    // These pointers should not be stored as the actual request objects may be
    // deleted upon navigation and so on.
    virtual const std::vector<std::unique_ptr<PermissionRequest>>& Requests()
        const = 0;

    // Get the single origin for the current set of requests.
    virtual GURL GetRequestingOrigin() const = 0;

    // Get the top-level origin currently displayed in the address bar
    // associated with the requests.
    virtual GURL GetEmbeddingOrigin() const = 0;

    virtual void Accept(const PromptOptions& prompt_options) = 0;
    virtual void AcceptThisTime(const PromptOptions& prompt_options) = 0;
    virtual void Deny(const PromptOptions& prompt_options) = 0;
    virtual void Dismiss(const PromptOptions& prompt_options) = 0;
    virtual void Ignore(const PromptOptions& prompt_options) = 0;
    virtual void SwitchToLoudPrompt() = 0;

    virtual GeolocationAccuracy GetInitialGeolocationAccuracySelection()
        const = 0;

    // Returns the type of geolocation prompt that should be shown.
    virtual std::optional<GeolocationPromptType> GetGeolocationPromptType()
        const = 0;

    // Called to explicitly finalize the current requests.
    virtual void FinalizeCurrentRequests() = 0;

    virtual void OpenHelpCenterLink(const ui::Event& event) = 0;

    // This method preemptively ignores a permission request but does not
    // finalize a permission prompt. That is needed in case a permission prompt
    // is a quiet chip. This should be called only if an origin is subscribed to
    // the `PermissionStatus.onchange` listener.
    virtual void PreIgnoreQuietPrompt() = 0;

    // If |ShouldCurrentRequestUseQuietUI| return true, this will provide a
    // reason as to why the quiet UI needs to be used. Returns `std::nullopt`
    // otherwise.
    virtual std::optional<PermissionUiSelector::QuietUiReason>
    ReasonForUsingQuietUi() const = 0;

    // Notification permission requests might use a quiet UI when the
    // "quiet-notification-prompts" feature is enabled. This is done either
    // directly by the user in notifications settings, or via automatic logic
    // that might trigger the current request to use the quiet UI.
    virtual bool ShouldCurrentRequestUseQuietUI() const = 0;

    // If the LocationBar is not visible, there is no place to display a quiet
    // permission prompt. Abusive prompts will be ignored.
    virtual bool ShouldDropCurrentRequestIfCannotShowQuietly() const = 0;

    // Whether the current request has been shown to the user at least once.
    virtual bool WasCurrentRequestAlreadyDisplayed() = 0;

    // Set whether the current request should be dismissed if the current tab is
    // closed.
    virtual void SetDismissOnTabClose() = 0;

    // Set whether the permission prompt bubble was shown for the current
    // request.
    virtual void SetPromptShown() = 0;

    // Set when the user made any decision for the currentrequest.
    virtual void SetDecisionTime() = 0;

    // Set when the user made any decision for manage settings.
    virtual void SetManageClicked() = 0;

    // Set when the user made any decision for clicking on learn more link.
    virtual void SetLearnMoreClicked() = 0;

    // HaTS surveys may display at an inconvenient time, such as when a chip
    // shown collapses after a certain timeout. To prevent affecting
    // usability, this setter sets a callback that is be called when a HaTS
    // survey is triggered to take appropriate actions.
    virtual void SetHatsShownCallback(base::OnceCallback<void()> callback) = 0;

    virtual content::WebContents* GetAssociatedWebContents() = 0;

    virtual base::WeakPtr<Delegate> GetWeakPtr() = 0;

    // Recreate the UI view because the UI flavor needs to change. Returns true
    // iff successful.
    virtual bool RecreateView() = 0;

    virtual const PermissionPrompt* GetCurrentPrompt() const = 0;

    virtual EmbeddedPermissionPromptFlowModel* GetEmbeddedPromptFlowModel()
        const;

    virtual void CalculateCurrentVariantForEmbeddedPrompt();

    virtual void AdvanceOrFinalizeEmbeddedPromptFlow();
  };

  typedef base::RepeatingCallback<
      std::unique_ptr<PermissionPrompt>(content::WebContents*, Delegate*)>
      Factory;

  // Create and display a platform specific prompt.
  static std::unique_ptr<PermissionPrompt> Create(
      content::WebContents* web_contents,
      Delegate* delegate);

  virtual ~PermissionPrompt() = default;

  // Updates where the prompt should be anchored. ex: fullscreen toggle.
  // Returns true, if the update was successful, and false if the caller should
  // recreate the view instead.
  virtual bool UpdateAnchor() = 0;

  // Get the behavior of this prompt when the user switches away from the
  // associated tab.
  virtual TabSwitchingBehavior GetTabSwitchingBehavior() = 0;

  // Get the type of prompt UI shown for metrics.
  virtual PermissionPromptDisposition GetPromptDisposition() const = 0;

  // Check if the view shown is an "Ask" prompt for metrics. Currently this only
  // distinguishes different prompt views displayed through the Page Embedded
  // Permission Element.
  virtual bool IsAskPrompt() const = 0;

  // Get the prompt view bounds in screen coordinates.
  virtual std::optional<gfx::Rect> GetViewBoundsInScreen() const = 0;

  // Return what variant of the secondary UI is shown for Page Embedded
  // Permission Element.
  virtual std::vector<permissions::ElementAnchoredBubbleVariant>
  GetPromptVariants() const = 0;

  virtual std::optional<feature_params::PermissionElementPromptPosition>
  GetPromptPosition() const = 0;
};
}  // namespace permissions

#endif  // COMPONENTS_PERMISSIONS_PERMISSION_PROMPT_H_
