// 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 "components/segmentation_platform/embedder/default_model/password_manager_user_segment.h"

#include <cstdint>
#include <memory>

#include "base/task/sequenced_task_runner.h"
#include "components/segmentation_platform/internal/metadata/metadata_writer.h"
#include "components/segmentation_platform/public/config.h"
#include "components/segmentation_platform/public/constants.h"
#include "components/segmentation_platform/public/features.h"
#include "components/segmentation_platform/public/proto/aggregation.pb.h"
#include "components/segmentation_platform/public/proto/model_metadata.pb.h"

namespace segmentation_platform {

namespace {
using proto::SegmentId;

// Default parameters for password manager user model.
constexpr SegmentId kPasswordManagerUserSegmentId =
    SegmentId::PASSWORD_MANAGER_USER;
// Store 28 buckets of input data (28 days).
constexpr int64_t kPasswordManagerUserSignalStorageLength = 28;
// Wait until we have 7 days of data.
constexpr int64_t kPasswordManagerUserMinSignalCollectionLength = 7;
// Refresh the result every 7 days.
constexpr int64_t kPasswordManagerUserResultTTLDays = 7;

// Threshold for password count. Users with more than 6 stored passwords are
// considered password manager users.
constexpr int64_t kStoredPasswordCountThreshold = 6;

// InputFeatures.

// Enum values for PasswordManager.ManagePasswordsReferrer.
constexpr std::array<int32_t, 1> kPasswordManagerReferrerEnumValues{
    0  // Chrome Settings.
};

// Enum values for PasswordManager.FillingAssistance, these buckets were
// selected based on the calculated histogram
// Transactions.Vitals.AssistedLogins.
constexpr std::array<int32_t, 3> kFillingAssistanceEnumValues{
    0,  // :-D Credential fields filled automatically.
    1,  // :-) Credential fields filled manually (but none required typing).
    2,  // :-| Password filled (automatically or manually), known username
        //     was typed.
};

// Enum values for PasswordManager.SavedPasswordIsGenerated.
constexpr std::array<int32_t, 1> kSavedPasswordTypeEnumValues{
    1  // Generated by Chrome.
};

// Positive enum value for PasswordManager.SaveUIDismissalReason. This is
// separate from the negative enum values so we get separate totals and we can
// compare them.
constexpr std::array<int32_t, 1> kSaveUiPositiveEnumValues{
    1  // Clicked 'Save'/'Update'/'Move'/etc.
};

// Negative enum values for PasswordManager.SaveUIDismissalReason.
constexpr std::array<int32_t, 3> kSaveUiNegativeEnumValues{
    0,  // Bubble lost focus / No infobar interaction.
    2,  // Clicked 'Nope'.
    3,  // Clicked 'Never'.
};

// Enum value for IOS.CredentialExtension.IsEnabled.Startup. (iOS only).
constexpr std::array<int32_t, 1> kIOSCredentialExtensionEnabledValue{
    1,  // True.
};

const char kPasswordManagerUserNegativeLabel[] = "Not_PasswordManagerUser";

// Set UMA metrics to use as input.
constexpr FeaturePair<PasswordManagerUserModel::Feature> kFeatures[] = {
    {PasswordManagerUserModel::kFeaturePasswordManagerReferrer,
     features::UMAEnum("PasswordManager.ManagePasswordsReferrer",
                       28,
                       kPasswordManagerReferrerEnumValues)},
    {PasswordManagerUserModel::kFeatureStoredPasswordCount,
     features::LatestOrDefaultValue(
         "PasswordManager.ProfileStore.TotalAccountsHiRes3.WithScheme.Https",
         28,
         0)},
    {PasswordManagerUserModel::kFeatureAssistedLoginCount,
     features::UMAEnum("PasswordManager.FillingAssistance",
                       28,
                       kFillingAssistanceEnumValues)},
    {PasswordManagerUserModel::kFeatureGeneratedPasswordCount,
     features::UMAEnum("PasswordManager.SavedPasswordIsGenerated",
                       28,
                       kSavedPasswordTypeEnumValues)},
    {PasswordManagerUserModel::kFeaturePasswordUIAcceptedCount,
     features::UMAEnum("PasswordManager.SaveUIDismissalReason",
                       28,
                       kSaveUiPositiveEnumValues)},
    {PasswordManagerUserModel::kFeaturePasswordUIDismissedCount,
     features::UMAEnum("PasswordManager.SaveUIDismissalReason",
                       28,
                       kSaveUiNegativeEnumValues)},
    {PasswordManagerUserModel::kFeatureIOSCredentialExtensionEnabled,
     features::UMAEnum("IOS.CredentialExtension.IsEnabled.Startup",
                       28,
                       kIOSCredentialExtensionEnabledValue)},
};
}  // namespace

// static
std::unique_ptr<Config> PasswordManagerUserModel::GetConfig() {
  if (!base::FeatureList::IsEnabled(
          features::kSegmentationPlatformPasswordManagerUser)) {
    return nullptr;
  }
  auto config = std::make_unique<Config>();
  config->segmentation_key = kPasswordManagerUserKey;
  config->segmentation_uma_name = kPasswordManagerUserUmaName;
  config->AddSegmentId(kPasswordManagerUserSegmentId,
                       std::make_unique<PasswordManagerUserModel>());
  config->auto_execute_and_cache = true;
  config->is_boolean_segment = true;

  return config;
}

PasswordManagerUserModel::PasswordManagerUserModel()
    : DefaultModelProvider(kPasswordManagerUserSegmentId) {}

std::unique_ptr<DefaultModelProvider::ModelConfig>
PasswordManagerUserModel::GetModelConfig() {
  proto::SegmentationModelMetadata intentional_user_metadata;
  MetadataWriter writer(&intentional_user_metadata);
  writer.SetDefaultSegmentationMetadataConfig(
      kPasswordManagerUserMinSignalCollectionLength,
      kPasswordManagerUserSignalStorageLength);

  // Set output config.
  writer.AddOutputConfigForBinaryClassifier(
      0.5,
      /*positive_label=*/kPasswordManagerUserUmaName,
      kPasswordManagerUserNegativeLabel);
  writer.AddPredictedResultTTLInOutputConfig(
      /*top_label_to_ttl_list=*/{},
      /*default_ttl=*/kPasswordManagerUserResultTTLDays, proto::TimeUnit::DAY);

  // Set features.
  writer.AddFeatures<Feature>(kFeatures);

  return std::make_unique<ModelConfig>(std::move(intentional_user_metadata),
                                       /*model_version=*/1);
}

void PasswordManagerUserModel::ExecuteModelWithInput(
    const ModelProvider::Request& inputs,
    ExecutionCallback callback) {
  // Invalid inputs.
  if (inputs.size() != kFeatureCount) {
    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE, base::BindOnce(std::move(callback), std::nullopt));
    return;
  }

  float result = 0;
  const int password_manager_visits = inputs[kFeaturePasswordManagerReferrer];
  const int stored_password_count = inputs[kFeatureStoredPasswordCount];
  const int assisted_login_count = inputs[kFeatureAssistedLoginCount];
  const int generated_password_count = inputs[kFeatureGeneratedPasswordCount];
  const int password_ui_accepted_count =
      inputs[kFeaturePasswordUIAcceptedCount];
  const int password_ui_dismissed_count =
      inputs[kFeaturePasswordUIDismissedCount];
  const int ios_credential_extension_enabled =
      inputs[kFeatureIOSCredentialExtensionEnabled];

  if (password_manager_visits > 1 ||
      stored_password_count > kStoredPasswordCountThreshold ||
      assisted_login_count >= 1 || generated_password_count >= 1 ||
      password_ui_accepted_count > password_ui_dismissed_count ||
      ios_credential_extension_enabled >= 1) {
    result = 1;
  }

  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
      FROM_HERE,
      base::BindOnce(std::move(callback), ModelProvider::Response(1, result)));
}

}  // namespace segmentation_platform
