// 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/web_applications/web_app_metrics.h"

#include <algorithm>
#include <optional>
#include <string>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/metrics/histogram_functions.h"
#include "base/observer_list.h"
#include "base/one_shot_event.h"
#include "base/power_monitor/power_monitor.h"
#include "base/task/sequenced_task_runner.h"
#include "build/build_config.h"
#include "chrome/browser/after_startup_task_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include "chrome/browser/ui/web_applications/web_app_metrics_factory.h"
#include "chrome/browser/web_applications/proto/web_app_install_state.pb.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_filter.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/browser/web_applications/web_app_tab_helper.h"
#include "chrome/browser/web_applications/web_app_ui_manager.h"
#include "components/site_engagement/content/engagement_type.h"
#include "components/site_engagement/content/site_engagement_service.h"
#include "content/public/browser/web_contents.h"

namespace web_app {

namespace {

bool g_disable_automatic_icon_health_checks_for_testing = false;

void RecordEngagementHistogram(
    const std::string& histogram_name,
    site_engagement::EngagementType engagement_type) {
  base::UmaHistogramEnumeration(histogram_name, engagement_type,
                                site_engagement::EngagementType::kLast);
}

void RecordTabOrWindowHistogram(
    const std::string& histogram_prefix,
    bool in_window,
    site_engagement::EngagementType engagement_type) {
  RecordEngagementHistogram(
      histogram_prefix + (in_window ? ".InWindow" : ".InTab"), engagement_type);
}

}  // namespace

// static
WebAppMetrics* WebAppMetrics::Get(Profile* profile) {
  return WebAppMetricsFactory::GetForProfile(profile);
}

// static
void WebAppMetrics::DisableAutomaticIconHealthChecksForTesting() {
  g_disable_automatic_icon_health_checks_for_testing = true;
}

WebAppMetrics::WebAppMetrics(Profile* profile)
    : SiteEngagementObserver(
          site_engagement::SiteEngagementService::Get(profile)),
      profile_(profile),
      icon_health_checks_(profile) {
  if (!g_disable_automatic_icon_health_checks_for_testing) {
    AfterStartupTaskUtils::PostTask(
        FROM_HERE, base::SequencedTaskRunner::GetCurrentDefault(),
        base::BindOnce(&WebAppIconHealthChecks::Start,
                       icon_health_checks_.GetWeakPtr(), base::DoNothing()));
  }

  WebAppProvider* provider = WebAppProvider::GetForLocalAppsUnchecked(profile_);
  DCHECK(provider);
  provider->on_registry_ready().Post(
      FROM_HERE, base::BindOnce(&WebAppMetrics::CountUserInstalledApps,
                                weak_ptr_factory_.GetWeakPtr()));
}

WebAppMetrics::~WebAppMetrics() = default;

void WebAppMetrics::OnEngagementEvent(
    content::WebContents* web_contents,
    const GURL& url,
    double score,
    double old_score,
    site_engagement::EngagementType engagement_type,
    const std::optional<webapps::AppId>& app_id) {
  if (!web_contents) {
    return;
  }

  BrowserWindowInterface* browser =
      GlobalBrowserCollection::GetInstance()->FindBrowserWithTab(web_contents);
  if (!browser) {
    return;
  }

  // Number of apps is not yet counted.
  if (num_user_installed_apps_ == kNumUserInstalledAppsNotCounted) {
    return;
  }

  // The engagement broken down by the number of apps installed must be recorded
  // for all engagement events, not just web apps.
  if (num_user_installed_apps_ > 3) {
    RecordEngagementHistogram(
        "WebApp.Engagement.MoreThanThreeUserInstalledApps", engagement_type);
  } else if (num_user_installed_apps_ > 0) {
    RecordEngagementHistogram("WebApp.Engagement.UpToThreeUserInstalledApps",
                              engagement_type);
  } else {
    RecordEngagementHistogram("WebApp.Engagement.NoUserInstalledApps",
                              engagement_type);
  }

  if (!app_id) {
    return;
  }
  CHECK(!app_id->empty());

  // No HostedAppBrowserController if app is running as a tab in common browser.
  const bool in_window = !!AppBrowserController::From(browser);
  WebAppRegistrar& registrar =
      WebAppProvider::GetForLocalAppsUnchecked(profile_)->registrar_unsafe();
  const bool user_installed =
      registrar.AppMatches(*app_id, WebAppFilter::InstalledByUser());
  const bool is_crafted_app =
      registrar.AppMatches(*app_id, WebAppFilter::IsCraftedApp());
  const bool is_default_installed = registrar.AppMatches(
      *app_id, WebAppFilter::InstalledByDefaultManagement());

  // Record all web apps:
  RecordTabOrWindowHistogram("WebApp.Engagement", in_window, engagement_type);

  if (user_installed) {
    RecordTabOrWindowHistogram("WebApp.Engagement.UserInstalled", in_window,
                               engagement_type);
    if (is_crafted_app) {
      RecordTabOrWindowHistogram("WebApp.Engagement.UserInstalled.Crafted",
                                 in_window, engagement_type);
    } else {
      RecordTabOrWindowHistogram("WebApp.Engagement.UserInstalled.Diy",
                                 in_window, engagement_type);
    }
  }
  if (is_default_installed) {
    // Record this app into more specific bucket if was installed by default:
    RecordTabOrWindowHistogram("WebApp.Engagement.DefaultInstalled", in_window,
                               engagement_type);
  }
}

void WebAppMetrics::CountUserInstalledAppsForTesting() {
  // Reset and re-count.
  num_user_installed_apps_ = kNumUserInstalledAppsNotCounted;
  CountUserInstalledApps();
}

void WebAppMetrics::CountUserInstalledApps() {
  DCHECK_EQ(kNumUserInstalledAppsNotCounted, num_user_installed_apps_);

  WebAppProvider* provider = WebAppProvider::GetForLocalAppsUnchecked(profile_);

  num_user_installed_apps_ =
      provider->registrar_unsafe().CountUserInstalledApps();
  DCHECK_NE(kNumUserInstalledAppsNotCounted, num_user_installed_apps_);
  DCHECK_GE(num_user_installed_apps_, 0);
}

}  // namespace web_app
