// Copyright 2023 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/url_identity.h"

#include <optional>
#include <string>

#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/types/expected.h"
#include "chrome/browser/profiles/profile.h"
#include "components/url_formatter/elide_url.h"
#include "components/url_formatter/url_formatter.h"
#include "extensions/buildflags/buildflags.h"
#include "url/gurl.h"

// Android does not support //chrome/browser/web_applications, see
// chrome/browser/web_applications/BUILD.gn.
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "components/webapps/common/web_app_id.h"
#include "components/webapps/isolated_web_apps/scheme.h"
#endif  // !BUILDFLAG(IS_ANDROID)

#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
#include "extensions/browser/extension_registry.h"  // nogncheck
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#endif  // BUILDFLAG(ENABLE_EXTENSIONS_CORE)

using Type = UrlIdentity::Type;
using DefaultFormatOptions = UrlIdentity::DefaultFormatOptions;
using FormatOptions = UrlIdentity::FormatOptions;

namespace {

UrlIdentity CreateDefaultUrlIdentityFromUrl(const GURL& url,
                                            const FormatOptions& options) {
  std::u16string name;

  if (options.default_options.Has(DefaultFormatOptions::kRawSpec)) {
    return UrlIdentity{
        .type = Type::kDefault,
        .name = base::CollapseWhitespace(base::UTF8ToUTF16(url.spec()), false)};
  }
  if (options.default_options.Has(
          DefaultFormatOptions::kOmitCryptographicScheme)) {
    name = url_formatter::FormatUrlForSecurityDisplay(
        url, url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
  } else if (options.default_options.Has(DefaultFormatOptions::kHostname)) {
    name = url_formatter::IDNToUnicode(url.host());
  } else if (options.default_options.Has(
                 DefaultFormatOptions::kOmitSchemePathAndTrivialSubdomains)) {
    name = url_formatter::FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains(
        url);
  } else {
    name = url_formatter::FormatUrlForSecurityDisplay(url);
  }

  return UrlIdentity{.type = Type::kDefault, .name = name};
}

#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
UrlIdentity CreateChromeExtensionIdentityFromUrl(Profile* profile,
                                                 const GURL& url,
                                                 const FormatOptions& options) {
  DCHECK(url.SchemeIs(extensions::kExtensionScheme));

  DCHECK(profile) << "Profile cannot be null when type is Chrome Extensions.";

  extensions::ExtensionRegistry* extension_registry =
      extensions::ExtensionRegistry::Get(profile);
  DCHECK(extension_registry);

  const extensions::Extension* extension = nullptr;
  extension = extension_registry->enabled_extensions().GetByID(url.GetHost());

  if (!extension) {  // fallback to default
    return CreateDefaultUrlIdentityFromUrl(url, options);
  }

  return UrlIdentity{.type = Type::kChromeExtension,
                     .name = base::CollapseWhitespace(
                         base::UTF8ToUTF16(extension->name()), false)};
}
#endif  // BUILDFLAG(ENABLE_EXTENSIONS_CORE)

#if !BUILDFLAG(IS_ANDROID)
UrlIdentity CreateIsolatedWebAppIdentityFromUrl(Profile* profile,
                                                const GURL& url,
                                                const FormatOptions& options) {
  DCHECK(url.SchemeIs(webapps::kIsolatedAppScheme));

  DCHECK(profile) << "Profile cannot be null when type is Isolated Web App.";

  web_app::WebAppProvider* provider =
      web_app::WebAppProvider::GetForWebApps(profile);
  DCHECK(provider);

  std::optional<webapps::AppId> app_id =
      provider->registrar_unsafe().FindBestAppWithUrlInScope(
          url, web_app::WebAppFilter::IsIsolatedApp() |
                   web_app::WebAppFilter::IsIsolatedSubApp());
  if (!app_id.has_value()) {  // fallback to default
    return CreateDefaultUrlIdentityFromUrl(url, options);
  }

  const web_app::WebApp* web_app =
      provider->registrar_unsafe().GetAppById(app_id.value());

  if (!web_app) {  // fallback to default
    return CreateDefaultUrlIdentityFromUrl(url, options);
  }

  return UrlIdentity{
      .type = Type::kIsolatedWebApp,
      .name = base::CollapseWhitespace(
          base::UTF8ToUTF16(
              provider->registrar_unsafe().GetAppShortName(app_id.value())),
          false)};
}
#endif  // !BUILDFLAG(IS_ANDROID)

UrlIdentity CreateFileIdentityFromUrl(Profile* profile,
                                      const GURL& url,
                                      const FormatOptions& options) {
  DCHECK(url.SchemeIsFile());

  return UrlIdentity{
      .type = Type::kFile,
      .name = url_formatter::FormatUrlForSecurityDisplay(url),
  };
}
}  // namespace

UrlIdentity UrlIdentity::CreateFromUrl(Profile* profile,
                                       const GURL& url,
                                       const TypeSet& allowed_types,
                                       const FormatOptions& options) {
#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
  if (url.SchemeIs(extensions::kExtensionScheme)) {
    DCHECK(allowed_types.Has(Type::kChromeExtension));
    return CreateChromeExtensionIdentityFromUrl(profile, url, options);
  }
#endif  // BUILDFLAG(ENABLE_EXTENSIONS_CORE)

#if !BUILDFLAG(IS_ANDROID)
  if (url.SchemeIs(webapps::kIsolatedAppScheme)) {
    DCHECK(allowed_types.Has(Type::kIsolatedWebApp));
    return CreateIsolatedWebAppIdentityFromUrl(profile, url, options);
  }
#endif  // !BUILDFLAG(IS_ANDROID)

  if (url.SchemeIsFile()) {
    DCHECK(allowed_types.Has(Type::kFile));
    return CreateFileIdentityFromUrl(profile, url, options);
  }

  DCHECK(allowed_types.Has(Type::kDefault));
  return CreateDefaultUrlIdentityFromUrl(url, options);
}
