// 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/apps/intent_helper/chromeos_disabled_apps_throttle.h"

#include <cstddef>
#include <memory>
#include <optional>
#include <vector>

#include "base/memory/ptr_util.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/apps/app_service/chrome_app_deprecation/chrome_app_deprecation.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/policy/system_features_disable_list_policy_handler.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/grit/browser_resources.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/navigation_throttle.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/webui/jstemplate_builder.h"
#include "ui/base/webui/web_ui_util.h"

namespace apps {
namespace {
using ThrottleCheckResult = content::NavigationThrottle::ThrottleCheckResult;

std::string GetAppDisabledErrorPage() {
  base::DictValue strings;

  strings.Set("disabledPageHeader",
              l10n_util::GetStringUTF16(IDS_CHROME_URLS_DISABLED_PAGE_HEADER));
  strings.Set("disabledPageTitle",
              l10n_util::GetStringUTF16(IDS_CHROME_URLS_DISABLED_PAGE_TITLE));
  strings.Set("disabledPageMessage",
              l10n_util::GetStringUTF16(IDS_CHROME_URLS_DISABLED_PAGE_MESSAGE));
  std::string html =
      ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
          IDR_CHROME_URLS_DISABLED_PAGE_HTML);
  const std::string& app_locale = g_browser_process->GetApplicationLocale();
  webui::SetLoadTimeDataDefaults(app_locale, &strings);
  return webui::GetI18nTemplateHtml(html, strings);
}

std::optional<std::string> FindThrottlingApp(Profile* profile,
                                             const GURL& url) {
  std::vector<std::string> app_ids =
      apps::AppServiceProxyFactory::GetForProfile(profile)->GetAppIdsForUrl(
          url, /*exclude_browsers=*/true, /*exclude_browser_tab_apps=*/false);

  for (const auto& app_id : app_ids) {
    policy::SystemFeature system_feature =
        policy::SystemFeaturesDisableListPolicyHandler::
            GetSystemFeatureFromAppId(app_id);

    if (system_feature == policy::SystemFeature::kUnknownSystemFeature) {
      continue;
    }

    if (!policy::SystemFeaturesDisableListPolicyHandler::
            IsSystemFeatureDisabled(system_feature,
                                    g_browser_process->local_state())) {
      continue;
    }

    return app_id;
  }

  return std::nullopt;
}
}  // namespace

// static
void ChromeOsDisabledAppsThrottle::MaybeCreateAndAdd(
    content::NavigationThrottleRegistry& registry) {
  // Don't handle navigations in subframes or main frames that are in a nested
  // frame tree (e.g. portals, fenced-frame).
  auto& handle = registry.GetNavigationHandle();
  if (!handle.IsInPrimaryMainFrame() && !handle.IsInPrerenderedMainFrame()) {
    return;
  }

  content::WebContents* web_contents = handle.GetWebContents();
  Profile* profile =
      Profile::FromBrowserContext(web_contents->GetBrowserContext());
  if (!AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile)) {
    return;
  }

  registry.AddThrottle(
      base::WrapUnique(new ChromeOsDisabledAppsThrottle(registry)));
}

ChromeOsDisabledAppsThrottle::ChromeOsDisabledAppsThrottle(
    content::NavigationThrottleRegistry& registry)
    : content::NavigationThrottle(registry) {}
ChromeOsDisabledAppsThrottle::~ChromeOsDisabledAppsThrottle() = default;

const char* ChromeOsDisabledAppsThrottle::GetNameForLogging() {
  return "ChromeOsDisabledAppsThrottle";
}

ThrottleCheckResult ChromeOsDisabledAppsThrottle::WillStartRequest() {
  return HandleRequest();
}

ThrottleCheckResult ChromeOsDisabledAppsThrottle::WillRedirectRequest() {
  return HandleRequest();
}

ThrottleCheckResult ChromeOsDisabledAppsThrottle::HandleRequest() {
  content::WebContents* web_contents = navigation_handle()->GetWebContents();
  const GURL& url = navigation_handle()->GetURL();

  Profile* profile =
      Profile::FromBrowserContext(web_contents->GetBrowserContext());

  std::optional<std::string> found_app_id = FindThrottlingApp(profile, url);

  if (!found_app_id) {
    return content::NavigationThrottle::PROCEED;
  }

  switch (
      apps::chrome_app_deprecation::HandleDeprecation(*found_app_id, profile)) {
    case apps::chrome_app_deprecation::DeprecationStatus::kLaunchBlocked:
      return content::NavigationThrottle::CANCEL;
    case apps::chrome_app_deprecation::DeprecationStatus::kLaunchAllowed:
      return ThrottleCheckResult(content::NavigationThrottle::CANCEL,
                                 net::ERR_BLOCKED_BY_ADMINISTRATOR,
                                 GetAppDisabledErrorPage());
  }
}

}  // namespace apps
