// Copyright 2025 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/extensions/api/autofill_private/autofill_ai_util.h"

#include <optional>
#include <string>
#include <string_view>
#include <utility>

#include "base/containers/flat_set.h"
#include "base/containers/span.h"
#include "base/containers/to_vector.h"
#include "base/feature_list.h"
#include "base/notimplemented.h"
#include "base/notreached.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/common/extensions/api/autofill_private.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/data_model/addresses/autofill_structured_address_component.h"
#include "components/autofill/core/browser/data_model/autofill_ai/entity_instance.h"
#include "components/autofill/core/browser/data_model/autofill_ai/entity_type.h"
#include "components/autofill/core/browser/data_model/autofill_ai/entity_type_names.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/integrators/autofill_ai/autofill_ai_labels.h"
#include "components/autofill/core/browser/integrators/autofill_ai/autofill_ai_wallet_utils.h"
#include "components/autofill/core/browser/integrators/autofill_ai/management_utils.h"
#include "components/autofill/core/browser/proto/server.pb.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"

namespace autofill_private = extensions::api::autofill_private;

namespace extensions::autofill_ai_util {

namespace {

using autofill::AttributeInstance;
using autofill::AttributeType;
using autofill::AttributeTypeName;
using autofill::AutofillFormatString;
using autofill::EntityInstance;
using autofill::EntityType;
using autofill::EntityTypeName;

// Converts a vector of `autofill::EntityInstance` objects into a vector of
// `api::autofill_private::EntityInstanceWithLabels`, according to a
// given `app_locale`. Appends the result to `output`.
// This function works as follows:
//
// 1. Retrieve all available labels for each entity using
//    `GetLabelsForEntities()`.
//
// 2. Creates for each entity an
//    `api::autofill_private::EntityInstanceWithLabels`,
//    setting its label (first line in the settings page list) as the entity
//    name, and its sublabel (second line) as the concatenation of the final
//    list of labels defined in 3#. Appends
//    `api::autofill_private::EntityInstanceWithLabels` to the output.
void EntityInstanceToPrivateApiEntityInstanceWithLabels(
    base::span<const EntityInstance*> entity_instances,
    std::string_view app_locale,
    bool obfuscate_sensitive_types,
    std::vector<autofill_private::EntityInstanceWithLabels>& output) {
  // Step 1#, get all available labels for `entity_instances`.
  const std::vector<autofill::EntityLabel> labels_for_entities =
      autofill::GetLabelsForEntities(
          entity_instances,
          /*attribute_types_to_ignore=*/{},
          /*only_disambiguating_types=*/false,
          /*obfuscate_sensitive_types=*/obfuscate_sensitive_types, app_locale);

  // Step 2#
  // Update the `output` with each entity's respective
  // `autofill_private::EntityInstanceWithLabels`, making sure to set the label
  //  and sublabels. In the context of the settings page,
  // `autofill_private::EntityInstanceWithLabels::entity_instance_label` is the
  // first line of each entities list (the equivalent of a filling suggestion
  // main text) while
  // `autofill_private::EntityInstanceWithLabels::entity_instance_sub_label` is
  // the second line.
  std::vector<autofill_private::EntityInstanceWithLabels>
      entities_instances_with_labels;
  CHECK_EQ(entity_instances.size(), labels_for_entities.size());
  for (size_t i = 0; i < entity_instances.size(); i++) {
    const EntityInstance& entity_instance = *entity_instances[i];
    autofill_private::EntityInstanceWithLabels& entity_instance_with_labels =
        output.emplace_back();
    entity_instance_with_labels.guid = *entity_instance.guid();

    // TODO(crbug.com/477845712): Pass a proper value for
    // supports_wallet_storage. Currently, the value doesn't matter, because
    // EntityInstanceWithLabels is only used for displaying the list of existing
    // entities, which are already stored either locally or in Wallet.
    entity_instance_with_labels.type = EntityTypeToPrivateApiEntityType(
        entity_instance.type(), /*supports_wallet_storage=*/false);
    entity_instance_with_labels.entity_instance_label =
        base::UTF16ToUTF8(entity_instance.type().GetNameForI18n());
    entity_instance_with_labels.entity_instance_sub_label = base::UTF16ToUTF8(
        base::JoinString(labels_for_entities[i], autofill::kLabelSeparator));
    entity_instance_with_labels.stored_in_wallet =
        entity_instance.record_type() ==
        EntityInstance::RecordType::kServerWallet;
    if (entity_instance_with_labels.stored_in_wallet) {
      entity_instance_with_labels.wallet_entity_url =
          autofill::GetWalletManagementURL(entity_instance);
    }
    entity_instance_with_labels.is_read_only =
        entity_instance.are_attributes_read_only().value();
  }
}

}  // namespace

api::autofill_private::AttributeTypeDataType
AttributeTypeDataTypeToPrivateApiAttributeTypeDataType(
    AttributeType::DataType data_type) {
  switch (data_type) {
    case AttributeType::DataType::kCountry:
      return autofill_private::AttributeTypeDataType::kCountry;
    case AttributeType::DataType::kDate:
      return autofill_private::AttributeTypeDataType::kDate;
    case AttributeType::DataType::kName:
    case AttributeType::DataType::kState:
    case AttributeType::DataType::kString:
      return autofill_private::AttributeTypeDataType::kString;
  }
  NOTREACHED();
}

std::optional<EntityInstance> PrivateApiEntityInstanceToEntityInstance(
    const autofill_private::EntityInstance& private_api_entity_instance,
    std::string_view app_locale,
    bool entity_supports_wallet_storage) {
  base::flat_set<AttributeInstance, AttributeInstance::CompareByType>
      attribute_instances;
  for (const autofill_private::AttributeInstance&
           private_api_attribute_instance :
       private_api_entity_instance.attribute_instances) {
    if (private_api_attribute_instance.type.type_name < 0 ||
        private_api_attribute_instance.type.type_name >
            std::to_underlying(AttributeTypeName::kMaxValue)) {
      return std::nullopt;
    }

    AttributeType attribute_type(
        AttributeTypeName(private_api_attribute_instance.type.type_name));
    AttributeInstance attribute_instance(std::move(attribute_type));

    if (attribute_instance.type().data_type() ==
        AttributeType::DataType::kDate) {
      const std::optional<autofill_private::DateValue>& date =
          private_api_attribute_instance.value.as_date_value;
      if (!date.has_value()) {
        return std::nullopt;
      }
      if (date->month.empty() || date->day.empty() || date->year.empty()) {
        return std::nullopt;
      }

      attribute_instance.SetInfo(
          attribute_instance.type().field_type(),
          base::UTF8ToUTF16(date->month), app_locale,
          AutofillFormatString(u"M", autofill::FormatString_Type_DATE),
          autofill::VerificationStatus::kUserVerified);
      attribute_instance.SetInfo(
          attribute_instance.type().field_type(), base::UTF8ToUTF16(date->day),
          app_locale,
          AutofillFormatString(u"D", autofill::FormatString_Type_DATE),
          autofill::VerificationStatus::kUserVerified);
      attribute_instance.SetInfo(
          attribute_instance.type().field_type(), base::UTF8ToUTF16(date->year),
          app_locale,
          AutofillFormatString(u"YYYY", autofill::FormatString_Type_DATE),
          autofill::VerificationStatus::kUserVerified);
    } else {
      if (!private_api_attribute_instance.value.as_string.has_value()) {
        return std::nullopt;
      }
      attribute_instance.SetRawInfo(
          attribute_instance.type().field_type(),
          base::UTF8ToUTF16(
              private_api_attribute_instance.value.as_string.value()),
          autofill::VerificationStatus::kUserVerified);
    }
    attribute_instance.FinalizeInfo();
    attribute_instances.emplace(std::move(attribute_instance));
  }

  std::optional<EntityTypeName> entity_type_name =
      autofill::ToSafeEntityTypeName(
          private_api_entity_instance.type.type_name);
  if (!entity_type_name.has_value()) {
    return std::nullopt;
  }
  EntityType entity_type(entity_type_name.value());
  // Newly added entity instances need to have a guid generated for them.
  EntityInstance::EntityId guid(
      private_api_entity_instance.guid.empty()
          ? base::Uuid::GenerateRandomV4().AsLowercaseString()
          : private_api_entity_instance.guid);

  const bool save_entity_to_wallet =
      private_api_entity_instance.stored_in_wallet.value_or(false) &&
      entity_supports_wallet_storage;

  EntityInstance::RecordTypeData record_type_data =
      save_entity_to_wallet ? EntityInstance::RecordTypeData(
                                  EntityInstance::WalletRecordTypePayload{})
                            : EntityInstance::RecordTypeData(
                                  EntityInstance::LocalRecordTypePayload{});

  return EntityInstance(
      std::move(entity_type), attribute_instances, std::move(guid),
      private_api_entity_instance.nickname, base::Time::Now(), /*use_count=*/0,
      /*use_date=*/base::Time::Now(), std::move(record_type_data),
      EntityInstance::AreAttributesReadOnly(
          private_api_entity_instance.is_read_only.value_or(false)),
      /*frecency_override=*/"");
}

autofill_private::EntityInstance EntityInstanceToPrivateApiEntityInstance(
    const EntityInstance& entity_instance,
    std::string_view app_locale,
    bool entity_supports_wallet_storage) {
  std::vector<autofill_private::AttributeInstance>
      private_api_attribute_instances;
  bool should_authenticate_to_view = false;
  for (const AttributeInstance& attribute_instance :
       entity_instance.attributes()) {
    private_api_attribute_instances.emplace_back();
    private_api_attribute_instances.back().type.type_name =
        std::to_underlying(attribute_instance.type().name());
    private_api_attribute_instances.back().type.type_name_as_string =
        base::UTF16ToUTF8(attribute_instance.type().GetNameForI18n());

    if (attribute_instance.type().is_obfuscated() &&
        !attribute_instance.GetCompleteRawInfo().empty()) {
      should_authenticate_to_view = true;
    }

    AttributeType::DataType data_type = attribute_instance.type().data_type();
    private_api_attribute_instances.back().type.data_type =
        AttributeTypeDataTypeToPrivateApiAttributeTypeDataType(data_type);

    if (data_type == AttributeType::DataType::kDate) {
      std::optional<autofill::FieldType> field_type =
          attribute_instance.type().field_type();
      base::DictValue date_value;
      date_value.SetByDottedPath(
          "month",
          base::UTF16ToUTF8(attribute_instance.GetInfo(
              field_type, app_locale,
              AutofillFormatString(u"M", autofill::FormatString_Type_DATE))));
      date_value.SetByDottedPath(
          "day",
          base::UTF16ToUTF8(attribute_instance.GetInfo(
              field_type, app_locale,
              AutofillFormatString(u"D", autofill::FormatString_Type_DATE))));
      date_value.SetByDottedPath(
          "year", base::UTF16ToUTF8(attribute_instance.GetInfo(
                      field_type, app_locale,
                      AutofillFormatString(u"YYYY",
                                           autofill::FormatString_Type_DATE))));
      autofill_private::AttributeInstance::Value::Populate(
          base::Value(std::move(date_value)),
          private_api_attribute_instances.back().value);
    } else {
      autofill_private::AttributeInstance::Value::Populate(
          base::Value(base::UTF16ToUTF8(
              attribute_instance.GetCompleteInfo(app_locale))),
          private_api_attribute_instances.back().value);
    }
  }

  autofill_private::EntityInstance private_api_entity_instance;
  private_api_entity_instance.type = EntityTypeToPrivateApiEntityType(
      entity_instance.type(), entity_supports_wallet_storage);
  private_api_entity_instance.attribute_instances =
      std::move(private_api_attribute_instances);
  private_api_entity_instance.guid = *entity_instance.guid();
  private_api_entity_instance.nickname = entity_instance.nickname();
  private_api_entity_instance.should_authenticate_to_view =
      should_authenticate_to_view;
  private_api_entity_instance.stored_in_wallet =
      (entity_instance.record_type() ==
       EntityInstance::RecordType::kServerWallet);
  private_api_entity_instance.is_read_only =
      entity_instance.are_attributes_read_only().value();
  return private_api_entity_instance;
}

std::vector<autofill_private::EntityInstanceWithLabels>
EntityInstancesToPrivateApiEntityInstancesWithLabels(
    base::span<const EntityInstance> entity_instances,
    bool obfuscate_sensitive_types,
    std::string_view app_locale) {
  // Entity labels should be generated based on other entities of the same
  // type. This is because the disambiguation values of attributes are only
  // relevant inside a specific entity type.
  std::map<EntityType, std::vector<const EntityInstance*>> entities_per_type;
  for (const EntityInstance& entity : entity_instances) {
    entities_per_type[entity.type()].push_back(&entity);
  }
  std::vector<autofill_private::EntityInstanceWithLabels> response;
  response.reserve(entity_instances.size());
  for (auto& [entity_type, entities] : entities_per_type) {
    EntityInstanceToPrivateApiEntityInstanceWithLabels(
        entities, app_locale, obfuscate_sensitive_types, response);
  }
  return response;
}

api::autofill_private::EntityType EntityTypeToPrivateApiEntityType(
    EntityType entity_type,
    bool supports_wallet_storage) {
  autofill_private::EntityType api_type;
  api_type.type_name = std::to_underlying(entity_type.name());
  api_type.type_name_as_string =
      base::UTF16ToUTF8(entity_type.GetNameForI18n());
  api_type.add_entity_type_string = autofill::GetAddEntityTypeStringForI18n(
      entity_type,
      /*is_wallet_branded=*/supports_wallet_storage &&
          base::FeatureList::IsEnabled(
              autofill::features::kAutofillAiWalletPassBranding2026));
  api_type.edit_entity_type_string =
      autofill::GetEditEntityTypeStringForI18n(entity_type);
  api_type.delete_entity_type_string =
      autofill::GetDeleteEntityTypeStringForI18n(entity_type);
  api_type.supports_wallet_storage = supports_wallet_storage;
  switch (entity_type.name()) {
    case EntityTypeName::kPassport:
    case EntityTypeName::kDriversLicense:
    case EntityTypeName::kNationalIdCard:
    case EntityTypeName::kKnownTravelerNumber:
    case EntityTypeName::kRedressNumber:
      api_type.pass_type = autofill_private::EntityPassType::kPrivatePass;
      break;
    case EntityTypeName::kVehicle:
    case EntityTypeName::kFlightReservation:
      api_type.pass_type = autofill_private::EntityPassType::kPublicPass;
      break;
    case EntityTypeName::kOrder:
    case EntityTypeName::kShipment:
      // pass_type is unset for non-pass type entities.
      break;
  }
  return api_type;
}

api::autofill_private::AttributeType AttributeTypeToPrivateApiAttributeType(
    autofill::AttributeType attribute_type) {
  api::autofill_private::AttributeType api_attr;
  api_attr.type_name = std::to_underlying(attribute_type.name());
  api_attr.type_name_as_string =
      base::UTF16ToUTF8(attribute_type.GetNameForI18n());
  api_attr.data_type = AttributeTypeDataTypeToPrivateApiAttributeTypeDataType(
      attribute_type.data_type());
  return api_attr;
}

std::vector<api::autofill_private::AttributeType> GetRequiredAttributesForType(
    autofill::EntityType entity_type) {
  // Read-only entity types cannot be created or updated in the UI, so they
  // do not have required fields in the UI context. Bypassing them also
  // prevents hitting a CHECK on complex constraints (e.g. for Orders/Shipments)
  // which the UI cannot represent.
  if (entity_type.read_only()) {
    return {};
  }

  return base::ToVector(entity_type.import_constraints(), [](autofill::DenseSet<
                                                              AttributeType>
                                                                 group) {
    // It was decided to keep the schema expressive to allow future complex
    // constraints, rather than restricting it to match current UI
    // capabilities. Consequently, this code enforces the current UI limitation
    // (simple disjunctions only) via runtime checks.
    // Ex. "[[a]]", "[[a], [b]]", "[[a], [b], [c]]" etc are supported in UI.
    // More details here: crrev.com/c/7245980
    CHECK_EQ(group.size(), 1u)
        << "Unsupported format: Complex constraint groups not supported by UI";

    // Flatten the constraints: {{A}, {B}} -> [A, B]
    return AttributeTypeToPrivateApiAttributeType(*group.begin());
  });
}

}  // namespace extensions::autofill_ai_util
