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

#include "chrome/browser/keyboard_accessory/android/accessory_sheet_data.h"

#include <algorithm>
#include <ios>
#include <utility>

#include "base/base64.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/keyboard_accessory/android/accessory_sheet_enums.h"
#include "components/password_manager/core/browser/origin_credential_store.h"

namespace autofill {

AccessorySheetField::AccessorySheetField() = default;

AccessorySheetField::AccessorySheetField(const AccessorySheetField&) = default;

AccessorySheetField::AccessorySheetField(AccessorySheetField&&) = default;

AccessorySheetField::~AccessorySheetField() = default;

AccessorySheetField& AccessorySheetField::operator=(
    const AccessorySheetField&) = default;

AccessorySheetField& AccessorySheetField::operator=(AccessorySheetField&&) =
    default;

std::ostream& operator<<(std::ostream& os, const AccessorySheetField& field) {
  os << "(suggestion_type: " << std::to_underlying(field.suggestion_type())
     << ", " << "display text: \"" << field.display_text() << "\", "
     << "text_to_fill: \"" << field.text_to_fill() << "\", "
     << "a11y_description: \"" << field.a11y_description() << "\", " << "id: \""
     << field.id() << "\", " << "icon_id: \"" << field.icon_id() << "\", "
     << "is " << (field.selectable() ? "" : "not ") << "selectable, " << "is "
     << (field.is_obfuscated() ? "" : "not ") << "obfuscated)";
  return os;
}

AccessorySheetField::Builder::Builder() = default;

AccessorySheetField::Builder::~Builder() = default;

AccessorySheetField::Builder&& AccessorySheetField::Builder::SetSuggestionType(
    AccessorySuggestionType suggestion_type) && {
  accessory_sheet_field_.set_suggestion_type(suggestion_type);
  return std::move(*this);
}

AccessorySheetField::Builder&& AccessorySheetField::Builder::SetDisplayText(
    std::u16string display_text) && {
  // Calls SetDisplayText(...)& since |this| is an lvalue.
  accessory_sheet_field_.set_display_text(std::move(display_text));
  return std::move(*this);
}

AccessorySheetField::Builder&& AccessorySheetField::Builder::SetTextToFill(
    std::u16string text_to_fill) && {
  accessory_sheet_field_.set_text_to_fill(std::move(text_to_fill));
  return std::move(*this);
}

AccessorySheetField::Builder&& AccessorySheetField::Builder::SetA11yDescription(
    std::u16string a11y_description) && {
  accessory_sheet_field_.set_a11y_description(std::move(a11y_description));
  return std::move(*this);
}

AccessorySheetField::Builder&& AccessorySheetField::Builder::SetId(
    std::string id) && {
  accessory_sheet_field_.set_id(std::move(id));
  return std::move(*this);
}

AccessorySheetField::Builder&& AccessorySheetField::Builder::SetIsObfuscated(
    bool is_obfuscated) && {
  accessory_sheet_field_.set_is_obfuscated(is_obfuscated);
  return std::move(*this);
}

AccessorySheetField::Builder&& AccessorySheetField::Builder::SetSelectable(
    bool selectable) && {
  accessory_sheet_field_.set_selectable(selectable);
  return std::move(*this);
}

AccessorySheetField::Builder&& AccessorySheetField::Builder::SetIconId(
    int icon_id) && {
  accessory_sheet_field_.set_icon_id(icon_id);
  return std::move(*this);
}

AccessorySheetField&& AccessorySheetField::Builder::Build() && {
  if (accessory_sheet_field_.text_to_fill().empty()) {
    accessory_sheet_field_.set_text_to_fill(
        accessory_sheet_field_.display_text());
  }
  if (accessory_sheet_field_.a11y_description().empty()) {
    accessory_sheet_field_.set_a11y_description(
        accessory_sheet_field_.display_text());
  }
  return std::move(accessory_sheet_field_);
}

UserInfo::UserInfo() = default;

UserInfo::UserInfo(std::string origin)
    : UserInfo(std::move(origin), IsExactMatch(true)) {}

UserInfo::UserInfo(std::string origin, IsExactMatch is_exact_match)
    : UserInfo(std::move(origin),
               is_exact_match,
               GURL(),
               IsBackupCredential(false)) {}

UserInfo::UserInfo(std::string origin,
                   IsExactMatch is_exact_match,
                   IsBackupCredential is_backup_credential)
    : UserInfo(std::move(origin),
               is_exact_match,
               GURL(),
               is_backup_credential) {}

UserInfo::UserInfo(std::string origin, GURL icon_url)
    : UserInfo(std::move(origin),
               IsExactMatch(true),
               std::move(icon_url),
               IsBackupCredential(false)) {}

UserInfo::UserInfo(std::string origin,
                   IsExactMatch is_exact_match,
                   GURL icon_url,
                   IsBackupCredential is_backup_credential)
    : origin_(std::move(origin)),
      is_exact_match_(is_exact_match),
      icon_url_(std::move(icon_url)),
      is_backup_credential_(is_backup_credential) {}

UserInfo::UserInfo(const UserInfo&) = default;

UserInfo& UserInfo::operator=(const UserInfo&) = default;

UserInfo::UserInfo(UserInfo&&) = default;

UserInfo& UserInfo::operator=(UserInfo&&) = default;

UserInfo::~UserInfo() = default;

std::ostream& operator<<(std::ostream& os, const UserInfo& user_info) {
  os << "origin: \"" << user_info.origin() << "\", "
     << "is_exact_match: " << std::boolalpha << user_info.is_exact_match()
     << ", "
     << "icon_url: " << user_info.icon_url() << ","
     << "is_backup_credential: " << std::boolalpha
     << user_info.is_backup_credential() << ", "
     << "fields: [\n";
  for (const AccessorySheetField& field : user_info.fields()) {
    os << field << ", \n";
  }
  return os << "]";
}

UserInfoSection::UserInfoSection(std::u16string title)
    : title_(std::move(title)) {}

UserInfoSection::UserInfoSection(const UserInfoSection&) = default;

UserInfoSection& UserInfoSection::operator=(const UserInfoSection&) = default;

UserInfoSection::UserInfoSection(UserInfoSection&&) = default;

UserInfoSection& UserInfoSection::operator=(UserInfoSection&&) = default;

UserInfoSection::~UserInfoSection() = default;

std::ostream& operator<<(std::ostream& os, const UserInfoSection& section) {
  os << "with title: \"" << section.title() << "\" and user info list: [";
  for (const UserInfo& user_info : section.user_info_list()) {
    os << user_info << ", ";
  }
  os << "]";
  return os;
}

PasskeySection::PasskeySection(std::string display_name,
                               std::vector<uint8_t> passkey_id)
    : display_name_(std::move(display_name)),
      passkey_id_(std::move(passkey_id)) {}

PasskeySection::PasskeySection(const PasskeySection&) = default;

PasskeySection& PasskeySection::operator=(const PasskeySection&) = default;

PasskeySection::PasskeySection(PasskeySection&&) = default;

PasskeySection& PasskeySection::operator=(PasskeySection&&) = default;

PasskeySection::~PasskeySection() = default;

std::ostream& operator<<(std::ostream& os,
                         const PasskeySection& passkey_section) {
  os << "display_name: \"" << passkey_section.display_name() << "\", "
     << "passkey_id: \"" << base::Base64Encode(passkey_section.passkey_id())
     << "\"";
  return os;
}

PromoCodeInfo::PromoCodeInfo(std::u16string promo_code,
                             std::u16string details_text)
    : promo_code_(AccessorySheetField::Builder()
                      .SetSuggestionType(AccessorySuggestionType::kPromoCode)
                      .SetDisplayText(std::move(promo_code))
                      .SetSelectable(true)
                      .Build()),
      details_text_(details_text) {}

PromoCodeInfo::PromoCodeInfo(const PromoCodeInfo&) = default;

PromoCodeInfo& PromoCodeInfo::operator=(const PromoCodeInfo&) = default;

PromoCodeInfo::PromoCodeInfo(PromoCodeInfo&&) = default;

PromoCodeInfo& PromoCodeInfo::operator=(PromoCodeInfo&&) = default;

PromoCodeInfo::~PromoCodeInfo() = default;

std::ostream& operator<<(std::ostream& os,
                         const PromoCodeInfo& promo_code_info) {
  os << "promo_code: \"" << promo_code_info.promo_code() << "\", "
     << "details_text: \"" << promo_code_info.details_text() << "\"";
  return os;
}

IbanInfo::IbanInfo(std::u16string value,
                   std::u16string text_to_fill,
                   std::string id)
    : value_(AccessorySheetField::Builder()
                 .SetSuggestionType(AccessorySuggestionType::kIban)
                 .SetDisplayText(std::move(value))
                 .SetTextToFill(std::move(text_to_fill))
                 .SetId(std::move(id))
                 .SetSelectable(true)
                 .Build()) {}

IbanInfo::IbanInfo(const IbanInfo&) = default;

IbanInfo& IbanInfo::operator=(const IbanInfo&) = default;

IbanInfo::IbanInfo(IbanInfo&&) = default;

IbanInfo& IbanInfo::operator=(IbanInfo&&) = default;

IbanInfo::~IbanInfo() = default;

std::ostream& operator<<(std::ostream& os, const IbanInfo& iban_info) {
  os << "iban_info: \"" << iban_info.value() << "\"";
  return os;
}

LoyaltyCardInfo::LoyaltyCardInfo(std::string merchant_name,
                                 GURL program_logo_url,
                                 std::u16string loyalty_card_number)
    : merchant_name_(std::move(merchant_name)),
      program_logo_url_(std::move(program_logo_url)),
      value_(AccessorySheetField::Builder()
                 .SetSuggestionType(AccessorySuggestionType::kLoyaltyCard)
                 .SetDisplayText(loyalty_card_number)
                 .SetSelectable(true)
                 .Build()) {}

LoyaltyCardInfo::LoyaltyCardInfo(const LoyaltyCardInfo&) = default;

LoyaltyCardInfo& LoyaltyCardInfo::operator=(const LoyaltyCardInfo&) = default;

LoyaltyCardInfo::LoyaltyCardInfo(LoyaltyCardInfo&&) = default;

LoyaltyCardInfo& LoyaltyCardInfo::operator=(LoyaltyCardInfo&&) = default;

LoyaltyCardInfo::~LoyaltyCardInfo() = default;

std::ostream& operator<<(std::ostream& os,
                         const LoyaltyCardInfo& loyalty_card) {
  os << "merchant_name: \"" << loyalty_card.merchant_name()
     << "\", loyalty_card_number=\"" << loyalty_card.value() << "\"";
  return os;
}

FooterCommand::FooterCommand(std::u16string display_text,
                             AccessoryAction action)
    : display_text_(std::move(display_text)), accessory_action_(action) {}

FooterCommand::FooterCommand(const FooterCommand&) = default;

FooterCommand& FooterCommand::operator=(const FooterCommand&) = default;

FooterCommand& FooterCommand::operator=(FooterCommand&&) = default;

FooterCommand::FooterCommand(FooterCommand&&) = default;

FooterCommand::~FooterCommand() = default;

std::ostream& operator<<(std::ostream& os, const FooterCommand& fc) {
  return os << "(display text: \"" << fc.display_text() << "\", "
            << "action: " << static_cast<int>(fc.accessory_action()) << ")";
}

OptionToggle::OptionToggle(std::u16string display_text,
                           bool enabled,
                           AccessoryAction action)
    : display_text_(display_text),
      enabled_(enabled),
      accessory_action_(action) {}

OptionToggle::OptionToggle(const OptionToggle&) = default;

OptionToggle& OptionToggle::operator=(const OptionToggle&) = default;

OptionToggle::OptionToggle(OptionToggle&&) = default;

OptionToggle& OptionToggle::operator=(OptionToggle&&) = default;

OptionToggle::~OptionToggle() = default;

std::ostream& operator<<(std::ostream& os, const OptionToggle& ot) {
  return os << "(display text: \"" << ot.display_text() << "\", "
            << "state: " << ot.is_enabled() << ", "
            << "action: " << static_cast<int>(ot.accessory_action()) << ")";
}

std::ostream& operator<<(std::ostream& os, const AccessoryTabType& type) {
  switch (type) {
    case AccessoryTabType::PASSWORDS:
      return os << "Passwords sheet";
    case AccessoryTabType::CREDIT_CARDS:
      return os << "Payments sheet";
    case AccessoryTabType::ADDRESSES:
      return os << "Address sheet";
    case AccessoryTabType::OBSOLETE_TOUCH_TO_FILL:
      return os << "(obsolete) Touch to Fill sheet";
    case AccessoryTabType::ALL:
      return os << "All sheets";
    case AccessoryTabType::COUNT:
      return os << "Invalid sheet";
  }
  return os;
}

AccessorySheetData::AccessorySheetData(AccessoryTabType sheet_type,
                                       std::u16string user_info_title)
    : AccessorySheetData(sheet_type,
                         std::move(user_info_title),
                         std::u16string()) {}

AccessorySheetData::AccessorySheetData(AccessoryTabType sheet_type,
                                       std::u16string user_info_title,
                                       std::u16string warning)
    : sheet_type_(sheet_type),
      warning_(std::move(warning)),
      user_info_section_(std::move(user_info_title)) {}

AccessorySheetData::AccessorySheetData(const AccessorySheetData&) = default;

AccessorySheetData& AccessorySheetData::operator=(const AccessorySheetData&) =
    default;

AccessorySheetData::AccessorySheetData(AccessorySheetData&&) = default;

AccessorySheetData& AccessorySheetData::operator=(AccessorySheetData&&) =
    default;

AccessorySheetData::~AccessorySheetData() = default;

std::ostream& operator<<(std::ostream& os, const AccessorySheetData& data) {
  os << data.get_sheet_type();
  if (data.option_toggle().has_value()) {
    os << "\", with option toggle: \"" << data.option_toggle().value();
  } else {
    os << "\", with option toggle: \"none";
  }

  os << "\", warning: \"" << data.warning() << "\", and passkey list: [";
  for (const PasskeySection& passkey_section : data.passkey_section_list()) {
    os << passkey_section << ", ";
  }
  os << "], and user info section " << data.user_info_section();
  os << ", and promo code info list: [";
  for (const PromoCodeInfo& promo_code_info : data.promo_code_info_list()) {
    os << promo_code_info << ", ";
  }
  os << "], and iban info list: [";
  for (const IbanInfo& iban_info : data.iban_info_list()) {
    os << iban_info << ", ";
  }
  os << "], and loyalty card info list: [";
  for (const LoyaltyCardInfo& loyatly_card_info :
       data.loyalty_card_info_list()) {
    os << loyatly_card_info << ", ";
  }
  os << "], footer commands: [";
  for (const FooterCommand& footer_command : data.footer_commands()) {
    os << footer_command << ", ";
  }
  return os << "]";
}

AccessorySheetData::Builder::Builder(AccessoryTabType type,
                                     std::u16string user_info_title)
    : accessory_sheet_data_(type, std::move(user_info_title)) {}

AccessorySheetData::Builder::~Builder() = default;

AccessorySheetData::Builder&& AccessorySheetData::Builder::SetWarning(
    std::u16string warning) && {
  // Calls SetWarning(std::u16string warning)()& since |this| is an lvalue.
  return std::move(SetWarning(std::move(warning)));
}

AccessorySheetData::Builder& AccessorySheetData::Builder::SetWarning(
    std::u16string warning) & {
  accessory_sheet_data_.set_warning(std::move(warning));
  return *this;
}

AccessorySheetData::Builder&& AccessorySheetData::Builder::SetOptionToggle(
    std::u16string display_text,
    bool enabled,
    AccessoryAction action) && {
  // Calls SetOptionToggle(...)& since |this| is an lvalue.
  return std::move(SetOptionToggle(std::move(display_text), enabled, action));
}

AccessorySheetData::Builder& AccessorySheetData::Builder::SetOptionToggle(
    std::u16string display_text,
    bool enabled,
    AccessoryAction action) & {
  accessory_sheet_data_.set_option_toggle(
      OptionToggle(std::move(display_text), enabled, action));
  return *this;
}

AccessorySheetData::Builder&& AccessorySheetData::Builder::AddUserInfo(
    std::string origin,
    UserInfo::IsExactMatch is_exact_match,
    GURL icon_url,
    UserInfo::IsBackupCredential is_backup_credential) && {
  // Calls AddUserInfo()& since |this| is an lvalue.
  return std::move(AddUserInfo(std::move(origin), is_exact_match,
                               std::move(icon_url), is_backup_credential));
}

AccessorySheetData::Builder& AccessorySheetData::Builder::AddUserInfo(
    std::string origin,
    UserInfo::IsExactMatch is_exact_match,
    GURL icon_url,
    UserInfo::IsBackupCredential is_backup_credential) & {
  accessory_sheet_data_.add_user_info(
      UserInfo(std::move(origin), is_exact_match, std::move(icon_url),
               UserInfo::IsBackupCredential(false)));
  return *this;
}

AccessorySheetData::Builder&& AccessorySheetData::Builder::AppendSimpleField(
    AccessorySuggestionType suggestion_type,
    std::u16string text) && {
  // Calls AppendSimpleField(...)& since |this| is an lvalue.
  return std::move(AppendSimpleField(suggestion_type, std::move(text)));
}

AccessorySheetData::Builder& AccessorySheetData::Builder::AppendSimpleField(
    AccessorySuggestionType suggestion_type,
    std::u16string text) & {
  std::u16string display_text = text;
  std::u16string text_to_fill = text;
  std::u16string a11y_description = std::move(text);
  return AppendField(suggestion_type, std::move(display_text),
                     std::move(text_to_fill), std::move(a11y_description),
                     false, true);
}

AccessorySheetData::Builder&& AccessorySheetData::Builder::AppendField(
    AccessorySuggestionType suggestion_type,
    std::u16string display_text,
    std::u16string a11y_description,
    bool is_obfuscated,
    bool selectable) && {
  std::u16string text_to_fill = display_text;
  // Calls AppendField(...)& since |this| is an lvalue.
  return std::move(AppendField(
      suggestion_type, std::move(display_text), std::move(text_to_fill),
      std::move(a11y_description), is_obfuscated, selectable));
}

AccessorySheetData::Builder& AccessorySheetData::Builder::AppendField(
    AccessorySuggestionType suggestion_type,
    std::u16string display_text,
    std::u16string text_to_fill,
    std::u16string a11y_description,
    bool is_obfuscated,
    bool selectable) & {
  accessory_sheet_data_.mutable_user_info_list().back().add_field(
      AccessorySheetField::Builder()
          .SetSuggestionType(suggestion_type)
          .SetDisplayText(std::move(display_text))
          .SetTextToFill(std::move(text_to_fill))
          .SetA11yDescription(std::move(a11y_description))
          .SetIsObfuscated(is_obfuscated)
          .SetSelectable(selectable)
          .Build());
  return *this;
}

AccessorySheetData::Builder&& AccessorySheetData::Builder::AppendField(
    AccessorySuggestionType suggestion_type,
    std::u16string display_text,
    std::u16string text_to_fill,
    std::u16string a11y_description,
    std::string id,
    bool is_obfuscated,
    bool selectable) && {
  // Calls AppendField(...)& since |this| is an lvalue.
  return std::move(AppendField(
      suggestion_type, std::move(display_text), std::move(text_to_fill),
      std::move(a11y_description), std::move(id), is_obfuscated, selectable));
}

AccessorySheetData::Builder& AccessorySheetData::Builder::AppendField(
    AccessorySuggestionType suggestion_type,
    std::u16string display_text,
    std::u16string text_to_fill,
    std::u16string a11y_description,
    std::string id,
    bool is_obfuscated,
    bool selectable) & {
  accessory_sheet_data_.mutable_user_info_list().back().add_field(
      AccessorySheetField::Builder()
          .SetSuggestionType(suggestion_type)
          .SetDisplayText(std::move(display_text))
          .SetTextToFill(std::move(text_to_fill))
          .SetA11yDescription(std::move(a11y_description))
          .SetId(std::move(id))
          .SetIsObfuscated(is_obfuscated)
          .SetSelectable(selectable)
          .Build());
  return *this;
}

AccessorySheetData::Builder&& AccessorySheetData::Builder::AppendField(
    AccessorySuggestionType suggestion_type,
    std::u16string display_text,
    std::u16string text_to_fill,
    std::u16string a11y_description,
    std::string id,
    int icon_id,
    bool is_obfuscated,
    bool selectable) && {
  accessory_sheet_data_.mutable_user_info_list().back().add_field(
      AccessorySheetField::Builder()
          .SetSuggestionType(suggestion_type)
          .SetDisplayText(std::move(display_text))
          .SetTextToFill(std::move(text_to_fill))
          .SetA11yDescription(std::move(a11y_description))
          .SetId(std::move(id))
          .SetIconId(icon_id)
          .SetIsObfuscated(is_obfuscated)
          .SetSelectable(selectable)
          .Build());
  return std::move(*this);
}

AccessorySheetData::Builder&& AccessorySheetData::Builder::AddPasskeySection(
    std::string username,
    std::vector<uint8_t> credential_id) && {
  // Calls PasskeySection(...)& since |this| is an lvalue.
  return std::move(
      AddPasskeySection(std::move(username), std::move(credential_id)));
}

AccessorySheetData::Builder& AccessorySheetData::Builder::AddPasskeySection(
    std::string username,
    std::vector<uint8_t> credential_id) & {
  accessory_sheet_data_.add_passkey_section(
      (PasskeySection(std::move(username), std::move(credential_id))));
  return *this;
}

AccessorySheetData::Builder&& AccessorySheetData::Builder::AddPromoCodeInfo(
    std::u16string promo_code,
    std::u16string details_text) && {
  // Calls PromoCodeInfo(...)& since |this| is an lvalue.
  return std::move(
      AddPromoCodeInfo(std::move(promo_code), std::move(details_text)));
}

AccessorySheetData::Builder& AccessorySheetData::Builder::AddPromoCodeInfo(
    std::u16string promo_code,
    std::u16string details_text) & {
  accessory_sheet_data_.add_promo_code_info(
      (PromoCodeInfo(std::move(promo_code), std::move(details_text))));
  return *this;
}

AccessorySheetData::Builder&& AccessorySheetData::Builder::AddIbanInfo(
    std::u16string value,
    std::u16string text_to_fill,
    std::string id) && {
  // Calls IbanInfo(...)& since `this` is an lvalue.
  return std::move(
      AddIbanInfo(std::move(value), std::move(text_to_fill), std::move(id)));
}

AccessorySheetData::Builder& AccessorySheetData::Builder::AddIbanInfo(
    std::u16string value,
    std::u16string text_to_fill,
    std::string id) & {
  accessory_sheet_data_.add_iban_info(
      (IbanInfo(std::move(value), std::move(text_to_fill), std::move(id))));
  return *this;
}

AccessorySheetData::Builder&& AccessorySheetData::Builder::AddLoyaltyCardInfo(
    std::string merchant_name,
    GURL program_logo_url,
    std::u16string loyalty_card_number) && {
  // Calls AddLoyaltyCardInfo(...)& since `this` is an lvalue.
  return std::move(AddLoyaltyCardInfo(std::move(merchant_name),
                                      std::move(program_logo_url),
                                      std::move(loyalty_card_number)));
}

AccessorySheetData::Builder& AccessorySheetData::Builder::AddLoyaltyCardInfo(
    std::string merchant_name,
    GURL program_logo_url,
    std::u16string loyalty_card_number) & {
  accessory_sheet_data_.add_loyalty_card_info(
      (LoyaltyCardInfo(std::move(merchant_name), std::move(program_logo_url),
                       std::move(loyalty_card_number))));
  return *this;
}

AccessorySheetData::Builder&& AccessorySheetData::Builder::AppendFooterCommand(
    std::u16string display_text,
    AccessoryAction action) && {
  // Calls AppendFooterCommand(...)& since |this| is an lvalue.
  return std::move(AppendFooterCommand(std::move(display_text), action));
}

AccessorySheetData::Builder& AccessorySheetData::Builder::AppendFooterCommand(
    std::u16string display_text,
    AccessoryAction action) & {
  accessory_sheet_data_.add_footer_command(
      FooterCommand(std::move(display_text), action));
  return *this;
}

AccessorySheetData&& AccessorySheetData::Builder::Build() && {
  return std::move(accessory_sheet_data_);
}

}  // namespace autofill
