// Copyright 2013 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/autofill/core/browser/webdata/autofill_webdata_backend_impl.h"

#include <stddef.h>
#include <stdint.h>

#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "base/check.h"
#include "base/check_op.h"
#include "base/debug/alias.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/ref_counted_delete_on_sequence.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/observer_list.h"
#include "base/supports_user_data.h"
#include "base/task/sequenced_task_runner.h"
#include "build/buildflag.h"
#include "components/autofill/core/browser/data_model/addresses/autofill_profile.h"
#include "components/autofill/core/browser/data_model/autofill_ai/entity_instance.h"
#include "components/autofill/core/browser/data_model/payments/autofill_offer_data.h"
#include "components/autofill/core/browser/data_model/payments/autofill_wallet_usage_data.h"
#include "components/autofill/core/browser/data_model/payments/bank_account.h"
#include "components/autofill/core/browser/data_model/payments/credit_card.h"
#include "components/autofill/core/browser/data_model/payments/credit_card_benefit.h"
#include "components/autofill/core/browser/data_model/payments/credit_card_cloud_token_data.h"
#include "components/autofill/core/browser/data_model/payments/iban.h"
#include "components/autofill/core/browser/data_model/payments/payments_metadata.h"
#include "components/autofill/core/browser/data_model/valuables/loyalty_card.h"
#include "components/autofill/core/browser/data_model/valuables/valuable_types.h"
#include "components/autofill/core/browser/payments/payments_customer_data.h"
#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
#include "components/autofill/core/browser/webdata/autocomplete/autocomplete_entry.h"
#include "components/autofill/core/browser/webdata/autocomplete/autocomplete_entry_label_sensitive.h"
#include "components/autofill/core/browser/webdata/autocomplete/autocomplete_table.h"
#include "components/autofill/core/browser/webdata/autocomplete/autocomplete_table_label_sensitive.h"
#include "components/autofill/core/browser/webdata/autofill_ai/entity_table.h"
#include "components/autofill/core/browser/webdata/autofill_change.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h"
#include "components/autofill/core/browser/webdata/payments/payments_autofill_table.h"
#include "components/autofill/core/browser/webdata/payments/server_cvc.h"
#include "components/autofill/core/browser/webdata/valuables/valuables_table.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/dense_set.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/sync/base/data_type.h"
#include "components/sync/protocol/autofill_specifics.pb.h"
#include "components/webdata/common/web_data_results.h"
#include "components/webdata/common/web_database_backend.h"
#include "sql/database.h"

namespace autofill {

namespace {

// Do not modify the order or values of these elements as they are reported
// as metrics. This is represented as AutofillWebDataBackendImplOperationResult
// in enums.xml.
// The ID space is a bit stretched out to enable adding new failure testing
// inside functions where needed without destroying the order of elements.
// LINT.IfChange(Result) -->
enum class Result {
  kAddFormElements_Success = 0,
  kAddFormElements_Failure = 1,
  kRemoveFormElementsAddedBetween_Success = 10,
  kRemoveFormElementsAddedBetween_Failure = 11,
  kRemoveFormValueForElementName_Success = 20,
  kRemoveFormValueForElementName_Failure = 22,
  kAddAutofillProfile_Success = 30,
  kAddAutofillProfile_Failure = 31,
  kAddAutofillProfile_GetFailure = 32,
  kUpdateAutofillProfile_Success = 40,
  kUpdateAutofillProfile_ReadFailure = 41,
  kUpdateAutofillProfile_WriteFailure = 42,
  kUpdateAutofillProfile_GetFailure = 43,
  kRemoveAutofillProfile_Success = 50,
  kRemoveAutofillProfile_ReadFailure = 51,
  kRemoveAutofillProfile_WriteFailure = 52,
  kUpdateAutocompleteEntries_Success = 60,
  kUpdateAutocompleteEntries_Failure = 61,
  kAddCreditCard_Success = 70,
  kAddCreditCard_Failure = 71,
  kUpdateCreditCard_Success = 80,
  kUpdateCreditCard_ReadFailure = 81,
  kUpdateCreditCard_WriteFailure = 82,
  kRemoveCreditCard_Success = 90,
  kRemoveCreditCard_ReadFailure = 91,
  kRemoveCreditCard_WriteFailure = 92,
  kAddServerCreditCardForTesting_Success = 100,
  kAddServerCreditCardForTesting_Failure = 101,
  kUnmaskServerCreditCard_Success = 110,
  kUnmaskServerCreditCard_Failure = 111,
  kMaskServerCreditCard_Success = 120,
  kMaskServerCreditCard_Failure = 121,
  kUpdateServerCardMetadata_Success = 130,
  kUpdateServerCardMetadata_Failure = 131,
  kAddIban_Success = 140,
  kAddIban_Failure = 141,
  kUpdateIban_Success = 150,
  kUpdateIban_ReadFailure = 151,
  kUpdateIban_WriteFailure = 152,
  kRemoveIban_Success = 160,
  kRemoveIban_ReadFailure = 161,
  kRemoveIban_WriteFailure = 162,
  // Server addresses metadata updates (170, 171) are deprecated.
  kAddUpiId_Success = 180,
  kAddUpiId_Failure = 181,
  kClearAllServerData_Success = 190,
  kClearAllServerData_Failure = 191,
  // Clearing of local data (200, 201) is deprecated.
  kRemoveAutofillDataModifiedBetween_Success = 210,
  kRemoveAutofillDataModifiedBetween_Failure = 211,
  kRemoveOriginURLsModifiedBetween_Success = 220,
  kRemoveOriginURLsModifiedBetween_Failure = 221,
  kAddServerCvc_Success = 230,
  kAddServerCvc_Failure = 231,
  kUpdateServerCvc_Success = 240,
  kUpdateServerCvc_Failure = 241,
  kRemoveServerCvc_Success = 250,
  kRemoveServerCvc_Failure = 251,
  kClearServerCvcs_Success = 260,
  kClearServerCvcs_Failure = 261,
  kUpdateCreditCardCvc_Success = 270,
  kUpdateCreditCardCvc_Failure = 271,
  kClearLocalCvcs_Success = 272,
  kClearLocalCvcs_Failure = 273,
  kUpdateServerIbanMetadata_Success = 274,
  kUpdateServerIbanMetadata_Failure = 275,
  kClearAllCreditCardBenefits_Success = 276,
  kClearAllCreditCardBenefits_Failure = 277,
  // Adding-but-not-updating entity instances (280, 281) is deprecated.
  kAddOrUpdateEntityInstance_Success = 290,
  kAddOrUpdateEntityInstance_Failure = 291,
  kRemoveEntityInstance_Success = 300,
  kRemoveEntityInstance_Failure = 301,
  kRemoveEntityInstancesModifiedBetween_Success = 310,
  kRemoveEntityInstancesModifiedBetween_Failure = 311,
  // Clearing local CVCs up to May 2025 (312, 313) is deprecated.
  kCleanupForCrbug445879524_Success = 314,
  kCleanupForCrbug445879524_Failure = 315,
  kAddOrUpdateValuableMetadata_Success = 316,
  kAddOrUpdateValuableMetadata_Failure = 317,
  kMaxValue = kAddOrUpdateValuableMetadata_Failure,
};
// LINT.ThenChange(/tools/metrics/histograms/metadata/autofill/enums.xml:AutofillWebDataBackendImplOperationResult)

// Reports the success or failure of various operations on the database via UMA.
//
// Unit tests live in web_data_service_unittest.cc.
void ReportResult(Result result) {
  base::UmaHistogramEnumeration(
      "WebDatabase.AutofillWebDataBackendImpl.OperationResult", result);
}

bool SupportsSyncingEntityMetadata(const EntityInstance& entity_instance) {
  switch (entity_instance.record_type()) {
    case EntityInstance::RecordType::kServerWallet:
      return true;
    case EntityInstance::RecordType::kLocal:
    case EntityInstance::RecordType::kPersonalContext:
      return false;
  }
}

}  // namespace

AutofillWebDataBackendImpl::AutofillWebDataBackendImpl(
    scoped_refptr<WebDatabaseBackend> web_database_backend,
    scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
    scoped_refptr<base::SequencedTaskRunner> db_task_runner)
    : base::RefCountedDeleteOnSequence<AutofillWebDataBackendImpl>(
          std::move(db_task_runner)),
      ui_task_runner_(std::move(ui_task_runner)),
      web_database_backend_(std::move(web_database_backend)) {
  DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
  this_during_ui_lifecycle_ = weak_ptr_factory_for_ui_lifecycle_.GetWeakPtr();
}

AutofillWebDataBackendImpl::~AutofillWebDataBackendImpl() {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
}

void AutofillWebDataBackendImpl::ShutdownOnUISequence() {
  DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
  weak_ptr_factory_for_ui_lifecycle_.InvalidateWeakPtrsAndDoom();
  DCHECK(!this_during_ui_lifecycle_);

  // We want to destroy the user-data ownees (i.e., the sync bridges) before
  // GetDatabase() becomes null to avoid nullptr dereferences.
  //
  // The below hack achieves that for the following reason.
  //
  // WebDataServiceWrapper::Shutdown() calls
  // - AutofillWebDataBackendImpl::ShutdownOnUISequence() and
  // - WebDatabaseService::ShutdownDatabase().
  //
  // Both functions post a task to the DB sequence:
  // - AutofillWebDataBackendImpl::ShutdownOnUISequence() destroys
  //   the sync bridges.
  // - WebDatabaseService::ShutdownDatabase() resets the database.
  //
  // Since those tasks are posted to a sequenced task runner, they're executed
  // in that order.
  //
  // Since this is the only callpath that resets the database, our hack ensures
  // that the sync bridges are destroyed before GetDatabase() becomes nullptr.
  //
  // See crbug.com/474706752#comment21 for details.
  //
  // If this hack is removed, the ~AutofillWebDataService() must explicitly call
  // `user_data_.ClearAllUserData()` because the sync bridges may call into
  // `this` during their destruction.
  owning_task_runner()->PostTask(
      FROM_HERE, BindOnce(
                     [](scoped_refptr<AutofillWebDataBackendImpl> self) {
                       self->user_data_.ClearAllUserData();
                     },
                     scoped_refptr(this)));
}

void AutofillWebDataBackendImpl::AddObserver(
    AutofillWebDataServiceObserverOnDBSequence* observer) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  db_observer_list_.AddObserver(observer);
}

void AutofillWebDataBackendImpl::RemoveObserver(
    AutofillWebDataServiceObserverOnDBSequence* observer) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  db_observer_list_.RemoveObserver(observer);
}

void AutofillWebDataBackendImpl::AddObserver(
    AutofillWebDataServiceObserverOnUISequence* observer) {
  DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
  ui_observer_list_.AddObserver(observer);
}

void AutofillWebDataBackendImpl::RemoveObserver(
    AutofillWebDataServiceObserverOnUISequence* observer) {
  DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
  ui_observer_list_.RemoveObserver(observer);
}

WebDatabase* AutofillWebDataBackendImpl::GetDatabase() {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  return web_database_backend_->database();
}

void AutofillWebDataBackendImpl::CommitChanges() {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  // TODO(crbug.com/410101523): Investigate the transaction errors. There should
  // always be a pending sql transaction.
  sql::Database* sql = web_database_backend_->database()->GetSQLConnection();
  base::debug::Alias(&sql);
  DCHECK(sql->is_open());
  DCHECK(sql->HasActiveTransactions());
  web_database_backend_->database()->CommitTransaction();
  web_database_backend_->database()->BeginTransaction();
}

std::unique_ptr<WDTypedResult>
AutofillWebDataBackendImpl::RemoveExpiredAutocompleteEntries(WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  AutocompleteChangeList changes;
  const bool old_table_write_success =
      AutocompleteTable::FromWebDatabase(db)->RemoveExpiredFormElements(
          changes);
  if (old_table_write_success && !changes.empty()) {
    // Post the notifications including the list of affected keys.
    // This is sent here so that work resulting from this notification
    // will be done on the DB sequence, and not the UI sequence.
    for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
         db_observer_list_) {
      db_observer.AutocompleteEntriesChanged(changes);
    }
  }

  if (base::FeatureList::IsEnabled(
          features::kAutofillLabelSensitiveAutocomplete)) {
    const bool new_table_write_success =
        AutocompleteTableLabelSensitive::FromWebDatabase(db)
            ->RemoveExpiredFormElements();
    return std::make_unique<WDResult<bool>>(
        AUTOFILL_CLEANUP_RESULT,
        new_table_write_success && old_table_write_success);
  } else {
    return std::make_unique<WDResult<bool>>(AUTOFILL_CLEANUP_RESULT,
                                            old_table_write_success);
  }
}

void AutofillWebDataBackendImpl::NotifyOfAutofillProfileChanged(
    const AutofillProfileChange& change) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());

  // DB sequence notification.
  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.AutofillProfileChanged(change);
  }
}

void AutofillWebDataBackendImpl::NotifyOfCreditCardChanged(
    const CreditCardChange& change) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());

  // DB sequence notification.
  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.CreditCardChanged(change);
  }
}

void AutofillWebDataBackendImpl::NotifyOfIbanChanged(const IbanChange& change) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());

  // DB sequence notification.
  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.IbanChanged(change);
  }
}

void AutofillWebDataBackendImpl::NotifyOnAutofillChangedBySync(
    syncer::DataType data_type) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  ui_task_runner_->PostTask(
      FROM_HERE, base::BindOnce(
                     [](base::WeakPtr<AutofillWebDataBackendImpl> self,
                        syncer::DataType data_type) {
                       if (!self) {
                         return;
                       }
                       for (auto& ui_observer : self->ui_observer_list_) {
                         ui_observer.OnAutofillChangedBySync(data_type);
                       }
                     },
                     this_during_ui_lifecycle_, data_type));
}

void AutofillWebDataBackendImpl::NotifyOnServerCvcChanged(
    const ServerCvcChange& change) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());

  // DB sequence notification.
  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.ServerCvcChanged(change);
  }
}

void AutofillWebDataBackendImpl::NotifyOnEntityInstanceChanged(
    const EntityInstanceChange& change) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());

  // DB sequence notification.
  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.EntityInstanceChanged(change);
  }

  // Notify about potential server metadata changes.
  if (SupportsSyncingEntityMetadata(change.data_model())) {
    EntityInstanceMetadataChange::Type metadata_change_type = [&] {
      switch (change.type()) {
        case EntityInstanceChange::ADD:
          return EntityInstanceMetadataChange::ADD;
        case EntityInstanceChange::UPDATE:
          return EntityInstanceMetadataChange::UPDATE;
        case EntityInstanceChange::REMOVE:
          return EntityInstanceMetadataChange::REMOVE;
        case EntityInstanceChange::HIDE_IN_AUTOFILL:
          return EntityInstanceMetadataChange::HIDE_IN_AUTOFILL;
      }
      NOTREACHED();
    }();

    NotifyOnServerEntityMetadataChanged(EntityInstanceMetadataChange(
        metadata_change_type, change.key(), change.data_model().metadata()));
  }
}

void AutofillWebDataBackendImpl::NotifyOnServerEntityMetadataChanged(
    const EntityInstanceMetadataChange& change) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());

  // DB sequence notification.
  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.ServerEntityInstanceMetadataChanged(change);
  }
}

base::SupportsUserData& AutofillWebDataBackendImpl::GetDBUserData() {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  return user_data_;
}

WebDatabase::State AutofillWebDataBackendImpl::AddFormElements(
    const std::vector<FormFieldData>& fields,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  AutocompleteChangeList changes;
  const bool old_table_write_success =
      AutocompleteTable::FromWebDatabase(db)->AddFormFieldValues(fields,
                                                                 &changes);
  if (old_table_write_success) {
    // Post the notifications including the list of affected keys.
    // This is sent here so that work resulting from this notification will be
    // done on the DB sequence, and not the UI sequence.
    for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
         db_observer_list_) {
      db_observer.AutocompleteEntriesChanged(changes);
    }
  }

  const bool new_table_write_successful_or_not_needed =
      !base::FeatureList::IsEnabled(
          features::kAutofillLabelSensitiveAutocomplete) ||
      AutocompleteTableLabelSensitive::FromWebDatabase(db)->AddFormFieldValues(
          fields);

  if (old_table_write_success && new_table_write_successful_or_not_needed) {
    ReportResult(Result::kAddFormElements_Success);
    return WebDatabase::COMMIT_NEEDED;
  } else {
    ReportResult(Result::kAddFormElements_Failure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }
}

// TODO(crbug.com/507327886): Remove after feature launch.
std::unique_ptr<WDTypedResult>
AutofillWebDataBackendImpl::GetFormValuesForElementName(
    const std::u16string& name,
    const std::u16string& prefix,
    int limit,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::vector<AutocompleteEntry> entries;
  AutocompleteTable::FromWebDatabase(db)->GetFormValuesForElementName(
      name, prefix, limit, entries);
  return std::make_unique<WDResult<std::vector<AutocompleteEntry>>>(
      AUTOFILL_VALUE_RESULT, entries);
}

std::unique_ptr<WDTypedResult>
AutofillWebDataBackendImpl::GetFormValuesForElementNameAndLabel(
    std::u16string_view name,
    std::u16string_view label,
    std::u16string_view prefix,
    int limit,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::vector<AutocompleteSearchResultLabelSensitive> entries;
  const bool get_form_values_success =
      AutocompleteTableLabelSensitive::FromWebDatabase(db)
          ->GetFormValuesForElementNameAndLabel(name, label, prefix, limit,
                                                entries);
  DCHECK(get_form_values_success);
  return std::make_unique<
      WDResult<std::vector<AutocompleteSearchResultLabelSensitive>>>(
      AUTOCOMPLETE_SEARCH_RESULT, std::move(entries));
}

WebDatabase::State AutofillWebDataBackendImpl::RemoveFormElementsAddedBetween(
    base::Time delete_begin,
    base::Time delete_end,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  AutocompleteChangeList changes;
  const bool old_table_write_success =
      AutocompleteTable::FromWebDatabase(db)->RemoveFormElementsAddedBetween(
          delete_begin, delete_end, changes);
  if (old_table_write_success && !changes.empty()) {
    // Post the notifications including the list of affected keys.
    // This is sent here so that work resulting from this notification
    // will be done on the DB sequence, and not the UI sequence.
    for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
         db_observer_list_) {
      db_observer.AutocompleteEntriesChanged(changes);
    }
  }

  const bool new_table_write_successful_or_not_needed =
      !base::FeatureList::IsEnabled(
          features::kAutofillLabelSensitiveAutocomplete) ||
      AutocompleteTableLabelSensitive::FromWebDatabase(db)
          ->RemoveFormElementsAddedBetween(delete_begin, delete_end);

  if (old_table_write_success && new_table_write_successful_or_not_needed) {
    ReportResult(Result::kRemoveFormElementsAddedBetween_Success);
    return WebDatabase::COMMIT_NEEDED;
  } else {
    ReportResult(Result::kRemoveFormElementsAddedBetween_Failure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }
}

WebDatabase::State
AutofillWebDataBackendImpl::RemoveFormValueForElementNameAndLabel(
    std::u16string_view name,
    std::u16string_view label,
    std::u16string_view value,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  const bool old_table_write_success =
      AutocompleteTable::FromWebDatabase(db)->RemoveFormElement(
          std::u16string(name), std::u16string(value));
  if (old_table_write_success) {
    AutocompleteChangeList changes;
    changes.push_back(AutocompleteChange(
        AutocompleteChange::REMOVE,
        AutocompleteKey(std::u16string(name), std::u16string(value))));

    // Post the notifications including the list of affected keys.
    for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
         db_observer_list_) {
      db_observer.AutocompleteEntriesChanged(changes);
    }
  }
  const bool new_table_write_successful_or_not_needed =
      !base::FeatureList::IsEnabled(
          features::kAutofillLabelSensitiveAutocomplete) ||
      AutocompleteTableLabelSensitive::FromWebDatabase(db)->RemoveFormElement(
          name, label, value);
  if (old_table_write_success && new_table_write_successful_or_not_needed) {
    ReportResult(Result::kRemoveFormValueForElementName_Success);
    return WebDatabase::COMMIT_NEEDED;
  } else {
    ReportResult(Result::kRemoveFormValueForElementName_Failure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }
}

WebDatabase::State AutofillWebDataBackendImpl::AddAutofillProfile(
    const AutofillProfile& profile,
    base::OnceCallback<void(const AutofillProfileChange&)> on_success,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  AddressAutofillTable* table = AddressAutofillTable::FromWebDatabase(db);
  if (!table->AddAutofillProfile(profile)) {
    ReportResult(Result::kAddAutofillProfile_Failure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  std::optional<AutofillProfile> db_profile =
      table->GetAutofillProfile(profile.guid());
  if (!db_profile) {
    ReportResult(Result::kAddAutofillProfile_GetFailure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }
  // Notify observers.
  // The `db_profile` is not guaranteed to be equivalent to `profile`, since the
  // database might perform operations like `FinalizeAfterImport()`. Notify
  // observers with `db_profile`.
  AutofillProfileChange change(AutofillProfileChange::ADD, profile.guid(),
                               std::move(*db_profile));
  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.AutofillProfileChanged(change);
  }

  ui_task_runner_->PostTask(
      FROM_HERE, base::BindOnce(std::move(on_success), std::move(change)));

  ReportResult(Result::kAddAutofillProfile_Success);
  return WebDatabase::COMMIT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::UpdateAutofillProfile(
    const AutofillProfile& profile,
    base::OnceCallback<void(const AutofillProfileChange&)> on_success,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  AddressAutofillTable* table = AddressAutofillTable::FromWebDatabase(db);
  // Only perform the update if the profile exists.  It is currently
  // valid to try to update a missing profile.  We simply drop the write and
  // the caller will detect this on the next refresh.
  std::optional<AutofillProfile> original_profile =
      table->GetAutofillProfile(profile.guid());
  if (!original_profile) {
    ReportResult(Result::kUpdateAutofillProfile_ReadFailure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }
  if (!table->UpdateAutofillProfile(profile)) {
    ReportResult(Result::kUpdateAutofillProfile_WriteFailure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  std::optional<AutofillProfile> db_profile =
      table->GetAutofillProfile(profile.guid());
  if (!db_profile) {
    ReportResult(Result::kUpdateAutofillProfile_GetFailure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }
  // Notify observers.
  // The `db_profile` is not guaranteed to be equivalent to `profile`, since the
  // database might perform operations like `FinalizeAfterImport()`. Notify
  // observers with `db_profile`.
  AutofillProfileChange change(AutofillProfileChange::UPDATE, profile.guid(),
                               std::move(*db_profile));
  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.AutofillProfileChanged(change);
  }

  ui_task_runner_->PostTask(
      FROM_HERE, base::BindOnce(std::move(on_success), std::move(change)));

  ReportResult(Result::kUpdateAutofillProfile_Success);
  return WebDatabase::COMMIT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::RemoveAutofillProfile(
    const std::string& guid,
    AutofillProfileChange::Type change_type,
    base::OnceCallback<void(const AutofillProfileChange&)> on_success,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  CHECK(change_type == AutofillProfileChange::REMOVE ||
        change_type == AutofillProfileChange::HIDE_IN_AUTOFILL);

  std::optional<AutofillProfile> profile =
      AddressAutofillTable::FromWebDatabase(db)->GetAutofillProfile(guid);
  if (!profile) {
    ReportResult(Result::kRemoveAutofillProfile_ReadFailure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  if (!AddressAutofillTable::FromWebDatabase(db)->RemoveAutofillProfile(guid)) {
    ReportResult(Result::kRemoveAutofillProfile_WriteFailure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  // Notify observers. Even for removals the profile is a necessary part of the
  // AutofillProfileChange, so downstream code an distinguish by RecordType.
  AutofillProfileChange change(change_type, guid, *profile);
  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.AutofillProfileChanged(change);
  }

  ui_task_runner_->PostTask(
      FROM_HERE, base::BindOnce(std::move(on_success), std::move(change)));

  ReportResult(Result::kRemoveAutofillProfile_Success);
  return WebDatabase::COMMIT_NEEDED;
}

std::unique_ptr<WDTypedResult> AutofillWebDataBackendImpl::GetAutofillProfiles(
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::vector<AutofillProfile> profiles;
  AddressAutofillTable::FromWebDatabase(db)->GetAutofillProfiles(
      DenseSet<AutofillProfile::RecordType>::all(), profiles);
  return std::make_unique<WDResult<std::vector<AutofillProfile>>>(
      AUTOFILL_PROFILES_RESULT, std::move(profiles));
}

WebDatabase::State AutofillWebDataBackendImpl::AddOrUpdateEntityInstance(
    EntityInstance entity,
    base::OnceCallback<void(EntityInstanceChange)> on_success,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  EntityTable* table = EntityTable::FromWebDatabase(db);
  EntityInstance::EntityId guid = entity.guid();
  EntityInstanceChange::Type change_type = table->EntityInstanceExists(guid)
                                               ? EntityInstanceChange::UPDATE
                                               : EntityInstanceChange::ADD;

  if (!table->AddOrUpdateEntityInstance(entity)) {
    ReportResult(Result::kAddOrUpdateEntityInstance_Failure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  EntityInstanceChange change(change_type, std::move(guid), std::move(entity));
  NotifyOnEntityInstanceChanged(change);

  ui_task_runner_->PostTask(
      FROM_HERE, base::BindOnce(std::move(on_success), std::move(change)));
  ReportResult(Result::kAddOrUpdateEntityInstance_Success);
  return WebDatabase::COMMIT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::UpdateEntityMetadata(
    const EntityInstance& entity,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  EntityTable* table = EntityTable::FromWebDatabase(db);
  if (!table->AddOrUpdateEntityMetadata(entity.metadata())) {
    ReportResult(Result::kAddOrUpdateEntityInstance_Failure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  if (SupportsSyncingEntityMetadata(entity)) {
    EntityInstanceMetadataChange change(EntityInstanceMetadataChange::UPDATE,
                                        entity.guid(), entity.metadata());
    NotifyOnServerEntityMetadataChanged(change);
  }

  return WebDatabase::COMMIT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::RemoveEntityInstance(
    EntityInstance entity,
    base::OnceCallback<void(EntityInstanceChange)> on_success,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  EntityTable* table = EntityTable::FromWebDatabase(db);
  if (!table->RemoveEntityInstance(entity.guid())) {
    ReportResult(Result::kRemoveEntityInstance_Failure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }
  EntityInstance::EntityId guid = entity.guid();
  // Notify observers.
  EntityInstanceChange change(EntityInstanceChange::REMOVE, std::move(guid),
                              std::move(entity));
  NotifyOnEntityInstanceChanged(change);
  ui_task_runner_->PostTask(
      FROM_HERE, base::BindOnce(std::move(on_success), std::move(change)));
  ReportResult(Result::kRemoveEntityInstance_Success);
  return WebDatabase::COMMIT_NEEDED;
}

WebDatabase::State
AutofillWebDataBackendImpl::RemoveEntityInstancesModifiedBetween(
    base::Time delete_begin,
    base::Time delete_end,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  if (EntityTable::FromWebDatabase(db)->RemoveEntityInstancesModifiedBetween(
          delete_begin, delete_end)) {
    ReportResult(Result::kRemoveEntityInstancesModifiedBetween_Success);
    return WebDatabase::COMMIT_NEEDED;
  }
  ReportResult(Result::kRemoveEntityInstancesModifiedBetween_Failure);
  return WebDatabase::COMMIT_NOT_NEEDED;
}

std::unique_ptr<WDTypedResult> AutofillWebDataBackendImpl::GetEntityInstances(
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  return std::make_unique<WDResult<std::vector<EntityInstance>>>(
      AUTOFILL_ENTITY_INSTANCE_RESULT,
      EntityTable::FromWebDatabase(db)->GetEntityInstances());
}

std::unique_ptr<WDTypedResult> AutofillWebDataBackendImpl::GetLoyaltyCards(
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  return std::make_unique<WDResult<std::vector<LoyaltyCard>>>(
      AUTOFILL_LOYALTY_CARD_RESULT,
      ValuablesTable::FromWebDatabase(db)->GetLoyaltyCards());
}

WebDatabase::State AutofillWebDataBackendImpl::UpdateValuableMetadata(
    const ValuableMetadata& metadata,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  ValuablesTable* table = ValuablesTable::FromWebDatabase(db);
  if (!table->AddOrUpdateValuableMetadata(metadata)) {
    ReportResult(Result::kAddOrUpdateValuableMetadata_Failure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  ValuableMetadataChange change(ValuableMetadataChange::UPDATE,
                                metadata.valuable_id, metadata);
  NotifyOnValuableMetadataChanged(change);

  ReportResult(Result::kAddOrUpdateValuableMetadata_Success);
  return WebDatabase::COMMIT_NEEDED;
}

void AutofillWebDataBackendImpl::NotifyOnValuableMetadataChanged(
    const ValuableMetadataChange& change) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());

  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.ValuableMetadataChanged(change);
  }
}

std::unique_ptr<WDTypedResult>
AutofillWebDataBackendImpl::GetCountOfValuesContainedBetween(base::Time begin,
                                                             base::Time end,
                                                             WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  if (base::FeatureList::IsEnabled(
          features::kAutofillLabelSensitiveAutocomplete)) {
    const int64_t value = AutocompleteTableLabelSensitive::FromWebDatabase(db)
                              ->GetCountOfValuesContainedBetween(begin, end);
    return std::make_unique<WDResult<int64_t>>(INT64_RESULT, value);
  } else {
    const int64_t value = AutocompleteTable::FromWebDatabase(db)
                              ->GetCountOfValuesContainedBetween(begin, end);
    return std::make_unique<WDResult<int64_t>>(INT64_RESULT, value);
  }
}

WebDatabase::State AutofillWebDataBackendImpl::UpdateAutocompleteEntries(
    const std::vector<AutocompleteEntry>& autocomplete_entries,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  if (!AutocompleteTable::FromWebDatabase(db)->UpdateAutocompleteEntries(
          autocomplete_entries)) {
    ReportResult(Result::kUpdateAutocompleteEntries_Failure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  ReportResult(Result::kUpdateAutocompleteEntries_Success);
  return WebDatabase::COMMIT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::AddCreditCard(
    const CreditCard& credit_card,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  if (!PaymentsAutofillTable::FromWebDatabase(db)->AddCreditCard(credit_card)) {
    ReportResult(Result::kAddCreditCard_Failure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.CreditCardChanged(CreditCardChange(
        CreditCardChange::ADD, credit_card.guid(), credit_card));
  }
  ReportResult(Result::kAddCreditCard_Success);
  return WebDatabase::COMMIT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::UpdateCreditCard(
    const CreditCard& credit_card,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  // It is currently valid to try to update a missing profile.  We simply drop
  // the write and the caller will detect this on the next refresh.
  std::unique_ptr<CreditCard> original_credit_card =
      PaymentsAutofillTable::FromWebDatabase(db)->GetCreditCard(
          credit_card.guid());
  if (!original_credit_card) {
    ReportResult(Result::kUpdateCreditCard_ReadFailure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  if (!PaymentsAutofillTable::FromWebDatabase(db)->UpdateCreditCard(
          credit_card)) {
    ReportResult(Result::kUpdateCreditCard_WriteFailure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.CreditCardChanged(CreditCardChange(
        CreditCardChange::UPDATE, credit_card.guid(), credit_card));
  }
  ReportResult(Result::kUpdateCreditCard_Success);
  return WebDatabase::COMMIT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::UpdateLocalCvc(
    const std::string& guid,
    const std::u16string& cvc,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  if (PaymentsAutofillTable::FromWebDatabase(db)->UpdateLocalCvc(guid, cvc)) {
    ReportResult(Result::kUpdateCreditCardCvc_Success);
    return WebDatabase::COMMIT_NEEDED;
  }
  ReportResult(Result::kUpdateCreditCardCvc_Failure);
  return WebDatabase::COMMIT_NOT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::RemoveCreditCard(
    const std::string& guid,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::unique_ptr<CreditCard> card =
      PaymentsAutofillTable::FromWebDatabase(db)->GetCreditCard(guid);
  if (!card) {
    ReportResult(Result::kRemoveCreditCard_ReadFailure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  if (!PaymentsAutofillTable::FromWebDatabase(db)->RemoveCreditCard(guid)) {
    ReportResult(Result::kRemoveCreditCard_WriteFailure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.CreditCardChanged(
        CreditCardChange(CreditCardChange::REMOVE, guid, *card));
  }
  ReportResult(Result::kRemoveCreditCard_Success);
  return WebDatabase::COMMIT_NEEDED;
}

std::unique_ptr<WDTypedResult> AutofillWebDataBackendImpl::GetCreditCards(
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::vector<std::unique_ptr<CreditCard>> credit_cards;
  PaymentsAutofillTable::FromWebDatabase(db)->GetCreditCards(&credit_cards);
  return std::make_unique<WDResult<std::vector<std::unique_ptr<CreditCard>>>>(
      AUTOFILL_CREDITCARDS_RESULT, std::move(credit_cards));
}

std::unique_ptr<WDTypedResult> AutofillWebDataBackendImpl::GetServerCreditCards(
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::vector<std::unique_ptr<CreditCard>> credit_cards;
  PaymentsAutofillTable::FromWebDatabase(db)->GetServerCreditCards(
      credit_cards);
  return std::make_unique<WDResult<std::vector<std::unique_ptr<CreditCard>>>>(
      AUTOFILL_CREDITCARDS_RESULT, std::move(credit_cards));
}

WebDatabase::State AutofillWebDataBackendImpl::UpdateServerCardMetadata(
    const CreditCard& card,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  DCHECK_NE(CreditCard::RecordType::kLocalCard, card.record_type());
  if (!PaymentsAutofillTable::FromWebDatabase(db)
           ->AddOrUpdateServerCardMetadata(card.GetMetadata())) {
    ReportResult(Result::kUpdateServerCardMetadata_Failure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.CreditCardChanged(
        CreditCardChange(CreditCardChange::UPDATE, card.server_id(), card));
  }

  ReportResult(Result::kUpdateServerCardMetadata_Success);
  return WebDatabase::COMMIT_NEEDED;
}

std::unique_ptr<WDTypedResult> AutofillWebDataBackendImpl::GetLocalIbans(
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::vector<std::unique_ptr<Iban>> ibans;
  PaymentsAutofillTable::FromWebDatabase(db)->GetLocalIbans(&ibans);

  return std::make_unique<WDResult<std::vector<std::unique_ptr<Iban>>>>(
      AUTOFILL_IBANS_RESULT, std::move(ibans));
}

std::unique_ptr<WDTypedResult> AutofillWebDataBackendImpl::GetServerIbans(
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::vector<std::unique_ptr<Iban>> ibans;
  PaymentsAutofillTable::FromWebDatabase(db)->GetServerIbans(ibans);
  return std::make_unique<WDResult<std::vector<std::unique_ptr<Iban>>>>(
      AUTOFILL_IBANS_RESULT, std::move(ibans));
}

WebDatabase::State AutofillWebDataBackendImpl::AddLocalIban(const Iban& iban,
                                                            WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  if (!PaymentsAutofillTable::FromWebDatabase(db)->AddLocalIban(iban)) {
    ReportResult(Result::kAddIban_Failure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.IbanChanged(IbanChange(IbanChange::ADD, iban.guid(), iban));
  }
  ReportResult(Result::kAddIban_Success);
  return WebDatabase::COMMIT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::UpdateLocalIban(
    const Iban& iban,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  // It is currently valid to try to update a missing IBAN. We simply drop
  // the write and the caller will detect this on the next refresh.
  std::unique_ptr<Iban> original_iban =
      PaymentsAutofillTable::FromWebDatabase(db)->GetLocalIban(iban.guid());
  if (!original_iban) {
    ReportResult(Result::kUpdateIban_ReadFailure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  if (!PaymentsAutofillTable::FromWebDatabase(db)->UpdateLocalIban(iban)) {
    ReportResult(Result::kUpdateIban_WriteFailure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.IbanChanged(IbanChange(IbanChange::UPDATE, iban.guid(), iban));
  }
  ReportResult(Result::kUpdateIban_Success);
  return WebDatabase::COMMIT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::RemoveLocalIban(
    const std::string& guid,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::unique_ptr<Iban> iban =
      PaymentsAutofillTable::FromWebDatabase(db)->GetLocalIban(guid);
  if (!iban) {
    ReportResult(Result::kRemoveIban_ReadFailure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  if (!PaymentsAutofillTable::FromWebDatabase(db)->RemoveLocalIban(guid)) {
    ReportResult(Result::kRemoveIban_WriteFailure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.IbanChanged(IbanChange(IbanChange::REMOVE, guid, *iban));
  }
  ReportResult(Result::kRemoveIban_Success);
  return WebDatabase::COMMIT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::UpdateServerIbanMetadata(
    const Iban& iban,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  CHECK_EQ(Iban::RecordType::kServerIban, iban.record_type());
  if (!PaymentsAutofillTable::FromWebDatabase(db)
           ->AddOrUpdateServerIbanMetadata(iban.GetMetadata())) {
    ReportResult(Result::kUpdateServerIbanMetadata_Failure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.IbanChanged(
        IbanChange(IbanChange::UPDATE, iban.instrument_id(), iban));
  }

  ReportResult(Result::kUpdateServerIbanMetadata_Success);
  return WebDatabase::COMMIT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::AddServerCvc(
    int64_t instrument_id,
    const std::u16string& cvc,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  const ServerCvc server_cvc{instrument_id, cvc,
                             /*last_updated_timestamp=*/AutofillClock::Now()};
  if (PaymentsAutofillTable::FromWebDatabase(db)->AddServerCvc(server_cvc)) {
    const ServerCvcChange change{ServerCvcChange::ADD, instrument_id,
                                 server_cvc};
    for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
         db_observer_list_) {
      // TODO(crbug.com/40929129): Add integration tests for Add, Remove and
      // Update for Wallet Credential data.
      db_observer.ServerCvcChanged(change);
    }
    ReportResult(Result::kAddServerCvc_Success);
    return WebDatabase::COMMIT_NEEDED;
  }
  ReportResult(Result::kAddServerCvc_Failure);
  return WebDatabase::COMMIT_NOT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::UpdateServerCvc(
    int64_t instrument_id,
    const std::u16string& cvc,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  const ServerCvc server_cvc{instrument_id, cvc,
                             /*last_updated_timestamp=*/AutofillClock::Now()};
  if (PaymentsAutofillTable::FromWebDatabase(db)->UpdateServerCvc(server_cvc)) {
    const ServerCvcChange change{ServerCvcChange::UPDATE, instrument_id,
                                 server_cvc};
    for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
         db_observer_list_) {
      db_observer.ServerCvcChanged(change);
    }
    ReportResult(Result::kUpdateServerCvc_Success);
    return WebDatabase::COMMIT_NEEDED;
  }
  ReportResult(Result::kUpdateServerCvc_Failure);
  return WebDatabase::COMMIT_NOT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::RemoveServerCvc(
    int64_t instrument_id,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  if (PaymentsAutofillTable::FromWebDatabase(db)->RemoveServerCvc(
          instrument_id)) {
    // Remove doesn't require `ServerCvc` struct data, so an empty data is
    // passed to the ServerCvcChange
    const ServerCvcChange change{ServerCvcChange::REMOVE, instrument_id,
                                 ServerCvc{}};
    for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
         db_observer_list_) {
      db_observer.ServerCvcChanged(change);
    }
    ReportResult(Result::kRemoveServerCvc_Success);
    return WebDatabase::COMMIT_NEEDED;
  }
  ReportResult(Result::kRemoveServerCvc_Failure);
  return WebDatabase::COMMIT_NOT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::ClearServerCvcs(
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::vector<std::unique_ptr<ServerCvc>> server_cvc_list =
      PaymentsAutofillTable::FromWebDatabase(db)->GetAllServerCvcs();
  if (PaymentsAutofillTable::FromWebDatabase(db)->ClearServerCvcs()) {
    for (const std::unique_ptr<ServerCvc>& server_cvc_from_list :
         server_cvc_list) {
      // Remove doesn't require `ServerCvc` struct data, so an empty data is
      // passed to the ServerCvcChange
      const ServerCvcChange change{ServerCvcChange::REMOVE,
                                   server_cvc_from_list->instrument_id,
                                   ServerCvc{}};
      for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
           db_observer_list_) {
        db_observer.ServerCvcChanged(change);
      }
    }
    ReportResult(Result::kClearServerCvcs_Success);
    return WebDatabase::COMMIT_NEEDED;
  }
  ReportResult(Result::kClearServerCvcs_Failure);
  return WebDatabase::COMMIT_NOT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::ClearLocalCvcs(WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  if (PaymentsAutofillTable::FromWebDatabase(db)->ClearLocalCvcs()) {
    ReportResult(Result::kClearLocalCvcs_Success);
    return WebDatabase::COMMIT_NEEDED;
  }
  ReportResult(Result::kClearLocalCvcs_Failure);
  return WebDatabase::COMMIT_NOT_NEEDED;
}

#if BUILDFLAG(IS_IOS)
WebDatabase::State AutofillWebDataBackendImpl::CleanupForCrbug445879524(
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  if (PaymentsAutofillTable::FromWebDatabase(db)->CleanupForCrbug445879524()) {
    ReportResult(Result::kCleanupForCrbug445879524_Success);
    return WebDatabase::COMMIT_NEEDED;
  }
  ReportResult(Result::kCleanupForCrbug445879524_Failure);
  return WebDatabase::COMMIT_NOT_NEEDED;
}
#endif  // BUILDFLAG(IS_IOS)

std::unique_ptr<WDTypedResult>
AutofillWebDataBackendImpl::GetPaymentsCustomerData(WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::unique_ptr<PaymentsCustomerData> customer_data;
  PaymentsAutofillTable::FromWebDatabase(db)->GetPaymentsCustomerData(
      customer_data);
  return std::make_unique<WDResult<std::unique_ptr<PaymentsCustomerData>>>(
      AUTOFILL_CUSTOMERDATA_RESULT, std::move(customer_data));
}

std::unique_ptr<WDTypedResult>
AutofillWebDataBackendImpl::GetCreditCardCloudTokenData(WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::vector<std::unique_ptr<CreditCardCloudTokenData>> cloud_token_data;
  PaymentsAutofillTable::FromWebDatabase(db)->GetCreditCardCloudTokenData(
      cloud_token_data);
  return std::make_unique<
      WDResult<std::vector<std::unique_ptr<CreditCardCloudTokenData>>>>(
      AUTOFILL_CLOUDTOKEN_RESULT, std::move(cloud_token_data));
}

std::unique_ptr<WDTypedResult> AutofillWebDataBackendImpl::GetAutofillOffers(
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::vector<std::unique_ptr<AutofillOfferData>> offers;
  PaymentsAutofillTable::FromWebDatabase(db)->GetAutofillOffers(&offers);
  return std::make_unique<
      WDResult<std::vector<std::unique_ptr<AutofillOfferData>>>>(
      AUTOFILL_OFFER_DATA, std::move(offers));
}

std::unique_ptr<WDTypedResult>
AutofillWebDataBackendImpl::GetAutofillVirtualCardUsageData(WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::vector<VirtualCardUsageData> virtual_card_usage_data;
  PaymentsAutofillTable::FromWebDatabase(db)->GetAllVirtualCardUsageData(
      virtual_card_usage_data);
  return std::make_unique<WDResult<std::vector<VirtualCardUsageData>>>(
      AUTOFILL_VIRTUAL_CARD_USAGE_DATA, std::move(virtual_card_usage_data));
}

std::unique_ptr<WDTypedResult>
AutofillWebDataBackendImpl::GetCreditCardBenefits(WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::vector<CreditCardBenefit> credit_card_benefits;
  PaymentsAutofillTable::FromWebDatabase(db)->GetAllCreditCardBenefits(
      credit_card_benefits);
  return std::make_unique<WDResult<std::vector<CreditCardBenefit>>>(
      CREDIT_CARD_BENEFIT_RESULT, std::move(credit_card_benefits));
}

std::unique_ptr<WDTypedResult>
AutofillWebDataBackendImpl::GetMaskedBankAccounts(WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::vector<BankAccount> masked_bank_accounts;
  PaymentsAutofillTable::FromWebDatabase(db)->GetMaskedBankAccounts(
      masked_bank_accounts);
  return std::make_unique<WDResult<std::vector<BankAccount>>>(
      MASKED_BANK_ACCOUNTS_RESULT, std::move(masked_bank_accounts));
}

std::unique_ptr<WDTypedResult>
AutofillWebDataBackendImpl::GetPaymentInstruments(WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::vector<sync_pb::PaymentInstrument> payment_instruments;
  PaymentsAutofillTable::FromWebDatabase(db)->GetPaymentInstruments(
      payment_instruments);
  return std::make_unique<WDResult<std::vector<sync_pb::PaymentInstrument>>>(
      PAYMENT_INSTRUMENT_RESULT, std::move(payment_instruments));
}

std::unique_ptr<WDTypedResult>
AutofillWebDataBackendImpl::GetPaymentInstrumentCreationOptions(
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  std::vector<sync_pb::PaymentInstrumentCreationOption>
      payment_instrument_creation_options;
  PaymentsAutofillTable::FromWebDatabase(db)
      ->GetPaymentInstrumentCreationOptions(
          payment_instrument_creation_options);
  return std::make_unique<
      WDResult<std::vector<sync_pb::PaymentInstrumentCreationOption>>>(
      PAYMENT_INSTRUMENT_CREATION_OPTION_RESULT,
      std::move(payment_instrument_creation_options));
}

WebDatabase::State AutofillWebDataBackendImpl::ClearAllServerData(
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  if (PaymentsAutofillTable::FromWebDatabase(db)->ClearAllServerData()) {
    ReportResult(Result::kClearAllServerData_Success);
    return WebDatabase::COMMIT_NEEDED;
  }
  ReportResult(Result::kClearAllServerData_Failure);
  return WebDatabase::COMMIT_NOT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::ClearAllCreditCardBenefits(
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  if (PaymentsAutofillTable::FromWebDatabase(db)
          ->ClearAllCreditCardBenefits()) {
    ReportResult(Result::kClearAllCreditCardBenefits_Success);
    return WebDatabase::COMMIT_NEEDED;
  }
  ReportResult(Result::kClearAllCreditCardBenefits_Failure);
  return WebDatabase::COMMIT_NOT_NEEDED;
}

WebDatabase::State AutofillWebDataBackendImpl::AddServerCreditCardForTesting(
    const CreditCard& credit_card,
    WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  if (!PaymentsAutofillTable::FromWebDatabase(db)
           ->AddServerCreditCardForTesting(credit_card)) {
    ReportResult(Result::kAddServerCreditCardForTesting_Failure);
    return WebDatabase::COMMIT_NOT_NEEDED;
  }

  for (AutofillWebDataServiceObserverOnDBSequence& db_observer :
       db_observer_list_) {
    db_observer.CreditCardChanged(CreditCardChange(
        CreditCardChange::ADD, credit_card.guid(), credit_card));
  }
  ReportResult(Result::kAddServerCreditCardForTesting_Success);
  return WebDatabase::COMMIT_NEEDED;
}

// TODO(crbug.com/346507576): after full launch, change return type to
// WebDatabase::State.
std::unique_ptr<WDTypedResult>
AutofillWebDataBackendImpl::MigrateDataFromLegacyTable(WebDatabase* db) {
  DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
  if (!AutocompleteTableLabelSensitive::FromWebDatabase(db)
           ->MigrateDataFromLegacyTable()) {
    // TODO(crbug.com/346507576): Report failure via UMA metrics.
    return std::make_unique<WDResult<bool>>(BOOL_RESULT, false);
  }
  // TODO(crbug.com/346507576): Report success via UMA metrics.
  return std::make_unique<WDResult<bool>>(BOOL_RESULT, true);
}

}  // namespace autofill
