// Copyright 2020 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_WEBID_IDENTITY_DIALOG_CONTROLLER_H_
#define CHROME_BROWSER_UI_WEBID_IDENTITY_DIALOG_CONTROLLER_H_

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

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/actor/actor_keyed_service.h"
#include "chrome/browser/actor/actor_task.h"
#include "chrome/browser/ui/webid/account_selection_view.h"
#include "chrome/browser/webid/proto/fedcm_clickthrough_rate_metadata.pb.h"
#include "components/segmentation_platform/public/result.h"
#include "components/tabs/public/tab_interface.h"
#include "content/public/browser/page_user_data.h"
#include "content/public/browser/webid/identity_request_dialog_controller.h"
#include "third_party/blink/public/mojom/webid/federated_request.mojom-shared.h"
#include "ui/gfx/native_ui_types.h"

namespace content {
class WebContents;

namespace webid {
enum class FederatedLoginResult;
}  // namespace webid
}  // namespace content

namespace optimization_guide {
class OptimizationGuideDecider;
}

namespace segmentation_platform {
class SegmentationPlatformService;
}

using AccountSelectionCallback =
    content::IdentityRequestDialogController::AccountSelectionCallback;
using DismissCallback =
    content::IdentityRequestDialogController::DismissCallback;
using IdentityProviderDataPtr = scoped_refptr<content::IdentityProviderData>;
using IdentityRequestAccountPtr =
    scoped_refptr<content::IdentityRequestAccount>;
using TokenError = content::IdentityCredentialTokenError;

class IdentityDialogControllerBrowserTest;

// The IdentityDialogController controls the views that are used across
// browser-mediated federated sign-in flows.
class IdentityDialogController
    : public content::IdentityRequestDialogController,
      public AccountSelectionView::Delegate {
 public:
  explicit IdentityDialogController(
      content::WebContents* rp_web_contents,
      segmentation_platform::SegmentationPlatformService* service = nullptr,
      optimization_guide::OptimizationGuideDecider* decider = nullptr);

  IdentityDialogController(const IdentityDialogController&) = delete;
  IdentityDialogController& operator=(const IdentityDialogController&) = delete;

  ~IdentityDialogController() override;

  // This enum describes the user action taken when the UI shown uses
  // segmentation platform's UI volume recommendation and is used for
  // histograms. Do not remove or modify existing values, but you may add new
  // values at the end.
  // LINT.IfChange(UserAction)

  enum class UserAction {
    // kSuccess = 0,  // Deprecated.
    kIgnored = 1,
    kClosed = 2,
    kSuccess = 3,
    kMaxValue = kSuccess
  };

  // LINT.ThenChange(//tools/metrics/histograms/metadata/blink/enums.xml:FedCmUserAction)

  // content::IdentityRequestDelegate
  int GetBrandIconMinimumSize(blink::mojom::RpMode rp_mode) override;
  int GetBrandIconIdealSize(blink::mojom::RpMode rp_mode) override;

  // content::IdentityRequestDialogController
  void GetPassiveDialogVolume(GetPassiveDialogVolumeCallback cb) override;
  bool ShowAccountsDialog(
      content::RelyingPartyData rp_data,
      const std::vector<IdentityProviderDataPtr>& identity_provider_data,
      const std::vector<IdentityRequestAccountPtr>& accounts,
      const std::vector<IdentityRequestAccountPtr>& filtered_accounts,
      blink::mojom::RpMode rp_mode,
      AccountSelectionCallback on_selected,
      LoginToIdPCallback on_add_account,
      DismissCallback dismiss_callback,
      AccountsDisplayedCallback accounts_displayed_callback) override;
  bool ShowFailureDialog(
      const content::RelyingPartyData& rp_data,
      const std::string& idp_for_display,
      blink::mojom::RpContext rp_context,
      blink::mojom::RpMode rp_mode,
      const content::IdentityProviderMetadata& idp_metadata,
      const std::vector<IdentityRequestAccountPtr>& filtered_accounts,
      DismissCallback dismiss_callback,
      LoginToIdPCallback login_callback) override;
  bool ShowErrorDialog(const content::RelyingPartyData& rp_data,
                       const std::string& idp_for_display,
                       blink::mojom::RpContext rp_context,
                       blink::mojom::RpMode rp_mode,
                       const content::IdentityProviderMetadata& idp_metadata,
                       const std::optional<TokenError>& error,
                       DismissCallback dismiss_callback,
                       MoreDetailsCallback more_details_callback) override;
  bool ShowLoadingDialog(const content::RelyingPartyData& rp_data,
                         const std::string& idp_for_display,
                         blink::mojom::RpContext rp_context,
                         blink::mojom::RpMode rp_mode,
                         DismissCallback dismiss_callback) override;
  bool ShowVerifyingDialog(
      const content::RelyingPartyData& rp_data,
      const IdentityProviderDataPtr& idp_data,
      const IdentityRequestAccountPtr& account,
      Account::SignInMode sign_in_mode,
      blink::mojom::RpMode rp_mode,
      AccountsDisplayedCallback accounts_displayed_callback) override;

  std::string GetTitle() const override;
  std::optional<std::string> GetSubtitle() const override;

  void ShowUrl(LinkType type, const GURL& url) override;
  // Show a modal dialog that loads content from the IdP in a WebView.
  content::WebContents* ShowModalDialog(
      const GURL& url,
      blink::mojom::RpMode rp_mode,
      DismissCallback dismiss_callback,
      content::IdentityRequestDialogController::ShownModalAsyncCallback
          on_shown_async,
      NativeAppResultCallback native_result_callback) override;
  void CloseModalDialog() override;
  content::WebContents* GetRpWebContents() override;
  void RequestIdPRegistrationPermision(
      const url::Origin& origin,
      base::OnceCallback<void(bool accepted)> callback) override;

  // AccountSelectionView::Delegate:
  void OnAccountSelected(
      const GURL& idp_config_url,
      const std::string& account_id,
      const content::IdentityRequestAccount::LoginState& login_state) override;
  void OnDismiss(DismissReason dismiss_reason) override;
  void OnLoginToIdP(const GURL& idp_config_url,
                    const GURL& idp_login_url) override;
  void OnMoreDetails() override;
  void OnAccountsDisplayed() override;
  void OnNativeAppResult(const std::string& token) override;
  void OnNativeAppLoginFinished() override;
  gfx::NativeView GetNativeView() override;
  content::WebContents* GetWebContents() override;
  content::IdentityRequestDialogController::PassiveDialogVolume
  GetPassiveDialogVolume() const override;

  // Allows setting a mock AccountSelectionView for testing purposes.
  void SetAccountSelectionViewForTesting(
      std::unique_ptr<AccountSelectionView> account_view);

  // Set acting_task_id for testing purposes.
  void SetActingTaskIdForTesting(actor::TaskId task_id);

  // Requests a UI volume recommendation from |segmentation_platform_service_|.
  void RequestUiVolumeRecommendation(
      segmentation_platform::ClassificationResultCallback callback);

  void OnRequestUiVolumeRecommendationResultReceived(
      const segmentation_platform::ClassificationResult&
          ui_volume_recommendation);

  // Records the action the user has taken on the UI shown when a UI volume
  // recommendation from |segmentation_platform_service_| is used.
  void CollectTrainingData(UserAction user_action);

 private:
  friend class IdentityDialogControllerTest;
  friend class IdentityDialogControllerBrowserTest;

  // Attempts to set `account_view_` if it is not already set -- directly on
  // Android, via TabFeatures on desktop.
  bool TrySetAccountView();

  // Gets the clickthrough rate on the RP aggregated across all users.
  webid::FedCmClickthroughRateMetadata GetFedCmClickthroughRateMetadata();

  // Whether to show FedCM UI or not.
  bool ShouldShowFedCmUi();

  void OnActorTaskStateChanged(actor::ActorTask& task);

  void UpdateTaskId(actor::TaskId task_id);

  void DidInvokeShowUi();

  void NotifyEmbedderOfResult(content::webid::FederatedLoginResult result);

  std::unique_ptr<AccountSelectionView> account_view_{nullptr};
  AccountSelectionCallback on_account_selection_;
  DismissCallback on_dismiss_;
  NativeAppResultCallback on_native_result_;
  LoginToIdPCallback on_login_;
  MoreDetailsCallback on_more_details_;
  AccountsDisplayedCallback on_accounts_displayed_;
  raw_ptr<content::WebContents> rp_web_contents_{nullptr};
  blink::mojom::RpMode rp_mode_ = blink::mojom::RpMode::kPassive;
  // Wheter we invoked any show methods. UI may be shown be not invoked in cases
  // such as an active actor task.
  bool did_invoke_show_ui_ = false;
  // Whether we show any FedCM UI or not. Excludes the loading dialog since that
  // one is not something that modifies user state or is actionable by the user.
  bool did_show_ui_ = false;
  std::optional<content::IdentityRequestDialogController::PassiveDialogVolume>
      passive_dialog_volume_;
  GetPassiveDialogVolumeCallback passive_dialog_volume_callback_;

  // Request ID associated with a |GetClassificationResult| call to
  // |segmentation_platform_service_|. This is nullopt when the
  // |GetClassificationResult| call has not returned a result yet.
  std::optional<segmentation_platform::TrainingRequestId> training_request_id_;

  // Service which returns a recommendation for UI volume.
  raw_ptr<segmentation_platform::SegmentationPlatformService>
      segmentation_platform_service_{nullptr};

  // Optimization guide decider for information about URLs that have recently
  // been navigated to. e.g. Aggregated FedCM clickthrough rate.
  raw_ptr<optimization_guide::OptimizationGuideDecider>
      optimization_guide_decider_{nullptr};

  // The ID of the actor task currently acting on the tab, if any.
  actor::TaskId acting_task_id_;
  base::CallbackListSubscription actor_task_state_subscription_;

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

#endif  // CHROME_BROWSER_UI_WEBID_IDENTITY_DIALOG_CONTROLLER_H_
