// Copyright 2026 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/ui/webui/omnibox_everywhere/omnibox_everywhere_ui.h"

#include "base/feature_list.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/autocomplete/aim_eligibility_service_factory.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/contextual_search/contextual_search_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_prefs.h"
#include "chrome/browser/ui/omnibox/omnibox_everywhere_service.h"
#include "chrome/browser/ui/omnibox/omnibox_everywhere_service_factory.h"
#include "chrome/browser/ui/omnibox/omnibox_next_features.h"
#include "chrome/browser/ui/search/most_visited_metrics_logger.h"
#include "chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h"
#include "chrome/browser/ui/webui/cr_components/most_visited/most_visited_pref_observer.h"
#include "chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.h"
#include "chrome/browser/ui/webui/favicon_source.h"
#include "chrome/browser/ui/webui/metrics_reporter/metrics_reporter_service.h"
#include "chrome/browser/ui/webui/new_tab_page/composebox/variations/composebox_fieldtrial.h"
#include "chrome/browser/ui/webui/omnibox_everywhere/composebox_everywhere_handler.h"
#include "chrome/browser/ui/webui/omnibox_everywhere/debug/omnibox_everywhere_debug_page_handler.h"
#include "chrome/browser/ui/webui/omnibox_everywhere/omnibox_everywhere_handler.h"
#include "chrome/browser/ui/webui/plural_string_handler.h"
#include "chrome/browser/ui/webui/sanitized_image/sanitized_image_source.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/omnibox_everywhere_resources.h"
#include "chrome/grit/omnibox_everywhere_resources_map.h"
#include "components/contextual_search/contextual_search_metrics_recorder.h"
#include "components/contextual_search/contextual_search_service.h"
#include "components/favicon_base/favicon_url_parser.h"
#include "components/lens/lens_features.h"
#include "components/omnibox/browser/aim_eligibility_service.h"
#include "components/omnibox/browser/omnibox_pref_names.h"
#include "components/omnibox/common/composebox_features.h"
#include "components/omnibox/common/omnibox_features.h"
#include "components/search/ntp_features.h"
#include "components/strings/grit/components_strings.h"
#include "components/vector_icons/vector_icons.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/image_model.h"
#include "ui/base/mojom/menu_source_type.mojom.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/webui/webui_util.h"

namespace {

enum ScreenshotMenuCommand {
  kScreenshotEntireScreen = 1,
  kScreenshotWindow,
  kScreenshotRegion,
};

// Minimum preferred width for the screenshot Views menu, matching UX specs
// and the previous dropdown implementation (320px).
constexpr int kScreenshotMenuWidth = 320;
bool IsAimEligible(Profile* profile) {
  auto* aim_eligibility_service =
      AimEligibilityServiceFactory::GetForProfile(profile);
  return aim_eligibility_service && aim_eligibility_service->IsAimEligible();
}

bool IsFuseboxEligible(Profile* profile) {
  return IsAimEligible(profile) &&
         AimEligibilityServiceFactory::GetForProfile(profile)
             ->IsFuseboxEligible();
}

void AddMostVisitedSourceStrings(content::WebUIDataSource* source,
                                 Profile* profile) {
  source->AddBoolean("omniboxEverywhereMostVisitedEnabled",
                     omnibox::kOmniboxEverywhereMostVisitedParam.Get());
  source->AddBoolean(
      "omniboxEverywhereShowShortcuts",
      omnibox_everywhere::prefs::IsOmniboxEverywhereShortcutsVisible(
          profile, g_browser_process->local_state()));

  static constexpr webui::LocalizedString kMostVisitedStrings[] = {
      {"addLinkTitle", IDS_NTP_CUSTOM_LINKS_ADD_SHORTCUT_TITLE},
      {"editLinkTitle", IDS_NTP_CUSTOM_LINKS_EDIT_SHORTCUT},
      {"viewLinkTitle", IDS_NTP_CUSTOM_LINKS_SHORTCUT_DETAILS_TITLE},
      {"invalidUrl", IDS_NTP_CUSTOM_LINKS_INVALID_URL},
      {"linkAddedMsg", IDS_NTP_CONFIRM_MSG_SHORTCUT_ADDED},
      {"linkCancel", IDS_NTP_CUSTOM_LINKS_CANCEL},
      {"linkCantCreate", IDS_NTP_CUSTOM_LINKS_CANT_CREATE},
      {"linkCantEdit", IDS_NTP_CUSTOM_LINKS_CANT_EDIT},
      {"viewLink", IDS_NTP_CUSTOM_LINKS_DETAILS},
      {"linkDone", IDS_NTP_CUSTOM_LINKS_DONE},
      {"linkEditedMsg", IDS_NTP_CONFIRM_MSG_SHORTCUT_EDITED},
      {"linkRemove", IDS_NTP_CUSTOM_LINKS_REMOVE},
      {"linkRemoveA11y", IDS_NTP_MOST_VISITED_SITES_REMOVE},
      {"linkRemovedMsg", IDS_NTP_CONFIRM_MSG_SHORTCUT_REMOVED},
      {"shortcutMoreActions", IDS_NTP_CUSTOM_LINKS_MORE_ACTIONS},
      {"enterpriseShortcutSubtitle", IDS_NTP_ENTERPRISE_SHORTCUT_SUBTITLE},
      {"nameField", IDS_NTP_CUSTOM_LINKS_NAME},
      {"restoreDefaultLinks", IDS_NTP_CONFIRM_MSG_RESTORE_DEFAULTS},
      {"restoreDefaultEnterpriseShortcuts",
       IDS_NTP_CONFIRM_MSG_RESTORE_ENTERPRISE_DEFAULTS},
      {"restoreThumbnailsShort", IDS_NEW_TAB_RESTORE_THUMBNAILS_SHORT_LINK},
      {"shortcutAlreadyExists", IDS_NTP_CUSTOM_LINKS_ALREADY_EXISTS},
      {"urlField", IDS_NTP_CUSTOM_LINKS_URL},
      {"showMore", IDS_NTP_SHOW_MORE_BUTTON_LABEL},
      {"showLess", IDS_NTP_SHOW_LESS_BUTTON_LABEL},
      {"shortcutsInactivityRemovalMsg",
       IDS_NTP_MOST_VISITED_SHORTCUTS_INACTIVITY_REMOVAL},
      {"moduleInactivityRemovalMsg", IDS_NTP_MODULE_INACTIVITY_REMOVAL},
      {"modulesInactivityRemovalMsg", IDS_NTP_MODULES_INACTIVITY_REMOVAL},
      {"undo", IDS_NEW_TAB_UNDO_THUMBNAIL_REMOVE},
  };
  source->AddLocalizedStrings(kMostVisitedStrings);

  ui::Accelerator undo_accelerator(ui::VKEY_Z, ui::EF_PLATFORM_ACCELERATOR);
  source->AddString("undoDescription", l10n_util::GetStringFUTF16(
                                           IDS_UNDO_DESCRIPTION,
                                           undo_accelerator.GetShortcutText()));

  source->AddInteger("maxTilesInCollapsedState",
                     ntp_features::GetMaxTilesInCollapsedState());
  source->AddInteger("maxShortcutsInExpandedState",
                     ntp_features::GetMaxShortcutsInExpandedState());
  source->AddInteger("maxMostVisitedTilesInExpandedState",
                     ntp_features::GetMaxMostVisitedTilesInExpandedState());
  source->AddInteger("maxEnterpriseShortcuts",
                     ntp_features::GetMaxEnterpriseShortcuts());
  source->AddInteger("preconnectStartTimeThreshold", 0);
  source->AddInteger("prefetchStartTimeThreshold", 0);
  source->AddBoolean("prefetchTriggerEnabled", false);
  source->AddBoolean("prerenderOnPressEnabled", false);
  source->AddBoolean("mostVisitedHighDpiFaviconsEnabled", true);
}

}  // namespace

bool OmniboxEverywhereUIConfig::IsWebUIEnabled(
    content::BrowserContext* browser_context) {
  Profile* profile = Profile::FromBrowserContext(browser_context);
  return omnibox::IsOmniboxEverywhereEnabled(profile);
}

bool OmniboxEverywhereUIConfig::ShouldCrashOnJavascriptErrorInDevelopmentBuild()
    const {
  return true;
}

OmniboxEverywhereUI::OmniboxEverywhereUI(content::WebUI* web_ui)
    : TopChromeWebUIController(web_ui,
                               /*enable_chrome_send=*/true,
                               /*enable_chrome_histograms=*/true),
      profile_(Profile::FromWebUI(web_ui)) {
  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
      profile_, chrome::kChromeUIOmniboxEverywhereHost);

  webui::SetupWebUIDataSource(source, kOmniboxEverywhereResources,
                              IDR_OMNIBOX_EVERYWHERE_OMNIBOX_EVERYWHERE_HTML);
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::MediaSrc,
      "media-src blob: data: 'self';");
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::ImgSrc,
      "img-src 'self' chrome://resources chrome://theme chrome://favicon/ "
      "chrome://favicon2/ chrome://image/ data: blob:;");

  std::string profile_avatar_url =
      "chrome://theme/IDR_PROFILE_AVATAR_PLACEHOLDER_LARGE";
  if (g_browser_process && g_browser_process->profile_manager()) {
    ProfileAttributesEntry* entry =
        g_browser_process->profile_manager()
            ->GetProfileAttributesStorage()
            .GetProfileAttributesWithPath(profile_->GetPath());
    if (entry) {
      gfx::Image icon =
          profiles::GetSizedAvatarIcon(entry->GetAvatarIcon(), 48, 48);
      profile_avatar_url = webui::GetBitmapDataUrl(icon.AsBitmap());

      std::u16string gaia_name = entry->GetGAIAName();
      std::u16string profile_name = entry->GetName();
      std::u16string display_name = profile_name;
      if (!gaia_name.empty() && gaia_name != profile_name) {
        display_name = gaia_name + u" • " + profile_name;
      }
      source->AddString("profileName", base::UTF16ToUTF8(display_name));
      source->AddString("profileEmail",
                        base::UTF16ToUTF8(entry->GetUserName()));
    } else {
      source->AddString("profileName", "");
      source->AddString("profileEmail", "");
    }
  }
  source->AddString("profileAvatarUrl", profile_avatar_url);
  source->AddBoolean("omniboxEverywhereProfilePickerEnabled",
                     omnibox::kOmniboxEverywhereProfilePickerParam.Get());
  static constexpr webui::LocalizedString kStrings[] = {
      {"loomniboxFreAcceptHotkey", IDS_LOOMNIBOX_FRE_ACCEPT_HOTKEY},
      {"loomniboxFreCloseButtonAria", IDS_LOOMNIBOX_FRE_CLOSE_BUTTON_ARIA},
      {"loomniboxFreEditOwn", IDS_LOOMNIBOX_FRE_KEYBOARD_OPTION_EDIT_OWN},
      {"loomniboxFreKeyboardBadgeOption",
       IDS_LOOMNIBOX_FRE_KEYBOARD_BADGE_OPTION},
      {"loomniboxFreKeyboardBadgeSpace",
       IDS_LOOMNIBOX_FRE_KEYBOARD_BADGE_SPACE},
      {"loomniboxFreKeyboardPrimary", IDS_LOOMNIBOX_FRE_KEYBOARD_PRIMARY},
      {"loomniboxFreLensPrimary", IDS_LOOMNIBOX_FRE_LENS_PRIMARY},
      {"loomniboxFreLensSecondary", IDS_LOOMNIBOX_FRE_LENS_SECONDARY},
      {"loomniboxFreOr", IDS_LOOMNIBOX_FRE_OR},
      {"loomniboxFreTitle", IDS_LOOMNIBOX_FRE_TITLE},
      {"profileButtonLabel", IDS_OVERFLOW_MENU_ITEM_TEXT_PROFILE},
      {"screenshotEntireScreenLabel", IDS_OMNIBOX_EVERYWHERE_ENTIRE_SCREEN},
      {"screenshotRegionLabel", IDS_OMNIBOX_EVERYWHERE_REGION},
      {"screenshotWindowLabel", IDS_OMNIBOX_EVERYWHERE_WINDOW},
      {"searchBoxHintAskOrType", IDS_NTP_SEARCH_BOX_PLACEHOLDER_ASK_OR_TYPE},
      {"shareScreenshotLabel", IDS_OMNIBOX_EVERYWHERE_SHARE_SCREENSHOT},
  };
  source->AddLocalizedStrings(kStrings);

  // Sanitized image and favicon source initialization
  content::URLDataSource::Add(profile_,
                              std::make_unique<SanitizedImageSource>(profile_));
  content::URLDataSource::Add(
      profile_, std::make_unique<FaviconSource>(
                    profile_, chrome::FaviconUrlFormat::kFavicon2));

  bool session_allows_drag_and_drop = false;
  if (auto* session_handle = GetOrCreateContextualSessionHandle()) {
    session_allows_drag_and_drop =
        session_handle->CheckSearchContentSharingSettings(profile_->GetPrefs());
  }

  // Configure WebUIDataSource dictionary
  source->AddLocalizedStrings(SearchboxHandler::GetWebUIDataSourceDict(
      profile_,
      {.enable_voice_search = true,
       .enable_lens_search = true,
       .session_allows_drag_and_drop = session_allows_drag_and_drop}));

  source->AddBoolean("isTopChromeSearchbox", false);
  source->AddBoolean(
      "omniboxPopupDebugEnabled",
      base::FeatureList::IsEnabled(omnibox::kWebUIOmniboxPopupDebug));

  source->AddBoolean("reportMetrics", true);
  source->AddString("charTypedToPaintMetricName",
                    "Omnibox.WebUI.CharTypedToRepaintLatency.ToPaint");
  source->AddString(
      "resultChangedToPaintMetricName",
      "Omnibox.Popup.WebUI.ResultChangedToRepaintLatency.ToPaint");

  // Add composebox data.
  auto composebox_config = omnibox::FeatureConfig::Get().config.composebox();
  const std::string attachment_mime_types =
      composebox_config.attachment_upload().mime_types_allowed();
  source->AddString("composeboxAttachmentFileTypes", attachment_mime_types);
  source->AddInteger("composeboxFileMaxSize",
                     composebox_config.attachment_upload().max_size_bytes());
  const std::string image_mime_types =
      composebox_config.image_upload().mime_types_allowed();
  source->AddString("composeboxImageFileTypes", image_mime_types);
  source->AddBoolean("lensSendRawFileMediaTypesEnabled",
                     lens::features::IsLensSendRawFileMediaTypesEnabled());
  source->AddBoolean(
      "caretAnimationEnabled",
      base::FeatureList::IsEnabled(omnibox::kOmniboxAnimatedCaret));
  source->AddBoolean("composeboxContextMenuEnableMultiTabSelection",
                     omnibox::kContextMenuEnableMultiTabSelection.Get());
  source->AddBoolean("composeboxShowContextMenu",
                     omnibox::kShowContextMenu.Get());
  source->AddBoolean(
      "composeboxShowContextMenuDescription",
      omnibox::kShowContextMenuDescription.Get() &&
          omnibox::kWebUIOmniboxAimPopupAddContextButtonVariantParam.Get() !=
              omnibox::AddContextButtonVariant::kInline);
  source->AddBoolean("composeboxShowContextMenuTabPreviews",
                     omnibox::kShowContextMenuTabPreviews.Get());
  source->AddBoolean("composeboxShowImageSuggest",
                     omnibox::kShowComposeboxImageSuggestions.Get());

  AddMostVisitedSourceStrings(source, profile_);

  source->AddBoolean(
      "searchboxShowComposeEntrypoint",
      IsAimEligible(profile_) &&
          profile_->GetPrefs()->GetBoolean(omnibox::kShowAiModeOmniboxButton));
  source->AddBoolean("isFuseboxEnabled", IsFuseboxEligible(profile_));
  source->AddBoolean("ntpRealboxDynamicAiModeButton",
                     IsFuseboxEligible(profile_) &&
                         base::FeatureList::IsEnabled(
                             ntp_realbox::kNtpRealboxDynamicAiModeButton));
  source->AddBoolean("composeboxShowTypedSuggest",
                     omnibox::kShowComposeboxTypedSuggest.Get());
  source->AddBoolean("composeboxShowZps", omnibox::kShowComposeboxZps.Get());
  source->AddBoolean("composeboxSmartComposeEnabled",
                     omnibox::kShowSmartCompose.Get());
  source->AddBoolean("webuiOmniboxSimplificationEnabled",
                     base::FeatureList::IsEnabled(
                         omnibox::internal::kWebUIOmniboxSimplification));
  source->AddBoolean(
      "contextManagementInComposeboxEnabled",
      base::FeatureList::IsEnabled(omnibox::kContextManagementInComposebox) &&
          base::FeatureList::IsEnabled(omnibox::kContextManagementInOmnibox));
  source->AddBoolean(
      "tabFaviconChipsToCoinsEnabled",
      base::FeatureList::IsEnabled(omnibox::kContextManagementInComposebox) &&
          base::FeatureList::IsEnabled(omnibox::kContextManagementInOmnibox) &&
          base::FeatureList::IsEnabled(omnibox::kTabFaviconChipsToCoins));
  source->AddBoolean(
      "composeboxSkillsEnabled",
      base::FeatureList::IsEnabled(omnibox::kComposeboxSkillsOmniboxEverywhere));

  source->AddString("searchboxLayoutMode", "TallBottomContext");
  source->AddString(
      "composeboxSource",
      contextual_search::ContextualSearchMetricsRecorder::
          ContextualSearchSourceToString(
              contextual_search::ContextualSearchSource::kOmniboxEverywhere));
  source->AddBoolean("caretColorAnimationDisabled",
                     base::FeatureList::IsEnabled(
                         omnibox::kWebUIOmniboxDisableCaretColorAnimation));
  source->AddBoolean("composeboxAnimationDisabled",
                     base::FeatureList::IsEnabled(
                         omnibox::kWebUIOmniboxAimPopupDisableAnimation));
  // Disable the energy effect for the searchbox in Omnibox Everywhere so the
  // AIM compose button renders the outer conic rainbow glow animation instead
  // of the energy effect. The composebox explicitly enables energy effect for
  // its own expanding glow animation.
  source->AddBoolean("energyEffectEnabled", false);
  source->AddBoolean("energyEffectAnimationEnabled", false);
  source->AddBoolean("composeboxEnergyEffectAnimationEnabled", true);
  source->AddBoolean("contextButtonShapeIsOblong",
                     omnibox::kContextButtonShapeIsOblong.Get());

  // Add a handler to provide pluralized strings.
  auto plural_string_handler = std::make_unique<PluralStringHandler>();
  plural_string_handler->AddLocalizedString("sharingTabs",
                                            IDS_COMPOSE_SHARING_TABS);
  web_ui->AddMessageHandler(std::move(plural_string_handler));
}

OmniboxEverywhereUI::~OmniboxEverywhereUI() = default;

void OmniboxEverywhereUI::BindInterface(
    mojo::PendingReceiver<composebox::mojom::PageHandlerFactory> receiver) {
  if (!omnibox::IsOmniboxEverywhereEnabled(profile_)) {
    return;
  }
  if (composebox_page_factory_receiver_.is_bound()) {
    composebox_page_factory_receiver_.reset();
  }
  composebox_page_factory_receiver_.Bind(std::move(receiver));
}

void OmniboxEverywhereUI::CreatePageHandler(
    mojo::PendingReceiver<composebox::mojom::PageHandler> pending_page_handler,
    mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page,
    mojo::PendingReceiver<searchbox::mojom::PageHandler>
        pending_searchbox_handler) {
  // TODO(crbug.com/526629960): Create new EverywhereComposeboxHandler or allow
  // the ComposeboxHandler to parameterize the OmniboxClient.
  composebox_handler_ = std::make_unique<ComposeboxEverywhereHandler>(
      std::move(pending_page_handler), std::move(pending_searchbox_handler),
      std::move(pending_searchbox_page), profile_, web_ui()->GetWebContents(),
      base::BindRepeating(
          &OmniboxEverywhereUI::GetOrCreateContextualSessionHandle,
          base::Unretained(this)),
      base::BindRepeating(&OmniboxEverywhereUI::ClearContextualSessionHandle,
                          base::Unretained(this)),
      this);
}

void OmniboxEverywhereUI::BindInterface(
    content::RenderFrameHost* host,
    mojo::PendingReceiver<searchbox::mojom::PageHandlerFactory>
        pending_page_handler) {
  if (!omnibox::IsOmniboxEverywhereEnabled(profile_)) {
    return;
  }
  if (searchbox_page_factory_receiver_.is_bound()) {
    searchbox_page_factory_receiver_.reset();
  }
  searchbox_page_factory_receiver_.Bind(std::move(pending_page_handler));
}

void OmniboxEverywhereUI::CreatePageHandler(
    mojo::PendingRemote<searchbox::mojom::Page> page,
    mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler) {
  auto* service = OmniboxEverywhereServiceFactory::GetForProfile(profile_);
  CHECK(service);

  MetricsReporterService* metrics_reporter_service =
      MetricsReporterService::GetFromWebContents(web_ui()->GetWebContents());
  omnibox_handler_ = std::make_unique<OmniboxEverywhereHandler>(
      std::move(pending_page_handler), std::move(page),
      metrics_reporter_service->metrics_reporter(), web_ui(), service,
      base::BindRepeating(
          &OmniboxEverywhereUI::GetOrCreateContextualSessionHandle,
          base::Unretained(this)),
      this);
}

void OmniboxEverywhereUI::OnScreensharePickerOpened() {
  if (auto* service =
          OmniboxEverywhereServiceFactory::GetForProfile(profile_)) {
    service->OnScreensharePickerOpened();
  }
}

void OmniboxEverywhereUI::OnScreensharePickerClosed() {
  if (auto* service =
          OmniboxEverywhereServiceFactory::GetForProfile(profile_)) {
    service->OnScreensharePickerClosed();
  }
}

void OmniboxEverywhereUI::ShowRegionSelectOverlay(
    const SkBitmap& screenshot,
    RegionSelectedCallback callback) {
  if (auto* service =
          OmniboxEverywhereServiceFactory::GetForProfile(profile_)) {
    service->ShowRegionSelectOverlay(screenshot, std::move(callback));
    return;
  }
  std::move(callback).Run(SkBitmap());
}

void OmniboxEverywhereUI::BindInterface(
    mojo::PendingReceiver<omnibox_everywhere_debug::mojom::PageHandlerFactory>
        receiver) {
  if (debug_page_factory_receiver_.is_bound()) {
    debug_page_factory_receiver_.reset();
  }
  debug_page_factory_receiver_.Bind(std::move(receiver));
}

void OmniboxEverywhereUI::CreatePageHandler(
    mojo::PendingRemote<omnibox_everywhere_debug::mojom::Page> page,
    mojo::PendingReceiver<omnibox_everywhere_debug::mojom::PageHandler>
        handler) {
  debug_page_handler_ = std::make_unique<
      omnibox_everywhere_debug::OmniboxEverywhereDebugPageHandler>(
      profile_, std::move(page), std::move(handler));
}

void OmniboxEverywhereUI::BindInterface(
    mojo::PendingReceiver<most_visited::mojom::MostVisitedPageHandlerFactory>
        receiver) {
  if (!base::FeatureList::IsEnabled(omnibox::kOmniboxEverywhere)) {
    return;
  }
  if (most_visited_page_factory_receiver_.is_bound()) {
    most_visited_page_factory_receiver_.reset();
  }
  most_visited_page_factory_receiver_.Bind(std::move(receiver));
}

void OmniboxEverywhereUI::CreatePageHandler(
    mojo::PendingRemote<most_visited::mojom::MostVisitedPage> pending_page,
    mojo::PendingReceiver<most_visited::mojom::MostVisitedPageHandler>
        pending_page_handler) {
  most_visited_handler_ = std::make_unique<MostVisitedHandler>(
      std::move(pending_page_handler), std::move(pending_page), profile_,
      web_ui()->GetWebContents(),
      std::make_unique<MostVisitedMetricsLogger>("Omnibox"));
  most_visited_pref_observer_ = std::make_unique<MostVisitedPrefObserver>(
      profile_, most_visited_handler_.get());
}

contextual_search::ContextualSearchSessionHandle*
OmniboxEverywhereUI::GetOrCreateContextualSessionHandle() {
  if (!shared_session_handle_) {
    auto* contextual_search_service =
        ContextualSearchServiceFactory::GetForProfile(profile_);
    if (contextual_search_service) {
      shared_session_handle_ = contextual_search_service->CreateSession(
          omnibox::CreateQueryControllerConfigParams(),
          contextual_search::ContextualSearchSource::kOmniboxEverywhere,
          lens::LensOverlayInvocationSource::kOmniboxEverywhereComposebox);
      shared_session_handle_->CheckSearchContentSharingSettings(
          profile_->GetPrefs());
    }
  }
  return shared_session_handle_.get();
}

void OmniboxEverywhereUI::ClearContextualSessionHandle() {
  shared_session_handle_.reset();
}

// Shows a native Views menu rather than a WebUI <cr-action-menu> so that the
// menu:
// 1. Is not clipped by the Omnibox Everywhere WebUI popup widget bounds.
// 2. Can intelligently flip and position across monitor/screen boundaries.
// 3. Matches native TopChrome menu styling, accessibility, and keyboard
//    traversal.
void OmniboxEverywhereUI::ShowScreenshotMenu(
    const gfx::Rect& anchor_rect,
    base::WeakPtr<ContextualSearchboxHandler> source_handler) {
  if (screenshot_menu_runner_ && screenshot_menu_runner_->IsRunning()) {
    if (source_handler) {
      source_handler->OnScreenshotMenuClosed();
    }
    return;
  }
  content::WebContents* web_contents = web_ui()->GetWebContents();
  if (!web_contents) {
    if (source_handler) {
      source_handler->OnScreenshotMenuClosed();
    }
    return;
  }
  views::Widget* widget = views::Widget::GetWidgetForNativeWindow(
      web_contents->GetTopLevelNativeWindow());
  if (!widget || !widget->GetContentsView()) {
    if (source_handler) {
      source_handler->OnScreenshotMenuClosed();
    }
    return;
  }

  active_screenshot_handler_ = std::move(source_handler);

  screenshot_menu_model_ = std::make_unique<ui::SimpleMenuModel>(this);
  screenshot_menu_model_->AddTitle(
      l10n_util::GetStringUTF16(IDS_OMNIBOX_EVERYWHERE_SHARE_SCREENSHOT));
  screenshot_menu_model_->AddItemWithIcon(
      kScreenshotEntireScreen,
      l10n_util::GetStringUTF16(IDS_OMNIBOX_EVERYWHERE_ENTIRE_SCREEN),
      ui::ImageModel::FromVectorIcon(vector_icons::kScreenShareIcon,
                                     ui::kColorMenuIcon, 16));
  screenshot_menu_model_->AddItemWithIcon(
      kScreenshotWindow,
      l10n_util::GetStringUTF16(IDS_OMNIBOX_EVERYWHERE_WINDOW),
      ui::ImageModel::FromVectorIcon(kDesktopWindowsIcon, ui::kColorMenuIcon,
                                     16));
  screenshot_menu_model_->AddItemWithIcon(
      kScreenshotRegion,
      l10n_util::GetStringUTF16(IDS_OMNIBOX_EVERYWHERE_REGION),
      ui::ImageModel::FromVectorIcon(kScreenshotRegionIcon, ui::kColorMenuIcon,
                                     16));

  menu_model_adapter_ = std::make_unique<views::MenuModelAdapter>(
      screenshot_menu_model_.get(),
      base::BindRepeating(&OmniboxEverywhereUI::OnScreenshotMenuClosed,
                          weak_factory_.GetWeakPtr()));
  std::unique_ptr<views::MenuItemView> menu = menu_model_adapter_->CreateMenu();
  if (menu && menu->HasSubmenu()) {
    menu->GetSubmenu()->set_minimum_preferred_width(kScreenshotMenuWidth);
  }

  screenshot_menu_runner_ = std::make_unique<views::MenuRunner>(
      std::move(menu),
      views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU);

  gfx::Rect screen_rect =
      anchor_rect + web_contents->GetContainerBounds().OffsetFromOrigin();

  screenshot_menu_runner_->RunMenuAt(widget, nullptr, screen_rect,
                                     views::MenuAnchorPosition::kTopLeft,
                                     ui::mojom::MenuSourceType::kNone);
}

void OmniboxEverywhereUI::OnScreenshotMenuClosed() {
  if (active_screenshot_handler_) {
    active_screenshot_handler_->OnScreenshotMenuClosed();
  }
}

void OmniboxEverywhereUI::ExecuteCommand(int command_id, int event_flags) {
  if (!active_screenshot_handler_) {
    return;
  }
  auto handler = std::move(active_screenshot_handler_);
  switch (command_id) {
    case kScreenshotEntireScreen:
      handler->StartScreenshare(
          /*prefer_entire_screen=*/true, base::DoNothing());
      break;
    case kScreenshotWindow:
      handler->StartScreenshare(
          /*prefer_entire_screen=*/false, base::DoNothing());
      break;
    case kScreenshotRegion:
      handler->CaptureRegionScreenshot(base::DoNothing());
      break;
  }
}

bool OmniboxEverywhereUI::IsCommandIdChecked(int command_id) const {
  return false;
}

bool OmniboxEverywhereUI::IsCommandIdEnabled(int command_id) const {
  return true;
}

bool OmniboxEverywhereUI::IsCommandIdVisible(int command_id) const {
  return true;
}

WEB_UI_CONTROLLER_TYPE_IMPL(OmniboxEverywhereUI)
