// Copyright 2015 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/passwords/ui_utils.h"

#include <stddef.h>

#include <algorithm>

#include "base/feature_list.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "build/branding_buildflags.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "components/affiliations/core/browser/affiliation_utils.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
#include "components/password_manager/core/browser/manage_passwords_referrer.h"
#include "components/password_manager/core/browser/origin_credential_store.h"
#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/password_manager_client.h"
#include "components/password_manager/core/browser/password_manager_constants.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/browser/password_sync_util.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/strings/grit/components_strings.h"
#include "components/sync/service/sync_service.h"
#include "components/sync/service/sync_user_settings.h"
#include "components/url_formatter/elide_url.h"
#include "components/vector_icons/vector_icons.h"
#include "content/public/browser/web_contents.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_features.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "url/gurl.h"
#include "url/origin.h"

#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/hats/hats_service.h"          // nogncheck
#include "chrome/browser/ui/hats/hats_service_factory.h"  // nogncheck
#include "chrome/browser/ui/user_education/show_promo_in_page.h"
#endif

namespace {

using password_manager::ManagePasswordsReferrer;

// Checks whether two URLs are from the same domain or host.
bool SameDomainOrHost(const GURL& gurl, const url::Origin& origin) {
  return net::registry_controlled_domains::SameDomainOrHost(
      gurl, origin,
      net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
}

}  // namespace

gfx::ImageSkia ScaleImageForAccountAvatar(gfx::ImageSkia skia_image) {
  gfx::Size size = skia_image.size();
  if (size.height() != size.width()) {
    gfx::Rect target(size);
    int side = std::min(size.height(), size.width());
    target.ClampToCenteredSize(gfx::Size(side, side));
    skia_image = gfx::ImageSkiaOperations::ExtractSubset(skia_image, target);
  }
  return gfx::ImageSkiaOperations::CreateResizedImage(
      skia_image, skia::ImageOperations::RESIZE_BEST,
      gfx::Size(kAvatarImageSize, kAvatarImageSize));
}

std::pair<std::u16string, std::u16string> GetCredentialLabelsForAccountChooser(
    const password_manager::PasswordForm& form) {
  std::u16string federation;
  if (form.IsFederatedCredential()) {
    federation = GetDisplayFederation(form);
  }

  if (form.display_name.empty()) {
    return std::make_pair(form.username_value, std::move(federation));
  }

  // Display name isn't empty.
  if (federation.empty()) {
    return std::make_pair(form.display_name, form.username_value);
  }

  return std::make_pair(form.display_name,
                        form.username_value + u"\n" + federation);
}

std::u16string GetSavePasswordDialogTitleText(
    const GURL& user_visible_url,
    const url::Origin& form_origin_url,
    PasswordTitleType dialog_type) {
  std::vector<size_t> offsets;
  std::vector<std::u16string> replacements;
  int title_id = 0;
  switch (dialog_type) {
    case PasswordTitleType::SAVE_PASSWORD:
      title_id = IDS_SAVE_PASSWORD;
      break;
    case PasswordTitleType::SAVE_ACCOUNT:
      title_id = IDS_SAVE_ACCOUNT;
      break;
    case PasswordTitleType::UPDATE_PASSWORD:
      title_id = IDS_UPDATE_PASSWORD;
      break;
  }

  // Check whether the registry controlled domains for user-visible URL (i.e.
  // the one seen in the omnibox) and the password form post-submit navigation
  // URL differs or not.
  if (!SameDomainOrHost(user_visible_url, form_origin_url)) {
    DCHECK_NE(PasswordTitleType::SAVE_ACCOUNT, dialog_type)
        << "Calls to save account should always happen on the same domain.";
    title_id = dialog_type == PasswordTitleType::UPDATE_PASSWORD
                   ? IDS_UPDATE_PASSWORD_DIFFERENT_DOMAINS_TITLE
                   : IDS_SAVE_PASSWORD_DIFFERENT_DOMAINS_TITLE;
    replacements.push_back(url_formatter::FormatOriginForSecurityDisplay(
        form_origin_url, url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS));
  }

  return l10n_util::GetStringFUTF16(title_id, replacements, &offsets);
}

std::u16string GetManagePasswordsDialogTitleText(
    const GURL& user_visible_url,
    const url::Origin& password_origin_url,
    bool has_credentials) {
  DCHECK(!password_origin_url.opaque());
  // Check whether the registry controlled domains for user-visible URL
  // (i.e. the one seen in the omnibox) and the managed password origin URL
  // differ or not.
  if (!SameDomainOrHost(user_visible_url, password_origin_url)) {
    std::u16string formatted_url =
        url_formatter::FormatOriginForSecurityDisplay(password_origin_url);
    return l10n_util::GetStringFUTF16(
        has_credentials
            ? IDS_MANAGE_PASSWORDS_DIFFERENT_DOMAIN_TITLE
            : IDS_MANAGE_PASSWORDS_DIFFERENT_DOMAIN_NO_PASSWORDS_TITLE,
        formatted_url);
  }
  return l10n_util::GetStringUTF16(
      has_credentials ? IDS_MANAGE_PASSWORDS_TITLE
                      : IDS_MANAGE_PASSWORDS_NO_PASSWORDS_TITLE);
}

std::u16string GetConfirmationManagePasswordsDialogTitleText(bool is_update) {
  return is_update ? l10n_util::GetStringUTF16(
                         IDS_PASSWORD_MANAGER_CONFIRM_UPDATE_TITLE)
                   : l10n_util::GetStringUTF16(
                         IDS_PASSWORD_MANAGER_CONFIRM_SAVED_TITLE);
}

std::u16string GetDisplayUsername(const password_manager::PasswordForm& form) {
  return form.username_value.empty()
             ? l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_EMPTY_LOGIN)
             : form.username_value;
}

std::u16string GetDisplayUsername(
    const password_manager::UiCredential& credential) {
  return credential.username().empty()
             ? l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_EMPTY_LOGIN)
             : credential.username();
}

std::u16string GetDisplayFederation(
    const password_manager::PasswordForm& form) {
  return url_formatter::FormatUrlForSecurityDisplay(
      form.federation_origin.GetURL(),
      url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
}

std::u16string GetDisplayPassword(const password_manager::PasswordForm& form) {
  return !form.IsFederatedCredential()
             ? form.password_value
             : l10n_util::GetStringFUTF16(IDS_PASSWORDS_VIA_FEDERATION,
                                          GetDisplayFederation(form));
}

bool IsSyncingAutosignSetting(Profile* profile) {
  const syncer::SyncService* sync_service =
      SyncServiceFactory::GetForProfile(profile);
  return (
      sync_service &&
      sync_service->GetActiveDataTypes().Has(syncer::PRIORITY_PREFERENCES) &&
      // PRIORITY_PREFERENCES is always active (decoupled from sync user
      // toggle). Thus, the preferences user toggle should be checked
      // separately.
      sync_service->GetUserSettings()->GetSelectedTypes().Has(
          syncer::UserSelectableType::kPreferences));
}

std::string GetGooglePasswordManagerSubPageURLStr() {
  return base::StrCat({chrome::kChromeUIPasswordManagerURL, "/",
                       chrome::kPasswordManagerSubPage});
}

// Navigation is handled differently on Android.
#if !BUILDFLAG(IS_ANDROID)
void TriggerManagePasswordsPerceptionSurvey(BrowserWindowInterface* browser,
                                            ManagePasswordsReferrer referrer) {
  Profile* profile = browser->GetProfile();
  if (!base::FeatureList::IsEnabled(
          autofill::features::kManagePasswordsPerceptionSurvey) ||
      profile->IsOffTheRecord()) {
    return;
  }
  HatsService* hats_service =
      HatsServiceFactory::GetForProfile(profile, /*create_if_necessary=*/false);
  if (!hats_service) {
    return;
  }
  const SurveyBitsData product_specific_bits_data = {
      {"Visit from Your saved info",
       referrer == ManagePasswordsReferrer::kChromeSettings},
  };
  hats_service->LaunchDelayedSurvey(kHatsSurveyTriggerManagePasswordsPerception,
                                    10000, product_specific_bits_data);
}

void NavigateToManagePasswordsPage(BrowserWindowInterface* browser,
                                   ManagePasswordsReferrer referrer) {
  if (!browser) {
    return;
  }
  base::UmaHistogramEnumeration("PasswordManager.ManagePasswordsReferrer",
                                referrer);
  TriggerManagePasswordsPerceptionSurvey(browser, referrer);
  chrome::ShowPasswordManager(browser);
}

void NavigateToPasswordDetailsPage(BrowserWindowInterface* browser,
                                   const std::string& password_domain_name,
                                   ManagePasswordsReferrer referrer) {
  if (!browser) {
    return;
  }
  base::UmaHistogramEnumeration("PasswordManager.ManagePasswordsReferrer",
                                referrer);
  chrome::ShowPasswordDetailsPage(browser, password_domain_name);
}

#endif  // !BUILDFLAG(IS_ANDROID)

mojo::Remote<network::mojom::URLLoaderFactory> GetURLLoaderForMainFrame(
    content::WebContents* web_contents) {
  content::RenderFrameHost* frame = web_contents->GetPrimaryMainFrame();
  mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory;
  frame->CreateNetworkServiceDefaultFactory(
      url_loader_factory.BindNewPipeAndPassReceiver());
  return url_loader_factory;
}

const gfx::VectorIcon& GooglePasswordManagerVectorIcon() {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  return vector_icons::kGooglePasswordManagerIcon;
#else
  return features::IsRoundedIconsEnabled() ? kVpnKeyFilledIcon : kKeyOldIcon;
#endif
}

std::optional<AccountInfo> GetAccountInfoForPasswordMessages(
    syncer::SyncService* sync_service,
    signin::IdentityManager* identity_manager) {
  if (!password_manager::sync_util::HasChosenToSyncPasswords(sync_service)) {
    return std::nullopt;
  }
  CoreAccountId account_id =
      identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSignin);
  return identity_manager->FindExtendedAccountInfoByAccountId(account_id);
}

std::string GetDisplayableAccountName(
    syncer::SyncService* sync_service,
    signin::IdentityManager* identity_manager) {
  std::optional<AccountInfo> account_info =
      GetAccountInfoForPasswordMessages(sync_service, identity_manager);
  if (!account_info.has_value()) {
    return "";
  }
  return account_info->CanHaveEmailAddressDisplayed()
             ? std::string(account_info->GetEmail())
             : std::string(account_info->GetFullName().value_or(""));
}
