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

#include "components/autofill_assistant/browser/actions/collect_user_data_action.h"

#include <algorithm>
#include <array>
#include <set>
#include <utility>
#include <vector>

#include "base/bind.h"
#include "base/callback.h"
#include "base/i18n/case_conversion.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/geo/address_i18n.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/metrics.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/user_data_util.h"
#include "components/autofill_assistant/browser/website_login_fetcher_impl.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_contents.h"
#include "third_party/blink/public/mojom/payments/payment_request.mojom.h"
#include "third_party/libaddressinput/chromium/addressinput_util.h"
#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
#include "ui/base/l10n/l10n_util.h"

namespace {

static constexpr int kDefaultMaxNumberContactSummaryLines = 1;
static constexpr std::array<autofill_assistant::AutofillContactField, 2>
    kDefaultContactSummaryFields = {autofill_assistant::EMAIL_ADDRESS,
                                    autofill_assistant::NAME_FULL};
static constexpr int kDefaultMaxNumberContactFullLines = 2;
static constexpr std::array<autofill_assistant::AutofillContactField, 2>
    kDefaultContactFullFields = {autofill_assistant::NAME_FULL,
                                 autofill_assistant::EMAIL_ADDRESS};

using autofill_assistant::CollectUserDataOptions;
using autofill_assistant::DateTimeProto;
using autofill_assistant::TermsAndConditionsState;
using autofill_assistant::UserData;

bool IsCompleteContact(
    const autofill::AutofillProfile* profile,
    const CollectUserDataOptions& collect_user_data_options) {
  if (!collect_user_data_options.request_payer_name &&
      !collect_user_data_options.request_payer_email &&
      !collect_user_data_options.request_payer_phone) {
    return true;
  }

  if (!profile) {
    return false;
  }

  if (collect_user_data_options.request_payer_name &&
      profile->GetRawInfo(autofill::NAME_FULL).empty()) {
    return false;
  }

  if (collect_user_data_options.request_payer_email &&
      profile->GetRawInfo(autofill::EMAIL_ADDRESS).empty()) {
    return false;
  }

  if (collect_user_data_options.request_payer_phone &&
      profile->GetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER).empty()) {
    return false;
  }
  return true;
}

bool IsCompleteAddress(const autofill::AutofillProfile* profile,
                       bool require_postal_code) {
  if (!profile) {
    return false;
  }
  // We use a hard coded locale here since it's not used in the autofill:: code
  // anyway. I.e. creating this profile ends up in FormGroup::GetInfoImpl, which
  // simply ignores the app_locale.
  auto address_data =
      autofill::i18n::CreateAddressDataFromAutofillProfile(*profile, "en-US");
  if (!autofill::addressinput::HasAllRequiredFields(*address_data)) {
    return false;
  }

  if (require_postal_code && address_data->postal_code.empty()) {
    return false;
  }

  return true;
}

bool IsCompleteShippingAddress(
    const autofill::AutofillProfile* profile,
    const CollectUserDataOptions& collect_user_data_options) {
  return !collect_user_data_options.request_shipping ||
         IsCompleteAddress(profile, /* require_postal_code = */ false);
}

bool IsCompleteCreditCard(
    const autofill::CreditCard* credit_card,
    const autofill::AutofillProfile* billing_profile,
    const CollectUserDataOptions& collect_user_data_options) {
  if (!collect_user_data_options.request_payment_method) {
    return true;
  }

  if (!credit_card || !billing_profile) {
    return false;
  }

  if (!IsCompleteAddress(
          billing_profile,
          collect_user_data_options.require_billing_postal_code)) {
    return false;
  }

  if (credit_card->record_type() != autofill::CreditCard::MASKED_SERVER_CARD &&
      !credit_card->HasValidCardNumber()) {
    // Can't check validity of masked server card numbers because they are
    // incomplete until decrypted.
    return false;
  }

  if (!credit_card->HasValidExpirationDate() ||
      credit_card->billing_address_id().empty()) {
    return false;
  }

  std::string basic_card_network =
      autofill::data_util::GetPaymentRequestData(credit_card->network())
          .basic_card_issuer_network;
  if (!collect_user_data_options.supported_basic_card_networks.empty() &&
      std::find(collect_user_data_options.supported_basic_card_networks.begin(),
                collect_user_data_options.supported_basic_card_networks.end(),
                basic_card_network) ==
          collect_user_data_options.supported_basic_card_networks.end()) {
    return false;
  }
  return true;
}

bool IsValidLoginChoice(
    const std::string& choice_identifier,
    const CollectUserDataOptions& collect_user_data_options) {
  return !collect_user_data_options.request_login_choice ||
         !choice_identifier.empty();
}

bool IsValidTermsChoice(
    TermsAndConditionsState terms_state,
    const CollectUserDataOptions& collect_user_data_options) {
  return collect_user_data_options.accept_terms_and_conditions_text.empty() ||
         terms_state != TermsAndConditionsState::NOT_SELECTED;
}

// Checks |proto| and writes an error message to |error| if fields are missing.
bool IsValidDateTimeRangeProto(
    const autofill_assistant::DateTimeRangeProto& proto,
    std::string* error) {
  std::vector<std::string> missing_fields;
  if (proto.start_date_label().empty())
    missing_fields.emplace_back("start_date_label");
  if (proto.start_time_label().empty())
    missing_fields.emplace_back("start_time_label");
  if (proto.end_date_label().empty())
    missing_fields.emplace_back("end_date_label");
  if (proto.end_time_label().empty())
    missing_fields.emplace_back("end_time_label");
  if (!proto.has_start_date())
    missing_fields.emplace_back("start_date");
  if (!proto.has_start_time_slot())
    missing_fields.emplace_back("start_time_slot");
  if (!proto.has_end_date())
    missing_fields.emplace_back("end_date");
  if (!proto.has_end_time_slot())
    missing_fields.emplace_back("end_time_slot");
  if (!proto.has_min_date())
    missing_fields.emplace_back("min_date");
  if (!proto.has_max_date())
    missing_fields.emplace_back("max_date");
  if (proto.time_slots().empty())
    missing_fields.emplace_back("time_slots");
  if (proto.date_not_set_error().empty())
    missing_fields.emplace_back("date_not_set_error");
  if (proto.time_not_set_error().empty())
    missing_fields.emplace_back("time_not_set_error");

  if (error != nullptr && !missing_fields.empty()) {
    error->assign("The following fields are missing or empty: ");
    error->append(base::JoinString(missing_fields, ", "));
  }

  return missing_fields.empty();
}

bool IsValidDateTimeRange(
    const base::Optional<autofill_assistant::DateProto>& start_date,
    const base::Optional<int> start_timeslot,
    const base::Optional<autofill_assistant::DateProto> end_date,
    const base::Optional<int> end_timeslot,
    const CollectUserDataOptions& collect_user_data_options) {
  if (!collect_user_data_options.request_date_time_range) {
    return true;
  }
  if (!start_date.has_value() || !start_timeslot.has_value() ||
      !end_date.has_value() || !end_timeslot.has_value()) {
    return false;
  }

  auto temp_start_date = start_date;
  auto temp_start_timeslot = start_timeslot;
  auto temp_end_date = end_date;
  auto temp_end_timeslot = end_timeslot;
  return !autofill_assistant::CollectUserDataAction::SanitizeDateTimeRange(
      &temp_start_date, &temp_start_timeslot, &temp_end_date,
      &temp_end_timeslot, collect_user_data_options, false);
}

bool IsValidUserFormSection(
    const autofill_assistant::UserFormSectionProto& proto) {
  if (proto.title().empty()) {
    VLOG(2) << "UserFormSectionProto: Empty title not allowed.";
    return false;
  }
  switch (proto.section_case()) {
    case autofill_assistant::UserFormSectionProto::kStaticTextSection:
      if (proto.static_text_section().text().empty()) {
        VLOG(2) << "StaticTextSectionProto: Empty text not allowed.";
        return false;
      }
      break;
    case autofill_assistant::UserFormSectionProto::kTextInputSection: {
      if (proto.text_input_section().input_fields().empty()) {
        VLOG(2) << "TextInputProto: At least one input must be specified.";
        return false;
      }
      std::set<std::string> memory_keys;
      for (const auto& input_field :
           proto.text_input_section().input_fields()) {
        if (input_field.client_memory_key().empty()) {
          VLOG(2) << "TextInputProto: Memory key must be specified.";
          return false;
        }
        if (!memory_keys.insert(input_field.client_memory_key()).second) {
          VLOG(2) << "TextInputProto: Duplicate memory keys ("
                  << input_field.client_memory_key() << ").";
          return false;
        }
      }
      break;
    }
    case autofill_assistant::UserFormSectionProto::kPopupListSection:
      if (proto.popup_list_section().item_names().empty()) {
        VLOG(2) << "PopupListProto: At least one item must be specified.";
        return false;
      }
      if (proto.popup_list_section().initial_selection().size() > 1 &&
          proto.popup_list_section().allow_multiselect() == false) {
        VLOG(2) << "PopupListProto: multiple initial selections for a single "
                   "selection popup.";
        return false;
      }
      for (int selection : proto.popup_list_section().initial_selection()) {
        if (selection >= proto.popup_list_section().item_names().size() ||
            selection < 0) {
          VLOG(2) << "PopupListProto: an initial selection is out of bounds.";
          return false;
        }
      }
      break;
    case autofill_assistant::UserFormSectionProto::SECTION_NOT_SET:
      VLOG(2) << "UserFormSectionProto: section oneof not set.";
      return false;
  }
  return true;
}

bool IsValidUserModel(const autofill_assistant::UserModel& user_model,
                      const autofill_assistant::CollectUserDataOptions&
                          collect_user_data_options) {
  if (!collect_user_data_options.additional_model_identifier_to_check
           .has_value()) {
    return true;
  }

  auto valid = user_model.GetValue(
      *collect_user_data_options.additional_model_identifier_to_check);
  if (!valid.has_value()) {
    VLOG(1) << "Error evaluating validity of user model: '"
            << *collect_user_data_options.additional_model_identifier_to_check
            << "' not found in user model.";
    return false;
  }

  if (valid->kind_case() != autofill_assistant::ValueProto::kBooleans ||
      valid->booleans().values().size() != 1) {
    VLOG(1) << "Error evaluating validity of user model: expected single "
               "boolean, but got "
            << *valid;
    return false;
  }

  return valid->booleans().values(0);
}

// Merges |model_a| and |model_b| into a new model.
// TODO(arbesser): deal with overlapping keys.
autofill_assistant::ModelProto MergeModelProtos(
    const autofill_assistant::ModelProto& model_a,
    const autofill_assistant::ModelProto& model_b) {
  autofill_assistant::ModelProto model_merged;
  for (const auto& value : model_a.values()) {
    *model_merged.add_values() = value;
  }
  for (const auto& value : model_b.values()) {
    *model_merged.add_values() = value;
  }
  return model_merged;
}

void FillProtoForAdditionalSection(
    const autofill_assistant::UserFormSectionProto& additional_section,
    const UserData& user_data,
    autofill_assistant::ProcessedActionProto* processed_action_proto) {
  switch (additional_section.section_case()) {
    case autofill_assistant::UserFormSectionProto::kTextInputSection:
      for (const auto& text_input :
           additional_section.text_input_section().input_fields()) {
        if (user_data.has_additional_value(text_input.client_memory_key())) {
          processed_action_proto->mutable_collect_user_data_result()
              ->add_set_text_input_memory_keys(text_input.client_memory_key());
          if (additional_section.send_result_to_backend()) {
            auto value = user_data.additional_values_.find(
                text_input.client_memory_key());
            autofill_assistant::ModelProto_ModelValue model_value;
            model_value.set_identifier(text_input.client_memory_key());
            *model_value.mutable_value() = value->second;
            *processed_action_proto->mutable_collect_user_data_result()
                 ->add_additional_sections_values() = model_value;
          }
        }
      }
      break;
    case autofill_assistant::UserFormSectionProto::kPopupListSection:
      if (user_data.has_additional_value(
              additional_section.popup_list_section().additional_value_key()) &&
          additional_section.send_result_to_backend()) {
        auto value = user_data.additional_values_.find(
            additional_section.popup_list_section().additional_value_key());
        autofill_assistant::ModelProto_ModelValue model_value;
        model_value.set_identifier(
            additional_section.popup_list_section().additional_value_key());
        *model_value.mutable_value() = value->second;
        *processed_action_proto->mutable_collect_user_data_result()
             ->add_additional_sections_values() = model_value;
      }
      break;
    case autofill_assistant::UserFormSectionProto::kStaticTextSection:
    case autofill_assistant::UserFormSectionProto::SECTION_NOT_SET:
      // Do nothing.
      break;
  }
}

bool IsAdditionalSectionComplete(
    const std::map<std::string, autofill_assistant::ValueProto>&
        additional_sections,
    const autofill_assistant::UserFormSectionProto& section) {
  if (section.section_case() !=
          autofill_assistant::UserFormSectionProto::kPopupListSection ||
      !section.popup_list_section().selection_mandatory()) {
    return true;
  }
  auto find_result = additional_sections.find(
      section.popup_list_section().additional_value_key());
  if (find_result != additional_sections.end() &&
      !find_result->second.ints().values().empty()) {
    return true;
  }
  return false;
}

bool AreAdditionalSectionsComplete(
    const std::map<std::string, autofill_assistant::ValueProto>&
        additional_sections,
    const CollectUserDataOptions& collect_user_data_options) {
  for (const auto& section :
       collect_user_data_options.additional_prepended_sections) {
    if (!IsAdditionalSectionComplete(additional_sections, section)) {
      return false;
    }
  }
  for (const auto& section :
       collect_user_data_options.additional_appended_sections) {
    if (!IsAdditionalSectionComplete(additional_sections, section)) {
      return false;
    }
  }
  return true;
}

void SetInitialUserDataForAdditionalSection(
    const autofill_assistant::UserFormSectionProto& additional_section,
    UserData* user_data) {
  switch (additional_section.section_case()) {
    case autofill_assistant::UserFormSectionProto::kTextInputSection: {
      for (const auto& text_input :
           additional_section.text_input_section().input_fields()) {
        autofill_assistant::ValueProto value;
        value.mutable_strings()->add_values(text_input.value());
        user_data->additional_values_[text_input.client_memory_key()] = value;
      }
      break;
    }
    case autofill_assistant::UserFormSectionProto::kPopupListSection: {
      autofill_assistant::ValueProto value;
      for (const auto& selection :
           additional_section.popup_list_section().initial_selection()) {
        value.mutable_ints()->add_values(selection);
      }
      user_data->additional_values_[additional_section.popup_list_section()
                                        .additional_value_key()] = value;
      break;
    }
    case autofill_assistant::UserFormSectionProto::kStaticTextSection:
    case autofill_assistant::UserFormSectionProto::SECTION_NOT_SET:
      // Do nothing.
      break;
  }
}
}  // namespace

namespace autofill_assistant {

CollectUserDataAction::LoginDetails::LoginDetails(
    bool _choose_automatically_if_no_other_options,
    const std::string& _payload,
    const WebsiteLoginFetcher::Login& _login)
    : choose_automatically_if_no_other_options(
          _choose_automatically_if_no_other_options),
      payload(_payload),
      login(_login) {}

CollectUserDataAction::LoginDetails::LoginDetails(
    bool _choose_automatically_if_no_other_options,
    const std::string& _payload)
    : choose_automatically_if_no_other_options(
          _choose_automatically_if_no_other_options),
      payload(_payload) {}

CollectUserDataAction::LoginDetails::~LoginDetails() = default;

CollectUserDataAction::CollectUserDataAction(ActionDelegate* delegate,
                                             const ActionProto& proto)
    : Action(delegate, proto) {
  DCHECK(proto_.has_collect_user_data());
}

CollectUserDataAction::~CollectUserDataAction() {
  delegate_->GetPersonalDataManager()->RemoveObserver(this);

  // Report UMA histograms.
  if (shown_to_user_) {
    Metrics::RecordPaymentRequestPrefilledSuccess(initially_prefilled,
                                                  action_successful_);
    Metrics::RecordPaymentRequestAutofillChanged(personal_data_changed_,
                                                 action_successful_);
    Metrics::RecordPaymentRequestMandatoryPostalCode(
        proto_.collect_user_data().require_billing_postal_code(),
        initial_card_has_billing_postal_code_, action_successful_);
  }
}

void CollectUserDataAction::InternalProcessAction(
    ProcessActionCallback callback) {
  callback_ = std::move(callback);
  if (!CreateOptionsFromProto()) {
    EndAction(ClientStatus(INVALID_ACTION));
    return;
  }

  // If Chrome password manager logins are requested, we need to asynchronously
  // obtain them before showing the UI.
  auto collect_user_data = proto_.collect_user_data();
  auto password_manager_option = std::find_if(
      collect_user_data.login_details().login_options().begin(),
      collect_user_data.login_details().login_options().end(),
      [&](const LoginDetailsProto::LoginOptionProto& option) {
        return option.type_case() ==
               LoginDetailsProto::LoginOptionProto::kPasswordManager;
      });
  bool requests_pwm_logins =
      password_manager_option !=
      collect_user_data.login_details().login_options().end();

  collect_user_data_options_->confirm_callback =
      base::BindOnce(&CollectUserDataAction::OnGetUserData,
                     weak_ptr_factory_.GetWeakPtr(), collect_user_data);
  collect_user_data_options_->additional_actions_callback =
      base::BindOnce(&CollectUserDataAction::OnAdditionalActionTriggered,
                     weak_ptr_factory_.GetWeakPtr());
  collect_user_data_options_->terms_link_callback =
      base::BindOnce(&CollectUserDataAction::OnTermsAndConditionsLinkClicked,
                     weak_ptr_factory_.GetWeakPtr());
  if (requests_pwm_logins) {
    delegate_->GetWebsiteLoginFetcher()->GetLoginsForUrl(
        delegate_->GetWebContents()->GetLastCommittedURL(),
        base::BindOnce(&CollectUserDataAction::OnGetLogins,
                       weak_ptr_factory_.GetWeakPtr(),
                       *password_manager_option));
  } else {
    ShowToUser();
  }
}

void CollectUserDataAction::EndAction(const ClientStatus& status) {
  delegate_->CleanUpAfterPrompt();
  action_successful_ = status.ok();
  UpdateProcessedAction(status);
  std::move(callback_).Run(std::move(processed_action_proto_));
}

void CollectUserDataAction::OnGetLogins(
    const LoginDetailsProto::LoginOptionProto& login_option,
    std::vector<WebsiteLoginFetcher::Login> logins) {
  for (const auto& login : logins) {
    auto identifier =
        base::NumberToString(collect_user_data_options_->login_choices.size());
    collect_user_data_options_->login_choices.emplace_back(
        identifier, login.username, login_option.sublabel(),
        login_option.sublabel_accessibility_hint(),
        login_option.preselection_priority(),
        login_option.has_info_popup()
            ? base::make_optional(login_option.info_popup())
            : base::nullopt);
    login_details_map_.emplace(
        identifier, std::make_unique<LoginDetails>(
                        login_option.choose_automatically_if_no_other_options(),
                        login_option.payload(), login));
  }
  ShowToUser();
}

void CollectUserDataAction::ShowToUser() {
  // Set initial state.
  delegate_->WriteUserData(base::BindOnce(&CollectUserDataAction::OnShowToUser,
                                          weak_ptr_factory_.GetWeakPtr()));
}

void CollectUserDataAction::OnShowToUser(UserData* user_data,
                                         UserData::FieldChange* field_change) {
  // merge the new proto_ into the existing user_data. the proto_ always takes
  // precedence over the existing user_data.
  *field_change = UserData::FieldChange::ALL;
  user_data->succeed_ = false;
  auto collect_user_data = proto_.collect_user_data();
  // the backend should explicitly set the terms and conditions state on every
  // new action.
  switch (collect_user_data.terms_and_conditions_state()) {
    case CollectUserDataProto::NOT_SELECTED:
      user_data->terms_and_conditions_ = NOT_SELECTED;
      break;
    case CollectUserDataProto::ACCEPTED:
      user_data->terms_and_conditions_ = ACCEPTED;
      break;
    case CollectUserDataProto::REVIEW_REQUIRED:
      user_data->terms_and_conditions_ = REQUIRES_REVIEW;
      break;
  }
  for (const auto& additional_section :
       collect_user_data.additional_prepended_sections()) {
    SetInitialUserDataForAdditionalSection(additional_section, user_data);
  }
  for (const auto& additional_section :
       collect_user_data.additional_appended_sections()) {
    SetInitialUserDataForAdditionalSection(additional_section, user_data);
  }

  if (collect_user_data_options_->request_login_choice &&
      collect_user_data_options_->login_choices.empty()) {
    EndAction(ClientStatus(COLLECT_USER_DATA_ERROR));
    return;
  }

  // Special case: if the only available login option has
  // |choose_automatically_if_no_other_options=true|, the section will not be
  // shown.
  bool only_login_requested =
      collect_user_data_options_->request_login_choice &&
      !collect_user_data_options_->request_payer_name &&
      !collect_user_data_options_->request_payer_email &&
      !collect_user_data_options_->request_payer_phone &&
      !collect_user_data_options_->request_shipping &&
      !collect_user_data_options_->request_payment_method &&
      !collect_user_data.request_terms_and_conditions();

  if (collect_user_data_options_->login_choices.size() == 1 &&
      login_details_map_
          .at(collect_user_data_options_->login_choices.at(0).identifier)
          ->choose_automatically_if_no_other_options) {
    collect_user_data_options_->request_login_choice = false;

    user_data->login_choice_identifier_.assign(
        collect_user_data_options_->login_choices[0].identifier);

    // If only the login section is requested and the choice has already been
    // made implicitly, the entire UI will not be shown and the action will
    // complete immediately.
    if (only_login_requested) {
      user_data->succeed_ = true;
      std::move(collect_user_data_options_->confirm_callback)
          .Run(user_data, nullptr);
      return;
    }
  }

  // Clear previously selected info, if requested.
  if (proto_.collect_user_data().clear_previous_credit_card_selection()) {
    user_data->selected_card_.reset();
  }
  if (proto_.collect_user_data().clear_previous_login_selection()) {
    user_data->selected_login_.reset();
  }
  for (const auto& profile_name :
       proto_.collect_user_data().clear_previous_profile_selection()) {
    user_data->selected_addresses_.erase(profile_name);
  }

  // Add available profiles and start listening.
  delegate_->GetPersonalDataManager()->AddObserver(this);
  UpdatePersonalDataManagerProfiles(user_data);
  UpdatePersonalDataManagerCards(user_data);
  UpdateDateTimeRangeStart(user_data);
  UpdateDateTimeRangeEnd(user_data);

  // Gather info for UMA histograms.
  if (!shown_to_user_) {
    shown_to_user_ = true;
    initially_prefilled =
        CheckInitialAutofillDataComplete(delegate_->GetPersonalDataManager());
  }

  if (collect_user_data.has_prompt()) {
    delegate_->SetStatusMessage(collect_user_data.prompt());
  }
  delegate_->Prompt(/* user_actions = */ nullptr,
                    /* disable_force_expand_sheet = */ false);
  delegate_->CollectUserData(collect_user_data_options_.get());
}

void CollectUserDataAction::OnGetUserData(
    const CollectUserDataProto& collect_user_data,
    UserData* user_data,
    const UserModel* user_model) {
  if (!callback_)
    return;

  bool succeed = user_data->succeed_;
  if (succeed) {
    if (collect_user_data.request_payment_method()) {
      DCHECK(user_data->selected_card_);
      std::string card_issuer_network =
          autofill::data_util::GetPaymentRequestData(
              user_data->selected_card_->network())
              .basic_card_issuer_network;
      processed_action_proto_->mutable_collect_user_data_result()
          ->set_card_issuer_network(card_issuer_network);
    }

    if (collect_user_data.has_contact_details()) {
      auto contact_details_proto = collect_user_data.contact_details();
      auto* selected_profile = user_data->selected_address(
          contact_details_proto.contact_details_name());
      DCHECK(selected_profile);

      if (contact_details_proto.request_payer_name()) {
        Metrics::RecordPaymentRequestFirstNameOnly(
            selected_profile->GetRawInfo(autofill::NAME_LAST).empty());
      }

      if (contact_details_proto.request_payer_email()) {
        processed_action_proto_->mutable_collect_user_data_result()
            ->set_payer_email(base::UTF16ToUTF8(
                selected_profile->GetRawInfo(autofill::EMAIL_ADDRESS)));
      }
    }

    if (collect_user_data.has_login_details()) {
      auto login_details =
          login_details_map_.find(user_data->login_choice_identifier_);
      DCHECK(login_details != login_details_map_.end());
      if (login_details->second->login.has_value()) {
        user_data->selected_login_ = *login_details->second->login;
      }

      processed_action_proto_->mutable_collect_user_data_result()
          ->set_login_payload(login_details->second->payload);
    }

    if (collect_user_data.has_date_time_range()) {
      DCHECK(user_data->date_time_range_start_date_.has_value());
      DCHECK(user_data->date_time_range_start_timeslot_.has_value());
      DCHECK(user_data->date_time_range_end_date_.has_value());
      DCHECK(user_data->date_time_range_end_timeslot_.has_value());
      *processed_action_proto_->mutable_collect_user_data_result()
           ->mutable_date_range_start_date() =
          *user_data->date_time_range_start_date_;
      *user_data->date_time_range_start_date_;
      processed_action_proto_->mutable_collect_user_data_result()
          ->set_date_range_start_timeslot(
              *user_data->date_time_range_start_timeslot_);
      *processed_action_proto_->mutable_collect_user_data_result()
           ->mutable_date_range_end_date() =
          *user_data->date_time_range_end_date_;
      *user_data->date_time_range_end_date_;
      processed_action_proto_->mutable_collect_user_data_result()
          ->set_date_range_end_timeslot(
              *user_data->date_time_range_end_timeslot_);
    }
    for (const auto& section :
         collect_user_data.additional_prepended_sections()) {
      FillProtoForAdditionalSection(section, *user_data,
                                    processed_action_proto_.get());
    }
    for (const auto& section :
         collect_user_data.additional_appended_sections()) {
      FillProtoForAdditionalSection(section, *user_data,
                                    processed_action_proto_.get());
    }

    processed_action_proto_->mutable_collect_user_data_result()
        ->set_is_terms_and_conditions_accepted(
            user_data->terms_and_conditions_ ==
            TermsAndConditionsState::ACCEPTED);
    if (user_model != nullptr &&
        (collect_user_data.has_generic_user_interface_prepended() ||
         collect_user_data.has_generic_user_interface_appended())) {
      // Build the union of both models (this assumes that there are no
      // overlapping model keys).
      *processed_action_proto_->mutable_collect_user_data_result()
           ->mutable_model() = MergeModelProtos(
          collect_user_data.generic_user_interface_prepended().model(),
          collect_user_data.generic_user_interface_appended().model());
      user_model->UpdateProto(
          processed_action_proto_->mutable_collect_user_data_result()
              ->mutable_model());
    }
    processed_action_proto_->mutable_collect_user_data_result()
        ->set_shown_to_user(shown_to_user_);
  }

  EndAction(succeed ? ClientStatus(ACTION_APPLIED)
                    : ClientStatus(COLLECT_USER_DATA_ERROR));
}

void CollectUserDataAction::OnAdditionalActionTriggered(int index) {
  if (!callback_)
    return;

  processed_action_proto_->mutable_collect_user_data_result()
      ->set_additional_action_index(index);
  EndAction(ClientStatus(ACTION_APPLIED));
}

void CollectUserDataAction::OnTermsAndConditionsLinkClicked(int link) {
  if (!callback_)
    return;

  processed_action_proto_->mutable_collect_user_data_result()->set_terms_link(
      link);
  EndAction(ClientStatus(ACTION_APPLIED));
}

bool CollectUserDataAction::CreateOptionsFromProto() {
  DCHECK(collect_user_data_options_ == nullptr);
  collect_user_data_options_ = std::make_unique<CollectUserDataOptions>();
  auto collect_user_data = proto_.collect_user_data();

  if (collect_user_data.has_contact_details()) {
    auto contact_details = collect_user_data.contact_details();
    collect_user_data_options_->request_payer_email =
        contact_details.request_payer_email();
    collect_user_data_options_->request_payer_name =
        contact_details.request_payer_name();
    collect_user_data_options_->request_payer_phone =
        contact_details.request_payer_phone();
    // TODO(b/146405276): Remove legacy support for |summary_fields| and
    // |full_fields|.
    if (contact_details.summary_fields().empty()) {
      collect_user_data_options_->contact_summary_max_lines =
          kDefaultMaxNumberContactSummaryLines;
      collect_user_data_options_->contact_summary_fields.assign(
          kDefaultContactSummaryFields.begin(),
          kDefaultContactSummaryFields.end());
    } else {
      for (const auto& field : contact_details.summary_fields()) {
        collect_user_data_options_->contact_summary_fields.emplace_back(
            (AutofillContactField)field);
      }
      if (contact_details.max_number_summary_lines() <= 0) {
        VLOG(1) << "max_number_summary_lines must be > 0";
        return false;
      }
      collect_user_data_options_->contact_summary_max_lines =
          contact_details.max_number_summary_lines();
    }
    if (contact_details.full_fields().empty()) {
      collect_user_data_options_->contact_full_max_lines =
          kDefaultMaxNumberContactFullLines;
      collect_user_data_options_->contact_full_fields.assign(
          kDefaultContactFullFields.begin(), kDefaultContactFullFields.end());
    } else {
      for (const auto& field : contact_details.full_fields()) {
        collect_user_data_options_->contact_full_fields.emplace_back(
            (AutofillContactField)field);
      }
      if (contact_details.max_number_full_lines() <= 0) {
        VLOG(1) << "max_number_full_lines must be > 0";
        return false;
      }
      collect_user_data_options_->contact_full_max_lines =
          contact_details.max_number_full_lines();
    }
    if (collect_user_data_options_->request_payer_email ||
        collect_user_data_options_->request_payer_name ||
        collect_user_data_options_->request_payer_phone) {
      if (!contact_details.has_contact_details_name()) {
        VLOG(1) << "Contact details name missing";
        return false;
      }
    }

    collect_user_data_options_->contact_details_name =
        contact_details.contact_details_name();
  }

  for (const auto& network :
       collect_user_data.supported_basic_card_networks()) {
    if (!autofill::data_util::IsValidBasicCardIssuerNetwork(network)) {
      VLOG(1) << "Invalid basic card network: " << network;
      return false;
    }
  }
  std::copy(collect_user_data.supported_basic_card_networks().begin(),
            collect_user_data.supported_basic_card_networks().end(),
            std::back_inserter(
                collect_user_data_options_->supported_basic_card_networks));

  collect_user_data_options_->shipping_address_name =
      collect_user_data.shipping_address_name();
  collect_user_data_options_->request_shipping =
      !collect_user_data.shipping_address_name().empty();
  collect_user_data_options_->request_payment_method =
      collect_user_data.request_payment_method();
  collect_user_data_options_->require_billing_postal_code =
      collect_user_data.require_billing_postal_code();
  collect_user_data_options_->billing_postal_code_missing_text =
      collect_user_data.billing_postal_code_missing_text();
  if (collect_user_data_options_->require_billing_postal_code &&
      collect_user_data_options_->billing_postal_code_missing_text.empty()) {
    VLOG(1) << "Required postal code without error text";
    return false;
  }
  collect_user_data_options_->billing_address_name =
      collect_user_data.billing_address_name();
  if (collect_user_data_options_->request_payment_method &&
      collect_user_data_options_->billing_address_name.empty()) {
    VLOG(1) << "Required payment method without address name";
    return false;
  }

  collect_user_data_options_->credit_card_expired_text =
      collect_user_data.credit_card_expired_text();
  // TODO(b/146195295): Remove fallback and enforce non-empty backend string.
  if (collect_user_data_options_->credit_card_expired_text.empty()) {
    collect_user_data_options_->credit_card_expired_text =
        l10n_util::GetStringUTF8(
            IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED);
  }
  collect_user_data_options_->request_login_choice =
      collect_user_data.has_login_details();
  collect_user_data_options_->login_section_title.assign(
      collect_user_data.login_details().section_title());

  // Transform login options to concrete login choices.
  for (const auto& login_option :
       collect_user_data.login_details().login_options()) {
    switch (login_option.type_case()) {
      case LoginDetailsProto::LoginOptionProto::kCustom: {
        LoginChoice choice = {
            base::NumberToString(
                collect_user_data_options_->login_choices.size()),
            login_option.custom().label(),
            login_option.sublabel(),
            login_option.has_sublabel_accessibility_hint()
                ? base::make_optional(
                      login_option.sublabel_accessibility_hint())
                : base::nullopt,
            login_option.has_preselection_priority()
                ? login_option.preselection_priority()
                : -1,
            login_option.has_info_popup()
                ? base::make_optional(login_option.info_popup())
                : base::nullopt};
        collect_user_data_options_->login_choices.emplace_back(
            std::move(choice));
        login_details_map_.emplace(
            choice.identifier,
            std::make_unique<LoginDetails>(
                login_option.choose_automatically_if_no_other_options(),
                login_option.payload()));
        break;
      }
      case LoginDetailsProto::LoginOptionProto::kPasswordManager: {
        // Will be retrieved later.
        break;
      }
      case LoginDetailsProto::LoginOptionProto::TYPE_NOT_SET: {
        // Login option specified, but type not set (should never happen).
        VLOG(1) << "LoginDetailsProto::LoginOptionProto::TYPE_NOT_SET";
        return false;
      }
    }
  }

  if (collect_user_data.has_date_time_range()) {
    std::string error_message;
    if (!IsValidDateTimeRangeProto(collect_user_data.date_time_range(),
                                   &error_message)) {
      VLOG(1) << "Invalid action: " << error_message;
      return false;
    }
    if (collect_user_data.date_time_range().start_time_slot() < 0 ||
        collect_user_data.date_time_range().end_time_slot() < 0 ||
        collect_user_data.date_time_range().start_time_slot() >=
            collect_user_data.date_time_range().time_slots().size() ||
        collect_user_data.date_time_range().end_time_slot() >=
            collect_user_data.date_time_range().time_slots().size()) {
      VLOG(1) << "Invalid action: time slot index out of range";
      return false;
    }
    collect_user_data_options_->request_date_time_range = true;
    collect_user_data_options_->date_time_range =
        collect_user_data.date_time_range();
  }

  for (const auto& section :
       collect_user_data.additional_prepended_sections()) {
    if (!IsValidUserFormSection(section)) {
      VLOG(1)
          << "Invalid UserFormSectionProto in additional_prepended_sections";
      return false;
    }
    collect_user_data_options_->additional_prepended_sections.emplace_back(
        section);
  }
  for (const auto& section : collect_user_data.additional_appended_sections()) {
    if (!IsValidUserFormSection(section)) {
      VLOG(1) << "Invalid UserFormSectionProto in additional_appended_sections";
      return false;
    }
    collect_user_data_options_->additional_appended_sections.emplace_back(
        section);
  }

  if (collect_user_data.has_info_section_text() &&
      collect_user_data.info_section_text().empty()) {
    VLOG(1) << "Info text set but empty.";
    return false;
  }

  if (collect_user_data.has_generic_user_interface_prepended()) {
    collect_user_data_options_->generic_user_interface_prepended =
        collect_user_data.generic_user_interface_prepended();
  }
  if (collect_user_data.has_generic_user_interface_appended()) {
    collect_user_data_options_->generic_user_interface_appended =
        collect_user_data.generic_user_interface_appended();
  }
  if (collect_user_data.has_additional_model_identifier_to_check()) {
    collect_user_data_options_->additional_model_identifier_to_check =
        collect_user_data.additional_model_identifier_to_check();
  }

  // TODO(crbug.com/806868): Maybe we could refactor this to make the confirm
  // chip and direct_action part of the additional_actions.
  std::string confirm_text = collect_user_data.confirm_button_text();
  if (confirm_text.empty()) {
    confirm_text =
        l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_PAYMENT_INFO_CONFIRM);
  }
  collect_user_data_options_->confirm_action.mutable_chip()->set_text(
      confirm_text);
  collect_user_data_options_->confirm_action.mutable_chip()->set_type(
      HIGHLIGHTED_ACTION);
  *collect_user_data_options_->confirm_action.mutable_direct_action() =
      collect_user_data.confirm_direct_action();

  for (auto action : collect_user_data.additional_actions()) {
    collect_user_data_options_->additional_actions.push_back(action);
  }

  if (collect_user_data.request_terms_and_conditions()) {
    collect_user_data_options_->show_terms_as_checkbox =
        collect_user_data.show_terms_as_checkbox();

    if (collect_user_data.accept_terms_and_conditions_text().empty()) {
      VLOG(1) << "Required terms and conditions without text";
      return false;
    }
    collect_user_data_options_->accept_terms_and_conditions_text =
        collect_user_data.accept_terms_and_conditions_text();

    if (!collect_user_data.show_terms_as_checkbox() &&
        collect_user_data.terms_require_review_text().empty()) {
      VLOG(1) << "Required terms review without text";
      return false;
    }
    collect_user_data_options_->terms_require_review_text =
        collect_user_data.terms_require_review_text();
  }

  if (collect_user_data.has_info_section_text()) {
    collect_user_data_options_->info_section_text =
        collect_user_data.info_section_text();
    collect_user_data_options_->info_section_text_center =
        collect_user_data.info_section_text_center();
  }

  collect_user_data_options_->privacy_notice_text =
      collect_user_data.privacy_notice_text();

  collect_user_data_options_->default_email =
      delegate_->GetAccountEmailAddress();

  return true;
}

bool CollectUserDataAction::CheckInitialAutofillDataComplete(
    autofill::PersonalDataManager* personal_data_manager) {
  DCHECK(collect_user_data_options_ != nullptr);
  bool request_contact = collect_user_data_options_->request_payer_name ||
                         collect_user_data_options_->request_payer_email ||
                         collect_user_data_options_->request_payer_phone;
  if (request_contact || collect_user_data_options_->request_shipping) {
    auto profiles = personal_data_manager->GetProfiles();
    if (request_contact) {
      auto completeContactIter = std::find_if(
          profiles.begin(), profiles.end(), [this](const auto& profile) {
            return IsCompleteContact(profile,
                                     *this->collect_user_data_options_.get());
          });
      if (completeContactIter == profiles.end()) {
        return false;
      }
    }

    if (collect_user_data_options_->request_shipping) {
      auto completeAddressIter = std::find_if(
          profiles.begin(), profiles.end(), [this](const auto* profile) {
            return IsCompleteShippingAddress(
                profile, *this->collect_user_data_options_.get());
          });
      if (completeAddressIter == profiles.end()) {
        return false;
      }
    }
  }

  if (collect_user_data_options_->request_payment_method) {
    auto credit_cards = personal_data_manager->GetCreditCards();
    auto completeCardIter = std::find_if(
        credit_cards.begin(), credit_cards.end(),
        [this, personal_data_manager](const auto* credit_card) {
          // TODO(b/142630213): Figure out how to retrieve billing profile if
          // user has turned off addresses in Chrome settings.
          return IsCompleteCreditCard(
              credit_card,
              credit_card != nullptr
                  ? personal_data_manager->GetProfileByGUID(credit_card->guid())
                  : nullptr,
              *this->collect_user_data_options_.get());
        });
    if (completeCardIter == credit_cards.end()) {
      return false;
    }
    if (collect_user_data_options_->require_billing_postal_code) {
      initial_card_has_billing_postal_code_ = true;
    }
  }
  return true;
}

// TODO(b/148448649): Move to dedicated helper namespace.
// static
bool CollectUserDataAction::IsUserDataComplete(
    const UserData& user_data,
    const UserModel& user_model,
    const CollectUserDataOptions& options) {
  auto* selected_profile =
      user_data.selected_address(options.contact_details_name);
  auto* billing_address =
      user_data.selected_address(options.billing_address_name);
  auto* shipping_address =
      user_data.selected_address(options.shipping_address_name);
  return IsCompleteContact(selected_profile, options) &&
         IsCompleteShippingAddress(shipping_address, options) &&
         IsCompleteCreditCard(user_data.selected_card_.get(), billing_address,
                              options) &&
         IsValidLoginChoice(user_data.login_choice_identifier_, options) &&
         IsValidTermsChoice(user_data.terms_and_conditions_, options) &&
         IsValidDateTimeRange(user_data.date_time_range_start_date_,
                              user_data.date_time_range_start_timeslot_,
                              user_data.date_time_range_end_date_,
                              user_data.date_time_range_end_timeslot_,
                              options) &&
         AreAdditionalSectionsComplete(user_data.additional_values_, options) &&
         IsValidUserModel(user_model, options);
}

// TODO(b/148448649): Move to dedicated helper namespace.
// static
int CollectUserDataAction::CompareDates(const DateProto& first,
                                        const DateProto& second) {
  auto first_tuple = std::make_tuple(first.year(), first.month(), first.day());
  auto second_tuple =
      std::make_tuple(second.year(), second.month(), second.day());
  if (first_tuple < second_tuple) {
    return -1;
  } else if (second_tuple < first_tuple) {
    return 1;
  }
  return 0;
}

// TODO(b/148448649): Move to dedicated helper namespace.
// static
bool CollectUserDataAction::SanitizeDateTimeRange(
    base::Optional<DateProto>* start_date,
    base::Optional<int>* start_timeslot,
    base::Optional<DateProto>* end_date,
    base::Optional<int>* end_timeslot,
    const CollectUserDataOptions& collect_user_data_options,
    bool change_start) {
  if (!collect_user_data_options.request_date_time_range) {
    return false;
  }
  DCHECK(start_date);
  DCHECK(start_timeslot);
  DCHECK(end_date);
  DCHECK(end_timeslot);
  if (!start_date->has_value() || !end_date->has_value()) {
    return false;
  }

  auto date_comparison = CompareDates(**start_date, **end_date);
  if (date_comparison < 0) {
    return false;
  }

  // Start date > end date, reset date.
  if (date_comparison > 0) {
    if (change_start) {
      start_date->reset();
    } else {
      end_date->reset();
    }
    return true;
  }

  if (!start_timeslot->has_value() || !end_timeslot->has_value()) {
    return false;
  }

  DCHECK(**start_timeslot >= 0 &&
         **start_timeslot <
             collect_user_data_options.date_time_range.time_slots().size());
  DCHECK(**end_timeslot >= 0 &&
         **end_timeslot <
             collect_user_data_options.date_time_range.time_slots().size());
  auto start_time =
      collect_user_data_options.date_time_range.time_slots(**start_timeslot);
  auto end_time =
      collect_user_data_options.date_time_range.time_slots(**end_timeslot);
  auto time_comparison =
      start_time.comparison_value() - end_time.comparison_value();
  if (time_comparison < 0) {
    return false;
  }

  // Start date == end date and start time >= end time, reset time.
  if (time_comparison >= 0) {
    if (change_start) {
      start_timeslot->reset();
    } else {
      end_timeslot->reset();
    }
    return true;
  }

  NOTREACHED();
  return false;
}

void CollectUserDataAction::UpdatePersonalDataManagerProfiles(
    UserData* user_data,
    UserData::FieldChange* field_change) {
  if (user_data == nullptr) {
    return;
  }

  bool found_profile = false;
  bool found_shipping_address = false;

  auto* selected_profile = user_data->selected_address(
      collect_user_data_options_->contact_details_name);
  auto* shipping_address = user_data->selected_address(
      collect_user_data_options_->shipping_address_name);
  user_data->available_profiles_.clear();
  for (const auto* profile :
       delegate_->GetPersonalDataManager()->GetProfilesToSuggest()) {
    user_data->available_profiles_.emplace_back(
        std::make_unique<autofill::AutofillProfile>(*profile));

    if (selected_profile != nullptr &&
        CompareContactDetails(*collect_user_data_options_, profile,
                              selected_profile)) {
      found_profile = true;
    }

    if (shipping_address != nullptr &&
        profile->Compare(*shipping_address) == 0) {
      found_shipping_address = true;
    }
  }

  if (!found_profile && selected_profile != nullptr) {
    auto it = user_data->selected_addresses_.find(
        collect_user_data_options_->contact_details_name);
    if (it != user_data->selected_addresses_.end()) {
      user_data->selected_addresses_.erase(it);
    }
  }

  if (!user_data->has_selected_address(
          collect_user_data_options_->contact_details_name) &&
      (collect_user_data_options_->request_payer_name ||
       collect_user_data_options_->request_payer_phone ||
       collect_user_data_options_->request_payer_email)) {
    int default_selection = GetDefaultContactProfile(
        *collect_user_data_options_, user_data->available_profiles_);
    if (default_selection != -1) {
      user_data->selected_addresses_.emplace(
          collect_user_data_options_->contact_details_name,
          std::make_unique<autofill::AutofillProfile>(
              *(user_data->available_profiles_[default_selection])));
    }
  }

  if (!found_shipping_address && shipping_address != nullptr) {
    auto it = user_data->selected_addresses_.find(
        collect_user_data_options_->shipping_address_name);
    if (it != user_data->selected_addresses_.end()) {
      user_data->selected_addresses_.erase(it);
    }
  }
  if (!user_data->has_selected_address(
          collect_user_data_options_->shipping_address_name) &&
      collect_user_data_options_->request_shipping) {
    int default_selection = GetDefaultAddressProfile(
        *collect_user_data_options_, user_data->available_profiles_);
    if (default_selection != -1) {
      user_data->selected_addresses_.emplace(
          collect_user_data_options_->shipping_address_name,
          std::make_unique<autofill::AutofillProfile>(
              *(user_data->available_profiles_[default_selection])));
    }
  }

  if (field_change != nullptr) {
    *field_change = UserData::FieldChange::AVAILABLE_PROFILES;
  }
}

void CollectUserDataAction::UpdatePersonalDataManagerCards(
    UserData* user_data,
    UserData::FieldChange* field_change) {
  DCHECK(user_data != nullptr);

  bool found_card = false;
  user_data->available_payment_instruments_.clear();
  for (const auto* card :
       delegate_->GetPersonalDataManager()->GetCreditCardsToSuggest(true)) {
    if (collect_user_data_options_->supported_basic_card_networks.empty() ||
        std::find(
            collect_user_data_options_->supported_basic_card_networks.begin(),
            collect_user_data_options_->supported_basic_card_networks.end(),
            autofill::data_util::GetPaymentRequestData(card->network())
                .basic_card_issuer_network) !=
            collect_user_data_options_->supported_basic_card_networks.end()) {
      auto payment_instrument = std::make_unique<PaymentInstrument>();
      payment_instrument->card = std::make_unique<autofill::CreditCard>(*card);

      if (!card->billing_address_id().empty()) {
        auto* billing_address =
            delegate_->GetPersonalDataManager()->GetProfileByGUID(
                card->billing_address_id());
        if (billing_address != nullptr) {
          payment_instrument->billing_address =
              std::make_unique<autofill::AutofillProfile>(*billing_address);
        }
      }

      user_data->available_payment_instruments_.emplace_back(
          std::move(payment_instrument));

      if (user_data->selected_card_ != nullptr &&
          card->Compare(*user_data->selected_card_) == 0) {
        found_card = true;
      }
    }
  }

  if (!found_card) {
    user_data->selected_card_.reset();
    auto it = user_data->selected_addresses_.find(
        collect_user_data_options_->billing_address_name);
    if (it != user_data->selected_addresses_.end()) {
      user_data->selected_addresses_.erase(it);
    }
  }
  if (user_data->selected_card_ == nullptr &&
      collect_user_data_options_->request_payment_method) {
    int default_selection = GetDefaultPaymentInstrument(
        *collect_user_data_options_, user_data->available_payment_instruments_);
    if (default_selection != -1) {
      const auto& default_payment_instrument =
          user_data->available_payment_instruments_[default_selection];
      user_data->selected_card_ = std::make_unique<autofill::CreditCard>(
          *(default_payment_instrument->card));
      if (default_payment_instrument->billing_address != nullptr) {
        user_data->selected_addresses_.emplace(
            collect_user_data_options_->billing_address_name,
            std::make_unique<autofill::AutofillProfile>(
                *(default_payment_instrument->billing_address)));
      }
    }
  }

  if (field_change != nullptr) {
    *field_change = UserData::FieldChange::AVAILABLE_PAYMENT_INSTRUMENTS;
  }
}

void CollectUserDataAction::OnPersonalDataChanged() {
  personal_data_changed_ = true;

  delegate_->WriteUserData(
      base::BindOnce(&CollectUserDataAction::UpdatePersonalDataManagerProfiles,
                     weak_ptr_factory_.GetWeakPtr()));
  delegate_->WriteUserData(
      base::BindOnce(&CollectUserDataAction::UpdatePersonalDataManagerCards,
                     weak_ptr_factory_.GetWeakPtr()));
}

void CollectUserDataAction::UpdateDateTimeRangeStart(
    UserData* user_data,
    UserData::FieldChange* field_change) {
  DCHECK(user_data != nullptr);
  DCHECK(collect_user_data_options_ != nullptr);

  UserData::FieldChange changed = UserData::FieldChange::NONE;
  if (!user_data->date_time_range_start_date_.has_value()) {
    user_data->date_time_range_start_date_ =
        collect_user_data_options_->date_time_range.start_date();
    changed = UserData::FieldChange::DATE_TIME_RANGE_START;
  }
  if (!user_data->date_time_range_start_timeslot_.has_value()) {
    user_data->date_time_range_start_timeslot_ =
        collect_user_data_options_->date_time_range.start_time_slot();
    changed = UserData::FieldChange::DATE_TIME_RANGE_START;
  }

  if (field_change != nullptr && changed != UserData::FieldChange::NONE) {
    *field_change = changed;
  }
}

void CollectUserDataAction::UpdateDateTimeRangeEnd(
    UserData* user_data,
    UserData::FieldChange* field_change) {
  DCHECK(user_data != nullptr);
  DCHECK(collect_user_data_options_ != nullptr);

  UserData::FieldChange changed = UserData::FieldChange::NONE;
  if (!user_data->date_time_range_end_date_.has_value()) {
    user_data->date_time_range_end_date_ =
        collect_user_data_options_->date_time_range.end_date();
    changed = UserData::FieldChange::DATE_TIME_RANGE_END;
  }
  if (!user_data->date_time_range_end_timeslot_.has_value()) {
    user_data->date_time_range_end_timeslot_ =
        collect_user_data_options_->date_time_range.end_time_slot();
    changed = UserData::FieldChange::DATE_TIME_RANGE_END;
  }

  if (field_change != nullptr && changed != UserData::FieldChange::NONE) {
    *field_change = changed;
  }
}

}  // namespace autofill_assistant
