// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/bookmarks/common/bookmark_metrics.h"

#include <string>
#include <string_view>

#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/user_metrics.h"
#include "base/strings/strcat.h"
#include "components/bookmarks/common/storage_file_encryption_type.h"
#include "components/bookmarks/common/url_load_stats.h"
#include "components/bookmarks/common/user_folder_load_stats.h"

namespace bookmarks::metrics {

namespace {

constexpr int kBytesPerKB = 1024;
constexpr int kUmaUserFolderDepthLimit = 11;

void RecordBookmarkParentFolderType(BookmarkFolderTypeForUMA parent) {
  base::UmaHistogramEnumeration("Bookmarks.ParentFolderType", parent);
}

std::string_view GetStorageStateSuffixForMetrics(
    StorageStateForUma storage_state) {
  switch (storage_state) {
    case StorageStateForUma::kAccount:
      return ".AccountStorage";
    case StorageStateForUma::kLocalOnly:
      return ".LocalStorage";
    case StorageStateForUma::kSyncEnabled:
      return ".LocalStorageSyncing";
  }
  NOTREACHED();
}

// LINT.IfChange(BookmarksFileType)

std::string_view GetStorageFileSuffixForMetrics(
    StorageFileForUma storage_file) {
  switch (storage_file) {
    case StorageFileForUma::kLocalOrSyncable:
      return ".LocalOrSyncable";
    case StorageFileForUma::kAccount:
      return ".Account";
  }
  NOTREACHED();
}
// LINT.ThenChange(/tools/metrics/histograms/metadata/bookmarks/histograms.xml:BookmarksFileType)

// LINT.IfChange(EncryptionType)

std::string_view GetStorageFileEncryptionTypeSuffixForMetrics(
    StorageFileEncryptionType encryption_type) {
  switch (encryption_type) {
    case StorageFileEncryptionType::kClearText:
      return ".ClearText";
    case StorageFileEncryptionType::kEncrypted:
      return ".Encrypted";
  }
  NOTREACHED();
}
// LINT.ThenChange(/tools/metrics/histograms/metadata/bookmarks/histograms.xml:EncryptionType)

// LINT.IfChange(ImportantFileWriter)
std::string_view GetImportantFileWriterTypeSuffixForMetrics(
    ImportantFileWriterType important_file_writer_type) {
  switch (important_file_writer_type) {
    case ImportantFileWriterType::kBookmarkStorage:
      return ".BookmarkStorage";
    case ImportantFileWriterType::kBookmarkStorageEncrypted:
      return ".BookmarkStorageEncrypted";
    case ImportantFileWriterType::kBookmarkStorageImmediate:
      return ".BookmarkStorageImmediate";
    case ImportantFileWriterType::kBookmarkStorageEncryptedImmediate:
      return ".BookmarkStorageEncryptedImmediate";
  }
  NOTREACHED();
}
// LINT.ThenChange(/tools/metrics/histograms/metadata/bookmarks/histograms.xml:ImportantFileWriter)
}  // namespace

void RecordUrlBookmarkAdded(BookmarkFolderTypeForUMA parent,
                            StorageStateForUma storage_state,
                            int ancestor_user_folder_depth) {
  base::RecordAction(base::UserMetricsAction("Bookmarks.Added"));
  base::RecordComputedAction(base::StrCat(
      {"Bookmarks.Added", GetStorageStateSuffixForMetrics(storage_state)}));
  RecordBookmarkParentFolderType(parent);
  base::UmaHistogramExactLinear("Bookmarks.UserFolderDepth.UrlAdded",
                                ancestor_user_folder_depth,
                                kUmaUserFolderDepthLimit);
}

void RecordBookmarkFolderAdded(BookmarkFolderTypeForUMA parent,
                               StorageStateForUma storage_state) {
  base::RecordAction(base::UserMetricsAction("Bookmarks.FolderAdded"));
  base::RecordComputedAction(
      base::StrCat({"Bookmarks.FolderAdded",
                    GetStorageStateSuffixForMetrics(storage_state)}));
  RecordBookmarkParentFolderType(parent);
}

void RecordBookmarkRemoved(BookmarkEditSource source) {
  base::UmaHistogramEnumeration("Bookmarks.RemovedSource", source);
}

void RecordBookmarkOpened(base::Time now,
                          base::Time date_last_used,
                          base::Time date_added,
                          StorageStateForUma storage_state,
                          bool is_url_bookmark,
                          int ancestor_user_folder_depth) {
  if (date_last_used != base::Time()) {
    base::UmaHistogramCounts10000("Bookmarks.Opened.TimeSinceLastUsed",
                                  (now - date_last_used).InDays());
  }
  base::UmaHistogramCounts10000("Bookmarks.Opened.TimeSinceAdded",
                                (now - date_added).InDays());
  if (is_url_bookmark) {
    base::UmaHistogramExactLinear("Bookmarks.UserFolderDepth.UrlOpened",
                                  ancestor_user_folder_depth,
                                  kUmaUserFolderDepthLimit);
  }

  base::RecordAction(base::UserMetricsAction("Bookmarks.Opened"));
  base::RecordComputedAction(base::StrCat(
      {"Bookmarks.Opened", GetStorageStateSuffixForMetrics(storage_state)}));
}

void RecordBookmarkMovedTo(BookmarkFolderTypeForUMA new_parent) {
  RecordBookmarkParentFolderType(new_parent);
}

void RecordTimeSinceLastScheduledSave(base::TimeDelta delta) {
  base::UmaHistogramLongTimes("Bookmarks.Storage.TimeSinceLastScheduledSave",
                              delta);
}

void RecordTimeToLoadAtStartup(base::TimeDelta delta) {
  UmaHistogramTimes("Bookmarks.Storage.TimeToLoadAtStartup2", delta);
}

void RecordFileSizeAtStartup(StorageFileEncryptionType encryption_type,
                             int64_t total_bytes) {
  int total_size_kb = base::saturated_cast<int>(total_bytes / kBytesPerKB);
  base::UmaHistogramCounts1M(
      encryption_type == StorageFileEncryptionType::kClearText
          ? "Bookmarks.Storage.FileSizeAtStartup2"
          : "Bookmarks.Storage.EncryptedFileSizeAtStartup",
      total_size_kb);
}

void RecordURLEdit(BookmarkEditSource source) {
  base::UmaHistogramEnumeration("Bookmarks.EditURLSource", source);
}

void RecordTitleEdit(BookmarkEditSource source) {
  base::UmaHistogramEnumeration("Bookmarks.EditTitleSource", source);
}

void RecordUrlLoadStatsOnProfileLoad(const UrlLoadStats& stats) {
  base::UmaHistogramCounts100000(
      "Bookmarks.Count.OnProfileLoad3",
      base::saturated_cast<int>(stats.total_url_bookmark_count));

  base::UmaHistogramCounts1000(
      "Bookmarks.Times.OnProfileLoad.TimeSinceAdded3",
      base::saturated_cast<int>(stats.avg_num_days_since_added));

  int utilization;
  if (stats.used_url_bookmark_count == 0) {
    utilization = 0;
  } else {
    // Calculate the utilization as a percentage from 0 - 100. Do this without
    // a float conversion by multiplying everything by 100 first.
    utilization = (100 * stats.used_url_bookmark_count +
                   stats.total_url_bookmark_count / 2) /
                  stats.total_url_bookmark_count;
  }

  for (size_t num_days_since_used : stats.per_bookmark_num_days_since_used) {
    base::UmaHistogramCounts1000(
        "Bookmarks.UtilizationPerBookmark.OnProfileLoad.DaysSinceUsed",
        base::saturated_cast<int>(num_days_since_used));
  };

  base::UmaHistogramPercentage(
      "Bookmarks.Utilization.OnProfileLoad.PercentageUsed", utilization);
  base::UmaHistogramCounts1000("Bookmarks.Utilization.OnProfileLoad.TotalUsed",
                               stats.used_url_bookmark_count);
  base::UmaHistogramCounts1000(
      "Bookmarks.Utilization.OnProfileLoad.TotalUnused",
      stats.total_url_bookmark_count - stats.used_url_bookmark_count);

  if (stats.most_recently_used_bookmark_days != SIZE_MAX) {
    base::UmaHistogramCounts1000(
        "Bookmarks.Times.OnProfileLoad.MostRecentlyUsedBookmarkInDays",
        base::saturated_cast<int>(stats.most_recently_used_bookmark_days));
  }

  if (stats.most_recently_saved_bookmark_days != SIZE_MAX) {
    base::UmaHistogramCounts1000(
        "Bookmarks.Times.OnProfileLoad.MostRecentlySavedBookmarkInDays",
        base::saturated_cast<int>(stats.most_recently_saved_bookmark_days));
  }

  if (stats.most_recently_saved_folder_days != SIZE_MAX) {
    base::UmaHistogramCounts1000(
        "Bookmarks.Times.OnProfileLoad.MostRecentlyAddedFolderInDays",
        base::saturated_cast<int>(stats.most_recently_saved_folder_days));
  }
}

void RecordUserFolderLoadStatsOnProfileLoad(const UserFolderLoadStats& stats) {
  base::UmaHistogramExactLinear(
      "Bookmarks.UserFolder.OnProfileLoad.TopLevelCount",
      stats.total_top_level_folders, /*exclusive_max=*/31);

  base::UmaHistogramCustomCounts("Bookmarks.UserFolder.OnProfileLoad.Count",
                                 stats.total_folders, /*min=*/1,
                                 /*exclusive_max=*/300, /*buckets=*/100);

  base::UmaHistogramCounts100(
      "Bookmarks.UserFolder.OnProfileLoad.BookmarkBarTopLevelItems",
      stats.bookmark_bar_top_level_items);
}

void RecordCloneBookmarkNode(int num_cloned) {
  base::UmaHistogramCounts100("Bookmarks.Clone.NumCloned", num_cloned);
}

void RecordAverageNodeSizeAtStartupIfNonZero(
    StorageFileEncryptionType encryption_type,
    int total_url_bookmark_count,
    size_t sum_file_size_in_bytes) {
  if (sum_file_size_in_bytes == 0 || total_url_bookmark_count == 0) {
    return;
  }

  base::UmaHistogramCounts10000(
      base::StrCat(
          {"Bookmarks.AverageNodeSize",
           GetStorageFileEncryptionTypeSuffixForMetrics(encryption_type)}),
      sum_file_size_in_bytes / total_url_bookmark_count);
}

void RecordIdsReassignedOnProfileLoad(StorageFileForUma storage_file,
                                      bool ids_reassigned) {
  base::UmaHistogramBoolean(
      base::StrCat({"Bookmarks.IdsReassigned2.OnProfileLoad",
                    GetStorageFileSuffixForMetrics(storage_file)}),
      ids_reassigned);
}

void RecordBookmarksExistInStorageType(
    bool bookmark_bar_only,
    BookmarksExistInStorageType storage_type) {
  base::UmaHistogramEnumeration(
      base::StrCat({"Bookmarks.BookmarksExistInStorageType.",
                    bookmark_bar_only ? "UnderBookmarksBar"
                                      : "ConsideringAllBookmarks"}),
      storage_type);
}

void RecordBookmarksFileLoadResult(StorageFileForUma storage_file,
                                   StorageFileEncryptionType encryption_type,
                                   BookmarksFileLoadResult result) {
  base::UmaHistogramEnumeration(
      base::StrCat(
          {"Bookmarks.BookmarksFileLoadResult",
           GetStorageFileSuffixForMetrics(storage_file),
           GetStorageFileEncryptionTypeSuffixForMetrics(encryption_type)}),
      result);
}

void RecordEncryptedBookmarksFileMatchesResult(StorageFileForUma storage_file,
                                               bool file_matches) {
  base::UmaHistogramBoolean(
      base::StrCat({"Bookmarks.EncryptedBookmarksFileMatchesResult",
                    GetStorageFileSuffixForMetrics(storage_file)}),
      file_matches);
}

void RecordTimeToReadFile(StorageFileForUma storage_file,
                          StorageFileEncryptionType encryption_type,
                          base::TimeDelta delta) {
  base::UmaHistogramTimes(
      base::StrCat(
          {"Bookmarks.TimeToReadFile",
           GetStorageFileSuffixForMetrics(storage_file),
           GetStorageFileEncryptionTypeSuffixForMetrics(encryption_type)}),
      delta);
}

void RecordFallbackToClearTextFileOnLoadResult(StorageFileForUma storage_file,
                                               BookmarksFileLoadResult result) {
  base::UmaHistogramEnumeration(
      base::StrCat({"Bookmarks.FallbackToClearTextFileOnLoadResult",
                    GetStorageFileSuffixForMetrics(storage_file)}),
      result);
}

void RecordClearTextFileDeletionResult(StorageFileForUma storage_file,
                                       bool deletion_result) {
  base::UmaHistogramBoolean(
      base::StrCat({"Bookmarks.DeleteClearTextFile",
                    GetStorageFileSuffixForMetrics(storage_file)}),
      deletion_result);
}

void RecordBookmarksSerializationResult(
    ImportantFileWriterType important_file_writer_type,
    BookmarksSerializationResult result) {
  base::UmaHistogramEnumeration(
      base::StrCat({"Bookmarks.BookmarksSerializationResult",
                    GetImportantFileWriterTypeSuffixForMetrics(
                        important_file_writer_type)}),
      result);
}

void RecordTimeToSerialize(ImportantFileWriterType important_file_writer_type,
                           base::TimeDelta delta) {
  base::UmaHistogramTimes(
      base::StrCat({"Bookmarks.Storage.TimeToSerialize",
                    GetImportantFileWriterTypeSuffixForMetrics(
                        important_file_writer_type)}),
      delta);
}

}  // namespace bookmarks::metrics
