require "valid_email2/address"
require "active_model"
require "active_model/validations"
require_relative "../helpers/deprecation_helper"

module ValidEmail2
  class EmailValidator < ActiveModel::EachValidator
    include DeprecationHelper

    def default_options
      { disposable: false, mx: false, strict_mx: false, disallow_subaddressing: false, multiple: false, dns_timeout: 5, dns_nameserver: nil }
    end

    def validate_each(record, attribute, value)
      return unless value.present?
      options = default_options.merge(self.options)

      addresses = sanitized_values(value).map { |v| ValidEmail2::Address.new(v, options[:dns_timeout], options[:dns_nameserver]) }

      error(record, attribute) && return unless addresses.all?(&:valid?)

      if options[:disallow_dotted]
        error(record, attribute) && return if addresses.any?(&:dotted?)
      end

      if options[:disallow_subaddressing]
        error(record, attribute) && return if addresses.any?(&:subaddressed?)
      end

      if options[:disposable]
        error(record, attribute) && return if addresses.any?(&:disposable?)
      end

      if options[:disposable_domain]
        error(record, attribute) && return if addresses.any?(&:disposable_domain?)
      end

      if options[:disposable_with_whitelist]
        deprecation_message(:disposable_with_whitelist, :disposable_with_allow_list)
      end

      if options[:disposable_with_allow_list] || options[:disposable_with_whitelist]
        error(record, attribute) && return if addresses.any? { |address| address.disposable? && !address.allow_listed? }
      end

      if options[:disposable_domain_with_whitelist]
        deprecation_message(:disposable_domain_with_whitelist, :disposable_domain_with_allow_list)
      end

      if options[:disposable_domain_with_allow_list] || options[:disposable_domain_with_whitelist]
        error(record, attribute) && return if addresses.any? { |address| address.disposable_domain? && !address.allow_listed? }
      end

      if options[:blacklist]
        deprecation_message(:blacklist, :deny_list)
      end
      
      if options[:deny_list] || options[:blacklist]
        error(record, attribute) && return if addresses.any?(&:deny_listed?)
      end

      if options[:mx]
        error(record, attribute) && return unless addresses.all?(&:valid_mx?)
      end

      if options[:strict_mx]
        error(record, attribute) && return unless addresses.all?(&:valid_strict_mx?)
      end
    end

    def sanitized_values(input)
      options = default_options.merge(self.options)

      if options[:multiple]
        email_list = input.is_a?(Array) ? input : input.split(',')
      else
        email_list = [input]
      end

      email_list.reject(&:empty?).map(&:strip)
    end

    def error(record, attribute)
      message = options[:message].respond_to?(:call) ? options[:message].call : options[:message]

      record.errors.add(attribute, message || :invalid)
    end
  end
end