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

#ifndef CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_WEBUI_LOCATION_BAR_H_
#define CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_WEBUI_LOCATION_BAR_H_

#include <variant>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "chrome/browser/ui/views/bubble/webui_bubble_reopen_suppressor.h"
#include "chrome/browser/ui/views/location_bar/content_setting_image_view.h"
#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
#include "chrome/browser/ui/views/location_bar/webui_content_setting_image_control.h"
#include "chrome/browser/ui/views/omnibox/omnibox_popup_presenter_delegate.h"
#include "chrome/browser/ui/views/omnibox/webui_readonly_omnibox.h"
#include "chrome/browser/ui/views/page_action/webui_page_action_control.h"
#include "components/browser_apis/ui_controllers/toolbar/icon_handle.h"
#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom.h"
#include "mojo/public/mojom/base/error.mojom.h"
#include "ui/base/interaction/element_tracker.h"
#include "ui/views/mouse_constants.h"

class BrowserWindowInterface;
class OmniboxController;
class OmniboxPopupView;
class OmniboxPopupViewWebUI;
class PermissionDashboardController;
class WebUIPermissionDashboard;
class Profile;
class WebUIToolbarControlDelegate;

namespace content {
struct ContextMenuParams;
}  // namespace content

namespace gfx {
class Point;
}  // namespace gfx

// A LocationBar implementation using WebUI.
class WebUILocationBar : public LocationBar,
                         public LocationBarTesting,
                         public ContentSettingImageViewDelegate,
                         public WebUIReadOnlyOmnibox::UpdatePropagator,
                         public OmniboxPopupPresenterDelegate {
 public:
  WebUILocationBar(BrowserWindowInterface* browser,
                   LocationBarView::Delegate* delegate);
  ~WebUILocationBar() override;

  void Init(WebUIToolbarControlDelegate* delegate);

  // WebUIReadOnlyOmnibox::UpdatePropagator:
  void PropagateOmniboxUpdate(
      toolbar_ui_api::mojom::OmniboxViewStatePtr update) override;
  void PropagateApplyFocusRingToAimButton(bool force_focus) override;
  void PropagateFocusRequest(
      toolbar_ui_api::mojom::FocusRequestTarget target) override;
  void OpenOmniboxIfFullPopup(bool query_zps) override;

  // Called from WebUIToolbarWebView:
  void OnThemeChanged();
  base::expected<std::monostate, mojo_base::mojom::ErrorPtr> OnOmniboxAction(
      toolbar_ui_api::mojom::OmniboxActionPtr action);
  void SetFocusWithin(bool focused);
  void OnBlur();

  void HandleContextMenu(views::Widget* widget,
                         const gfx::Point& point,
                         ui::mojom::MenuSourceType source_type,
                         const content::ContextMenuParams& menu_params);

  // LocationBar:
  void FocusLocation(bool is_user_initiated,
                     bool clear_focus_if_failed) override;
  void FocusSearch() override;

  void UpdateFocusBehavior(bool toolbar_visible) override;
  void UpdateContentSettingsIcons() override;
  void SaveStateToContents(content::WebContents* contents) override;
  void Revert() override;
  OmniboxView* GetOmniboxView() override;
  OmniboxPopupView* GetOmniboxPopupView() override;
  OmniboxPopupPresenterDelegate* GetPresenterDelegate() override;
  OmniboxController* GetOmniboxController() override;
  bool ShouldCloseOmniboxPopup(ui::MouseEvent* event) override;
  ChipController* GetChipController() override;
  PermissionDashboardController* GetPermissionDashboardController() override;
  content::WebContents* GetWebContents() override;
  void SetPermissionPromptShowing(bool showing) override;
  void AnnounceAlert(const std::u16string& announcement) override;

  // LocationBarTesting:
  LocationBarModel* GetLocationBarModel() override;
  std::optional<bubble_anchor_util::AnchorConfiguration> GetChipAnchor()
      override;
  ui::TrackedElement* GetAnchorOrNull() override;
  bool in_popup_state_transition() const override;
  BrowserWindowInterface* GetBrowser() override;
  Profile* GetProfile() override;
  void OnChanged() override;
  void UpdateWithoutTabRestore() override;
  bool IsInitialized() const override;
  bool IsVisible() const override;
  bool IsDrawn() const override;
  bool IsFullscreen() const override;
  bool IsEditingOrEmpty() const override;
  bool IsMouseHovered() const override;
  bool IsFocusWithin() const override;
  void InvalidateLayout() override;
  gfx::Rect Bounds() const override;
  gfx::Rect BoundsInScreen() const override;
  gfx::Size MinimumSize() const override;
  gfx::Size PreferredSize() const override;
  void Update(content::WebContents* contents) override;
  void ResetTabState(content::WebContents* contents) override;
  bool HasSecurityStateChanged() override;
  LocationBarTesting* GetLocationBarForTesting() override;
  bool TestContentSettingImagePressed(size_t index) override;
  bool IsContentSettingBubbleShowing(size_t index) override;

  // Left hand side (LHS) chip events (called from WebUIToolbarWebView)
  void OnLhsChipMousePressed(
      toolbar_ui_api::mojom::LhsChipIdentifier identifier,
      bool is_middle_click);
  void OnLhsChipClicked(toolbar_ui_api::mojom::LhsChipIdentifier identifier,
                        bool is_mouse_interaction);
  void OnLhsChipPointerEntered(
      toolbar_ui_api::mojom::LhsChipIdentifier identifier);
  void OnLhsChipPointerExited(
      toolbar_ui_api::mojom::LhsChipIdentifier identifier);
  void OnLhsChipExpandAnimationEnded(
      toolbar_ui_api::mojom::LhsChipIdentifier identifier);
  void OnLhsChipCollapseAnimationEnded(
      toolbar_ui_api::mojom::LhsChipIdentifier identifier);
  void OnLhsChipDrag(toolbar_ui_api::mojom::LhsChipIdentifier identifier,
                     ui::mojom::DragEventSource source);

  WebUIContentSettingImageControl& content_setting_image_control() {
    return content_setting_image_control_;
  }

  page_actions::WebUIPageActionControl& page_action_control() {
    return page_action_control_;
  }

  page_actions::PageActionViewInterface* GetPageActionViewInterface(
      actions::ActionId action_id) {
    return page_action_control_.GetPageActionViewInterface(action_id);
  }

  // ContentSettingImageViewDelegate:
  bool ShouldHideContentSettingImage() override;
  content::WebContents* GetContentSettingWebContents() override;
  ContentSettingBubbleModelDelegate* GetContentSettingBubbleModelDelegate()
      override;

  // OmniboxPopupPresenterDelegate:
  views::Widget* GetLocationBarWidget() override;
  OmniboxPopupFileSelector* GetOmniboxPopupFileSelector() const override;
  OmniboxPopupAimPresenter* GetOmniboxPopupAimPresenter() const override;
  views::View* GetLocationBarFocusRestoreView() override;

  void SetSuppressionThresholdForTesting(base::TimeDelta threshold);

 private:
  void OnMiddleClickPaste(base::TimeTicks event_timestamp, std::u16string text);

  friend class WebUILocationBarTest;
  friend class WebUIPermissionChipTest;

  // Determines whether the location icon should be overridden while a chip is
  // being displayed.
  bool ShouldChipOverrideLocationIcon();
  bool ShouldHideRHSIcons();

  void OnMovedOrShown(ui::TrackedElement* element);
  void OnPopupStateChanged(OmniboxPopupState old_state,
                           OmniboxPopupState new_state);
  void ClearInPopupStateTransition();

  void UpdateLocationBarFlagsState();
  void UpdateSelectedKeywordState();
  void RefreshAiModePageAction();

  // Updates the state of the LHS location bar chips (e.g. security chip) and
  // pushes it to the WebUI.
  void UpdateLhsChipsState(bool icon_known = false);

  void UpdatePageActions(content::WebContents* contents);

  // Updates the state of the content setting models (e.g. camera, microphone,
  // sensors) to reflect status on the current page. Pushes the updated
  // right-hand side content setting states to the WebUI. Returns true if the
  // state of at least one left-hand side permission chip changed, indicating
  // that `UpdateLhsChipsState()` should be called.
  bool UpdateContentSettingModels();

  ui::ImageModel UpdateLocationIcon(
      toolbar_ui_api::mojom::SecurityLevel security_level,
      bool is_text_dangerous);

  void OnIconFetched(const gfx::Image& image);

  void ShowPageInfoBubble();
  void OnPageInfoBubbleClosed(views::Widget::ClosedReason closed_reason,
                              bool reload_prompt);

  void HandleFocusRequestForFullPopup(
      toolbar_ui_api::mojom::FocusRequestTarget target);

  raw_ptr<BrowserWindowInterface> browser_ = nullptr;
  raw_ptr<LocationBarView::Delegate> delegate_ = nullptr;
  raw_ptr<WebUIToolbarControlDelegate> toolbar_delegate_ = nullptr;

  ui::ElementTracker::Subscription moved_subscription_;
  ui::ElementTracker::Subscription shown_subscription_;

  // Must be declared before `permission_dashboard_controller_` because the
  // `permission_dashboard_controller_` depends on models owned by
  // `content_setting_image_control_` during teardown.
  WebUIContentSettingImageControl content_setting_image_control_;
  page_actions::WebUIPageActionControl page_action_control_;


  std::unique_ptr<WebUIPermissionDashboard> permission_dashboard_;
  std::unique_ptr<PermissionDashboardController>
      permission_dashboard_controller_;

  std::unique_ptr<OmniboxController> omnibox_controller_;
  std::unique_ptr<WebUIReadOnlyOmnibox> omnibox_view_;
  std::unique_ptr<OmniboxPopupViewWebUI> omnibox_popup_view_;
  const bool using_full_popup_;
  // The presenter controlling the showing of the AI mode popup.
  std::unique_ptr<OmniboxPopupAimPresenter> omnibox_popup_aim_presenter_;
  std::unique_ptr<OmniboxPopupFileSelector> omnibox_popup_file_selector_;
  base::CallbackListSubscription popup_state_changed_subscription_;

  bool is_initialized_ = false;

  // Keeps track of whether any of our descendant elements has focus.
  // Updated by SetFocusWithin, which is ultimately called via mojo from
  // the HTML side.
  bool focus_within_ = false;

  // Whether to paint AIM button as focused (with focus still on omnibox).
  bool force_aim_button_focus_ring_ = false;

  bool in_popup_state_transition_ = false;

  toolbar_ui_api::IconHandle location_icon_;
  security_state::SecurityLevel last_update_security_level_ =
      security_state::NONE;

  WebUIBubbleReopenSuppressor page_info_reopen_suppressor_;

  std::optional<std::u16string> last_search_keyword_;
  std::optional<bool> last_is_keyword_selected_;
  toolbar_ui_api::IconHandle keyword_icon_;

  base::WeakPtrFactory<WebUILocationBar> weak_ptr_factory_{this};
};

#endif  // CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_WEBUI_LOCATION_BAR_H_
