class ProfileValidator < ActiveModel::Validator
  SUMMARY_ATTRIBUTE = "summary".freeze
  MAX_SUMMARY_LENGTH = 200

  MAX_TEXT_AREA_LENGTH = 200
  MAX_TEXT_FIELD_LENGTH = 100

  def errors
    {
      text_area: I18n.t("errors.messages.too_long", count: MAX_TEXT_AREA_LENGTH),
      text_field: I18n.t("errors.messages.too_long", count: MAX_TEXT_FIELD_LENGTH)
    }.with_indifferent_access
  end

  def validate(record)
    # NOTE: The summary is a base profile field, which we add to all new Forem
    # instances, so it should be safe to validate. The method itself also guards
    # against the field's absence.
    if summary_too_long?(record)
      record.errors.add(:base,
                        message: I18n.t("validators.profile_validator.bio_too_long"))
    end

    ProfileField.all.each do |field|
      attribute = field.attribute_name
      next if attribute == SUMMARY_ATTRIBUTE # validated above
      next unless record.respond_to?(attribute) # avoid caching issues
      next if __send__("#{field.input_type}_valid?", record, attribute)

      record.errors.add(attribute, errors[field.input_type])
    end
  end

  private

  def summary_too_long?(record)
    record.summary&.gsub!(/\r\n/, "\n")
    return if record.summary.blank?

    # Grandfather in people who had a too long summary before
    previous_summary = record.summary_was
    previous_summary&.gsub!(/\r\n/, "\n")
    return if previous_summary && previous_summary.size > MAX_SUMMARY_LENGTH

    record.summary.size > MAX_SUMMARY_LENGTH
  end

  def check_box_valid?(_record, _attribute)
    true # checkboxes are always valid
  end

  def text_area_valid?(record, attribute)
    text = record.public_send(attribute)
    text = remove_inner_newlines(text)
    text.nil? || text.size <= MAX_TEXT_AREA_LENGTH
  end

  def text_field_valid?(record, attribute)
    text = record.public_send(attribute)
    text.nil? || text.size <= MAX_TEXT_FIELD_LENGTH
  end

  def remove_inner_newlines(text)
    text.presence && text.tr("\r\n\t", " ").squeeze(" ")
  end
end
