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

#include "chrome/browser/ui/views/profiles/profile_menu_coordinator.h"

#include "base/check_deref.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/signin_promo_util.h"
#include "chrome/browser/signin/signin_ui_util.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/interaction/browser_elements.h"
#include "chrome/browser/ui/user_education/browser_user_education_interface.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
#include "chrome/browser/ui/views/profiles/avatar_toolbar_button.h"
#include "chrome/browser/ui/views/profiles/incognito_menu_view.h"
#include "chrome/browser/ui/views/profiles/profile_menu_view_base.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "ui/views/bubble/bubble_anchor.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/view_utils.h"

#if !BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/ui/views/profiles/profile_menu_view.h"
#endif  // !BUILDFLAG(IS_CHROMEOS)

ProfileMenuCoordinator::~ProfileMenuCoordinator() {
  // Ensure the ProfileMenuCoordinator does not outlive its associated bubble
  // widget to mitigate the risk of dangling references.
  if (bubble_tracker_ && bubble_tracker_.view()->GetWidget()) {
    bubble_tracker_.view()->GetWidget()->CloseNow();
  }
}

void ProfileMenuCoordinator::Show(bool is_source_accelerator,
                                  bool from_avatar_promo) {
  // TODO(crbug.com/425953501): Update this code.
  auto avatar_toolbar_button = GetAvatarToolbarButton();

  // Do not show avatar bubble if there is no avatar menu button or if the
  // bubble is already showing.
  if (avatar_toolbar_button.IsNull() || IsShowing()) {
    return;
  }

#if BUILDFLAG(ENABLE_DICE_SUPPORT)
  signin::ComputeProfileMenuAvatarButtonPromoInfo(
      *GetProfile(),
      base::BindOnce(&ProfileMenuCoordinator::ShowWithPromoResults,
                     weak_pointer_factory_.GetWeakPtr(), is_source_accelerator,
                     from_avatar_promo));
#else
  ShowWithPromoResults(is_source_accelerator, from_avatar_promo);
#endif
}

void ProfileMenuCoordinator::ShowWithPromoResults(
    bool is_source_accelerator,
    bool from_avatar_promo
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
    ,
    signin::ProfileMenuAvatarButtonPromoInfo promo_info
#endif
) {
  // Results are asynchronous, which can cause the menu to be already shown
  // before receiving them. If this happens, ignore the second request as the
  // menu is already shown.
  if (IsShowing()) {
    return;
  }

  signin_ui_util::RecordProfileMenuViewShown(GetProfile());
  // Close any existing IPH bubble for the profile menu.
  BrowserUserEducationInterface::From(GetBrowser())
      ->NotifyFeaturePromoFeatureUsed(
          feature_engagement::kIPHProfileSwitchFeature,
          FeaturePromoFeatureUsedAction::kClosePromoIfPresent);
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
  BrowserUserEducationInterface::From(GetBrowser())
      ->NotifyFeaturePromoFeatureUsed(
          feature_engagement::kIPHSupervisedUserProfileSigninFeature,
          FeaturePromoFeatureUsedAction::kClosePromoIfPresent);
#endif

  auto avatar_toolbar_button = GetAvatarToolbarButton();
  std::unique_ptr<ProfileMenuViewBase> bubble;
  const bool is_incognito = GetProfile()->IsIncognitoProfile();
  if (is_incognito) {
    bubble = std::make_unique<IncognitoMenuView>(avatar_toolbar_button,
                                                 &browser_.get());
  } else {
#if BUILDFLAG(IS_CHROMEOS)
    // Note: on Ash, only incognito windows have a profile menu.
    NOTREACHED() << "The profile menu is not implemented on Ash.";
#else
    bubble = std::make_unique<ProfileMenuView>(
        avatar_toolbar_button, &browser_.get(), promo_info, from_avatar_promo);
#endif  // BUILDFLAG(IS_CHROMEOS)
  }
  bubble->SetProperty(views::kElementIdentifierKey,
                      kToolbarAvatarBubbleElementId);

  auto* bubble_ptr = bubble.get();
  DCHECK_EQ(nullptr, bubble_tracker_.view());
  bubble_tracker_.SetView(bubble_ptr);

  views::Widget* widget =
      views::BubbleDialogDelegateView::CreateBubble(std::move(bubble));
  bubble_ptr->CreateAXWidgetObserver(widget);
  widget->Show();
  if (is_source_accelerator) {
    bubble_ptr->FocusFirstProfileButton();
  }
}

bool ProfileMenuCoordinator::IsShowing() const {
  return bubble_tracker_.view() != nullptr;
}

ProfileMenuViewBase*
ProfileMenuCoordinator::GetProfileMenuViewBaseForTesting() {
  return IsShowing()
             ? views::AsViewClass<ProfileMenuViewBase>(bubble_tracker_.view())
             : nullptr;
}

BrowserWindowInterface* ProfileMenuCoordinator::GetBrowser() {
  return &browser_.get();
}

Profile* ProfileMenuCoordinator::GetProfile() {
  return &profile_.get();
}

views::BubbleAnchor ProfileMenuCoordinator::GetAvatarToolbarButton() {
  if (auto* avatar_toolbar_button =
          BrowserElements::From(GetBrowser())
              ->GetElement(kToolbarAvatarButtonElementId)) {
    return views::BubbleAnchor(avatar_toolbar_button);
  }

  return views::BubbleAnchor(BrowserView::GetBrowserViewForBrowser(GetBrowser())
                                 ->toolbar()
                                 ->avatar_toolbar_button());
}

ProfileMenuCoordinator::ProfileMenuCoordinator(BrowserWindowInterface* browser,
                                               Profile* profile)
    : browser_(CHECK_DEREF(browser)), profile_(CHECK_DEREF(profile)) {}
