// Copyright 2018 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/signin/turn_sync_on_helper_delegate_impl.h"

#include <optional>

#include "base/check.h"
#include "base/functional/bind.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/notreached.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/new_tab_page/chrome_colors/selected_colors_info.h"
#include "chrome/browser/policy/chrome_browser_policy_connector.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_window.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/browser_window/public/create_browser_window.h"
#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/profiles/profile_colors_util.h"
#include "chrome/browser/ui/signin/signin_view_controller.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h"
#include "chrome/browser/ui/webui/signin/signin_ui_error.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/url_constants.h"
#include "components/policy/core/browser/signin/profile_separation_policies.h"
#include "components/policy/core/browser/signin/user_cloud_signin_restriction_policy_fetcher.h"
#include "components/policy/core/common/policy_utils.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/base_window.h"

namespace {

// If the |browser| argument is non-null, returns the pointer directly.
// Otherwise creates a new browser for the profile, adds an empty tab and makes
// sure the browser is visible.
BrowserWindowInterface* EnsureBrowser(BrowserWindowInterface* browser,
                                      Profile* profile) {
  if (!browser) {
    // The user just created a new profile or has closed the browser that
    // we used previously. Grab an existing browser for the profile or else
    // create a new one.
    browser = ProfileBrowserCollection::GetForProfile(profile)
                  ->GetLastActiveBrowser();
    if (!browser) {
      browser = CreateBrowserWindow(BrowserWindowCreateParams(profile, true));
      chrome::AddTabAt(browser, GURL(), -1, true);
    }
    browser->GetWindow()->Show();
  }
  return browser;
}

// Converts SigninEmailConfirmationDialog::Action to
// TurnSyncOnHelper::SigninChoice and invokes |callback| on it.
void OnEmailConfirmation(signin::SigninChoiceCallback callback,
                         SigninEmailConfirmationDialog::Action action) {
  DCHECK(callback) << "This function should be called only once.";
  switch (action) {
    case SigninEmailConfirmationDialog::START_SYNC:
      std::move(callback).Run(signin::SIGNIN_CHOICE_CONTINUE);
      return;
    case SigninEmailConfirmationDialog::CREATE_NEW_USER:
      std::move(callback).Run(signin::SIGNIN_CHOICE_NEW_PROFILE);
      return;
    case SigninEmailConfirmationDialog::CLOSE:
      std::move(callback).Run(signin::SIGNIN_CHOICE_CANCEL);
      return;
  }
  NOTREACHED();
}

}  // namespace

TurnSyncOnHelperDelegateImpl::TurnSyncOnHelperDelegateImpl(
    BrowserWindowInterface* browser,
    bool is_sync_promo,
    bool user_already_signed_in)
    : browser_(browser),
      profile_(browser_->GetProfile()),
      is_sync_promo_(is_sync_promo),
      user_already_signed_in_(user_already_signed_in) {
  DCHECK(browser);
  DCHECK(profile_);
  browser_close_subscription_ = browser_->RegisterBrowserDidClose(
      base::BindRepeating(&TurnSyncOnHelperDelegateImpl::OnBrowserDidClose,
                          base::Unretained(this)));
}

TurnSyncOnHelperDelegateImpl::~TurnSyncOnHelperDelegateImpl() = default;

bool TurnSyncOnHelperDelegateImpl::IsProfileCreationRequiredByPolicy() const {
  return profile_creation_required_by_policy_;
}

void TurnSyncOnHelperDelegateImpl::ShowLoginError(const SigninUIError& error) {
  CHECK(!error.IsOk());
  if (is_sync_promo_ &&
      error.type() ==
          SigninUIError::Type::kAccountAlreadyUsedByAnotherProfile) {
    // Do not show Sync-related errors if it's a Sync promo.
    return;
  }

  TurnSyncOnHelper::Delegate::ShowLoginErrorForBrowser(error, browser_);
}

void TurnSyncOnHelperDelegateImpl::
    ShouldEnterpriseConfirmationPromptForNewProfile(
        Profile* profile,
        base::OnceCallback<void(bool)> callback) {
    std::move(callback).Run(/*prompt_for_new_profile=*/true);
}

void TurnSyncOnHelperDelegateImpl::ShowEnterpriseAccountConfirmation(
    const AccountInfo& account_info,
    signin::SigninChoiceCallback callback) {
  browser_ = EnsureBrowser(browser_, profile_);
  account_level_signin_restriction_policy_fetcher_ =
      std::make_unique<policy::UserCloudSigninRestrictionPolicyFetcher>(
          g_browser_process->browser_policy_connector(),
          g_browser_process->system_network_context_manager()
              ->GetSharedURLLoaderFactory());
  // Checking whether to show the prompt for a new profile is sometimes
  // asynchronous.
  ShouldEnterpriseConfirmationPromptForNewProfile(
      profile_,
      base::BindOnce(&TurnSyncOnHelperDelegateImpl::OnProfileCheckComplete,
                     weak_ptr_factory_.GetWeakPtr(), account_info,
                     std::move(callback)));
}

void TurnSyncOnHelperDelegateImpl::ShowSyncConfirmation(
    base::OnceCallback<void(LoginUIService::SyncConfirmationUIClosedResult)>
        callback) {
  DCHECK(callback);
  sync_confirmation_callback_ = std::move(callback);
  scoped_login_ui_service_observation_.Observe(
      LoginUIServiceFactory::GetForProfile(profile_));
  browser_ = EnsureBrowser(browser_, profile_);
  browser_->GetFeatures()
      .signin_view_controller()
      ->ShowModalSyncConfirmationDialog(
          /*is_signin_intercept=*/false, is_sync_promo_);
}

bool TurnSyncOnHelperDelegateImpl::
    ShouldAbortBeforeShowSyncDisabledConfirmation() {
  // Do not show the sync disabled confirmation if it's a Sync promo.
  return is_sync_promo_;
}

void TurnSyncOnHelperDelegateImpl::ShowSyncDisabledConfirmation(
    bool is_managed_account,
    base::OnceCallback<void(LoginUIService::SyncConfirmationUIClosedResult)>
        callback) {
  // This is handled by the same UI element as the normal sync confirmation.
  ShowSyncConfirmation(std::move(callback));
}

void TurnSyncOnHelperDelegateImpl::ShowMergeSyncDataConfirmation(
    const std::string& previous_email,
    const std::string& new_email,
    signin::SigninChoiceCallback callback) {
  DCHECK(callback);
  browser_ = EnsureBrowser(browser_, profile_);
  browser_->GetFeatures()
      .signin_view_controller()
      ->ShowModalSigninEmailConfirmationDialog(
          previous_email, new_email,
          base::BindOnce(&OnEmailConfirmation, std::move(callback)));
}

void TurnSyncOnHelperDelegateImpl::ShowSyncSettings() {
  browser_ = EnsureBrowser(browser_, profile_);
  chrome::ShowSettingsSubPage(browser_, chrome::kSyncSetupSubPage);
}

void TurnSyncOnHelperDelegateImpl::SwitchToProfile(Profile* new_profile) {
  profile_ = new_profile;
  browser_ = nullptr;
}

void TurnSyncOnHelperDelegateImpl::OnSyncConfirmationUIClosed(
    LoginUIService::SyncConfirmationUIClosedResult result) {
  scoped_login_ui_service_observation_.Reset();
  DCHECK(sync_confirmation_callback_);
  // Treat closing the ui as an implicit ABORT_SYNC action.
  if (result == LoginUIService::UI_CLOSED) {
    result = LoginUIService::ABORT_SYNC;
  }
  if (browser_) {
    browser_->GetFeatures().signin_view_controller()->CloseModalSignin();
  }
  std::move(sync_confirmation_callback_).Run(result);
}

void TurnSyncOnHelperDelegateImpl::OnBrowserDidClose(
    BrowserWindowInterface* browser) {
  if (browser_ == browser) {
    browser_ = nullptr;
  }
}

void TurnSyncOnHelperDelegateImpl::OnProfileSigninRestrictionsFetched(
    const AccountInfo& account_info,
    signin::SigninChoiceCallback callback,
    policy::ProfileSeparationPolicies profile_separation_policies) {
  if (!browser_) {
    std::move(callback).Run(signin::SIGNIN_CHOICE_CANCEL);
    return;
  }
  profile_creation_required_by_policy_ =
      signin_util::IsProfileSeparationEnforcedByProfile(
          browser_->GetProfile(), account_info.GetEmail()) ||
      signin_util::IsProfileSeparationEnforcedByPolicies(
          profile_separation_policies);
  bool show_link_data_option = signin_util::
      ProfileSeparationAllowsKeepingUnmanagedBrowsingDataInManagedProfile(
          browser_->GetProfile(), profile_separation_policies);
  browser_->GetFeatures()
      .signin_view_controller()
      ->ShowModalManagedUserNoticeDialog(
          std::make_unique<signin::EnterpriseProfileCreationDialogParams>(
              account_info, /*is_oidc_account=*/false,
              /*user_already_signed_in=*/user_already_signed_in_,
              profile_creation_required_by_policy_, show_link_data_option,
              std::move(callback),
              base::BindOnce(&SigninViewController::CloseModalSignin,
                             browser_->GetFeatures()
                                 .signin_view_controller()
                                 ->AsWeakPtr())));
}

void TurnSyncOnHelperDelegateImpl::OnProfileCheckComplete(
    const AccountInfo& account_info,
    signin::SigninChoiceCallback callback,
    bool prompt_for_new_profile) {
  if (!browser_) {
    std::move(callback).Run(signin::SIGNIN_CHOICE_CANCEL);
    return;
  }

  if (prompt_for_new_profile) {
    account_level_signin_restriction_policy_fetcher_
        ->GetManagedAccountsSigninRestriction(
            IdentityManagerFactory::GetForProfile(browser_->GetProfile()),
            account_info.GetAccountId(),
            base::BindOnce(&TurnSyncOnHelperDelegateImpl::
                               OnProfileSigninRestrictionsFetched,
                           weak_ptr_factory_.GetWeakPtr(), account_info,
                           std::move(callback)),
            policy::utils::IsPolicyTestingEnabled(
                browser_->GetProfile()->GetPrefs(), chrome::GetChannel())
                ? browser_->GetProfile()
                      ->GetPrefs()
                      ->GetDefaultPrefValue(
                          prefs::
                              kUserCloudSigninPolicyResponseFromPolicyTestPage)
                      ->GetString()
                : std::string());
    return;
  }

  browser_->GetFeatures()
      .signin_view_controller()
      ->ShowModalManagedUserNoticeDialog(
          std::make_unique<signin::EnterpriseProfileCreationDialogParams>(
              account_info, /*is_oidc_account=*/false,
              /*user_already_signed_in=*/user_already_signed_in_,
              /*profile_creation_required_by_policy=*/false,
              /*show_link_data_option=*/false,
              base::BindOnce(
                  [](signin::SigninChoiceCallback callback,
                     signin::SigninChoice choice) {
                    // When `show_link_data_option` is false,
                    // `ShowModalManagedUserNoticeDialog()` calls back
                    // with either `SIGNIN_CHOICE_CANCEL` or
                    // `SIGNIN_CHOICE_NEW_PROFILE`. The profile is clean here,
                    // no need to create a new one.
                    std::move(callback).Run(
                        choice == signin::SigninChoice::SIGNIN_CHOICE_CANCEL
                            ? signin::SigninChoice::SIGNIN_CHOICE_CANCEL
                            : signin::SigninChoice::SIGNIN_CHOICE_CONTINUE);
                  },
                  std::move(callback)),
              base::BindOnce(&SigninViewController::CloseModalSignin,
                             browser_->GetFeatures()
                                 .signin_view_controller()
                                 ->AsWeakPtr())));
}
