// 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.

#ifndef CHROME_BROWSER_UI_VIEWS_PERMISSIONS_CHIP_CHIP_CONTROLLER_H_
#define CHROME_BROWSER_UI_VIEWS_PERMISSIONS_CHIP_CHIP_CONTROLLER_H_

#include <cstddef>
#include <memory>

#include "base/check_is_test.h"
#include "base/functional/callback_helpers.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/timer/timer.h"
#include "chrome/browser/ui/views/permissions/chip/permission_chip_interface.h"
#include "components/permissions/permission_prompt.h"
#include "components/permissions/permission_request_manager.h"
#include "components/permissions/permission_util.h"
#include "ui/views/view_tracker.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"

class ContentSettingBubbleContents;
class ContentSettingImageViewDelegate;
class LocationBar;
class PermissionDashboardController;
class PermissionDashboardInterface;
class PermissionPromptBubbleBaseView;
class PermissionPromptChipModel;

// This class controls a chip UI to surface permission related information
// and prompts. For its creation, the controller expects an object of type
// PermissionChipInterface which should be a child of another UI component. No
// ownership is transferred through the creation, and the controller will never
// destruct the PermissionChipInterface object. The controller and its chip are
// intended to be long-lived.
class ChipController : public permissions::PermissionRequestManager::Observer,
                       public views::WidgetObserver,
                       public PermissionChipInterface::BubbleOwnerDelegate,
                       public PermissionChipInterface::Observer {
 public:
  class Observer : public base::CheckedObserver {
   public:
    // Triggered when the permission prompt shows.
    virtual void OnPermissionPromptShown() = 0;

    // Triggered when the permission prompt hides.
    virtual void OnPermissionPromptHidden() = 0;
  };

  ChipController(
      LocationBar* location_bar,
      ContentSettingImageViewDelegate* content_settings_image_delegate,
      PermissionChipInterface* chip,
      PermissionDashboardInterface* permission_dashboard = nullptr,
      PermissionDashboardController* permission_dashboard_controller = nullptr);

  ~ChipController() override;
  ChipController(const ChipController&) = delete;
  ChipController& operator=(const ChipController&) = delete;

  // PermissionRequestManager::Observer:
  void OnPermissionRequestManagerDestructed() override;
  void OnTabActiveChanged(bool is_active) override;
  // Called when the currently active permission request was finalized. That
  // could be called independently of `OnRequestDecided`.
  void OnRequestsFinalized() override;
  // Called when currently visible permission prompt was removed. That is called
  // independently from `OnRequestsFinalized` and `OnRequestDecided`.
  void OnPromptRemoved() override;

  // OnBubbleRemoved only triggers when a request chip (bubble) is removed, when
  // the user navigates while a confirmation chip is showing, the request is
  // already finished and hence OnBubbleRemoved is not triggered. Thus we need
  // to handle chip cleanup on navigation events separately.
  void OnNavigation(content::NavigationHandle* navigation_handle) override;

  // Called when there is a decision for a permission request.
  void OnRequestDecided(permissions::PermissionAction permissions) override;

  // PermissionChipInterface::BubbleOwnerDelegate:
  bool IsBubbleShowing() override;
  bool IsAnimating() const override;
  void RestartTimersOnMouseHover() override;

  // WidgetObserver:
  void OnWidgetDestroying(views::Widget* widget) override;
  void OnWidgetDestroyed(views::Widget* widget) override;
  void OnWidgetActivationChanged(views::Widget* widget, bool active) override;

  // PermissionChipInterface::Observer
  void OnChipVisibilityChanged(bool is_visible) override;
  void OnExpandAnimationEnded() override;
  void OnCollapseAnimationEnded() override;

  void AddObserver(Observer* observer);
  void RemoveObserver(Observer* observer);

  // Initializes the permission prompt model as well as the permission request
  // manager and observes the prompt bubble.
  void InitializePermissionPrompt(
      base::WeakPtr<permissions::PermissionPrompt::Delegate> delegate,
      base::OnceCallback<void()> = base::DoNothing());

  // Displays a permission chip. Call ShowPermissionPrompt() instead to show
  // both a chip and a prompt.
  void ShowPermissionChip(
      base::WeakPtr<permissions::PermissionPrompt::Delegate> delegate);

  // Displays a permission prompt using the chip UI.
  void ShowPermissionPrompt(
      base::WeakPtr<permissions::PermissionPrompt::Delegate> delegate);

  // Returns the PermissionChipInterface (e.g. PermissionChipView or
  // WebUIPermissionChip) controlled by this class.
  PermissionChipInterface* chip() { return chip_; }

  // Hide and clean up the chip.
  void ResetPermissionPromptChip();

  // Hide and clean up only if the chip displays a permission request. That
  // method is no-op if the confirmation chip is displayed.
  void ResetPermissionRequestChip();

  bool IsPermissionPromptChipVisible() {
    return chip_ && chip_->GetVisible() && permission_prompt_model_;
  }

  views::Widget* GetBubbleWidget();

  // Returns the currently active permission prompt bubble, specifically when a
  // non quiet chip UI is expected. This method CHECKs that the prompt style is
  // `kChip`.
  PermissionPromptBubbleBaseView* GetPromptBubbleView();
  // Returns the currently active permission prompt bubble,  specifically when a
  // quiet chip UI is expected. This method CHECKs that the prompt style is
  // `kQuietChip`.
  ContentSettingBubbleContents* GetContentSettingBubbleContentsForTesting();

  void ClosePermissionPrompt();
  void PromptDecided(permissions::PermissionAction action);

  PermissionPromptChipModel* permission_prompt_model() {
    return permission_prompt_model_.get();
  }

  bool should_expand_for_testing();

  bool is_collapse_timer_running_for_testing() const {
    CHECK_IS_TEST();
    return collapse_timer_.IsRunning();
  }

  void fire_collapse_timer_for_testing() {
    CHECK_IS_TEST();
    collapse_timer_.FireNow();
  }

  bool is_dismiss_timer_running_for_testing() const {
    CHECK_IS_TEST();
    return dismiss_timer_.IsRunning();
  }

  void fire_dismiss_timer_for_testing() {
    CHECK_IS_TEST();
    dismiss_timer_.FireNow();
  }

  views::View* get_prompt_bubble_view_for_testing() {
    CHECK_IS_TEST();
    return bubble_tracker_.view();
  }

  std::optional<permissions::PermissionRequestManager*>
  active_permission_request_manager() {
    return active_chip_permission_request_manager_;
  }

  bool is_confirmation_showing() const { return is_confirmation_showing_; }

  bool is_waiting_for_confirmation_collapse_for_testing() const {
    CHECK_IS_TEST();
    return is_waiting_for_confirmation_collapse_;
  }

  void set_is_bubble_suppressed(bool is_bubble_suppressed) {
    is_bubble_suppressed_ = is_bubble_suppressed;
  }

  void DoNotCollapseForTesting() { do_no_collapse_for_testing_ = true; }

 private:
  FRIEND_TEST_ALL_PREFIXES(PermissionChipBrowserTest, AccessibleName);

  bool ShouldWaitForConfirmationToComplete() const;
  bool ShouldWaitForLHSIndicatorToCollapse() const;
  void AnimateExpand();

  // Confirmation chip.
  void HandleConfirmation(permissions::PermissionAction permission_action);
  void CollapseConfirmation();

  // Permission prompt chip functionality.
  void AnnouncePermissionRequestForAccessibility(const std::u16string& text);
  void CollapsePrompt(bool allow_restart);

  // Hides the chip and invalidates the layout.
  void HideChip();

  void ShowPermissionUi(
      base::WeakPtr<permissions::PermissionPrompt::Delegate> delegate);

  // Permission prompt bubble functiontionality.
  void OpenPermissionPromptBubble();
  void ClosePermissionPromptBubbleWithReason(
      views::Widget::ClosedReason reason);

  void RecordRequestChipButtonPressed(const char* recordKey);

  // Event handling.
  void ObservePromptBubble();
  void OnPromptBubbleDismissed();
  void OnPromptExpired();
  void OnRequestChipButtonPressed();

  // Updates chip icon, text and theme with model.
  void SyncChipWithModel();

  // Opens the Page Info Dialog.
  void ShowPageInfoDialog();

  // Actions executed when the user closes the page info dialog.
  void OnPageInfoBubbleClosed(views::Widget::ClosedReason closed_reason,
                              bool reload_prompt);

  // Clean up utility.
  void RemoveBubbleObserverAndResetTimersAndChipCallbacks();

  // Timer functionality.
  void StartCollapseTimer();
  void StartDismissTimer();
  void ResetTimers();

  // The location bar to which the chip is attached.
  LocationBar* GetLocationBar() { return location_bar_; }

  bool is_confirmation_showing_ = false;
  bool is_waiting_for_confirmation_collapse_ = false;

  // `LocationBar` owns this (sometimes indirectly via
  // PermissionDashboardController).
  raw_ptr<LocationBar> location_bar_ = nullptr;

  raw_ptr<ContentSettingImageViewDelegate> content_settings_image_delegate_ =
      nullptr;

  // The chip this controller modifies.
  raw_ptr<PermissionChipInterface> chip_;

  // `PermissionDashboardView` is an owner of PermissionChipInterface.
  raw_ptr<PermissionDashboardInterface> permission_dashboard_;
  // `PermissionDashboardController` is an owner of this.
  raw_ptr<PermissionDashboardController> permission_dashboard_controller_;

  // A timer used to dismiss the permission request after it's been collapsed
  // for a while.
  base::OneShotTimer dismiss_timer_;

  // A timer used to collapse the chip after a delay.
  base::OneShotTimer collapse_timer_;

  // A timer used to delay showing a prompt if a confirmation chip is being
  // displayed.
  base::OneShotTimer delay_prompt_timer_;

  bool parent_was_visible_when_activation_changed_ = false;

  // The model of a permission prompt if one is present.
  std::unique_ptr<PermissionPromptChipModel> permission_prompt_model_;

  std::optional<permissions::PermissionRequestManager*>
      active_chip_permission_request_manager_;

  views::ViewTracker bubble_tracker_;
  bool is_bubble_suppressed_ = false;

  bool do_no_collapse_for_testing_ = false;

  // Keep prompt's decision until the prompt's widget is removed.
  std::optional<permissions::PermissionAction> prompt_decision_;

  base::ScopedClosureRunner disallowed_custom_cursors_scope_;

  base::ScopedObservation<PermissionChipInterface,
                          PermissionChipInterface::Observer>
      observation_{this};

  base::ObserverList<Observer> permission_prompt_observers_;

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

#endif  // CHROME_BROWSER_UI_VIEWS_PERMISSIONS_CHIP_CHIP_CONTROLLER_H_
