# frozen_string_literal: true

module Decidim
  # This class fetches the file validation conditions from the active record
  # model objects which have specific validators and uploaders attached to them.
  # This class is used to convert these validation conditions to a human
  # readable format in the front-end and to simplify the code where these are
  # needed from.
  #
  # This considers if the validation conditions and the uploader have been set
  # directly to the record being validated or if they should be read from
  # another object in case the PassthruValidator is in charge of the
  # validations.
  class FileValidatorHumanizer
    def initialize(record, attribute)
      @record = record
      @attribute = attribute
      @passthru_validator ||= record.singleton_class.validators_on(
        attribute
      ).find { |validator| validator.is_a?(PassthruValidator) }
    end

    def uploader
      @uploader ||= passthru_uploader || record.send(attribute)
    end

    def messages
      messages = []

      if (file_size = max_file_size)
        file_size_mb = (((file_size / 1024 / 1024) * 100) / 100).round
        messages << I18n.t(
          "max_file_size",
          megabytes: file_size_mb,
          scope: "decidim.forms.file_validation"
        )
      end

      if (extensions = extension_whitelist)
        messages << I18n.t(
          "allowed_file_extensions",
          extensions: extensions.join(" "),
          scope: "decidim.forms.file_validation"
        )
      end

      messages
    end

    def max_file_size
      # First try if the record itself has a file size validator defined.
      validator = record.singleton_class.validators_on(attribute).find do |v|
        v.is_a?(::ActiveModel::Validations::FileSizeValidator)
      end
      if validator
        lte = validator.options[:less_than_or_equal_to]
        return lte if lte.is_a?(Numeric)
        return lte.call(record) if lte.respond_to?(:call)
      end
      return unless passthru_validator

      # If not, check for the same validator from the pass through record.
      validator = passthru_validator.target_validators(attribute).find do |v|
        v.is_a?(::ActiveModel::Validations::FileSizeValidator)
      end
      return unless validator

      lte = validator.options[:less_than_or_equal_to]
      return lte if lte.is_a?(Numeric)

      lte.call(passthru_record) if lte.respond_to?(:call)
    end

    def extension_whitelist
      return unless uploader.respond_to?(:extension_whitelist, true)

      # It may be a private method in some uploaders which is why we need to use
      # `#send`.
      uploader.send(:extension_whitelist)
    end

    private

    attr_reader :record, :attribute, :passthru_validator

    def passthru_record
      return unless passthru_validator

      @passthru_record ||= passthru_validator.validation_record(record)
    end

    def passthru_uploader
      return unless passthru_record

      passthru_record.send(passthru_validator.target_attribute(attribute))
    end
  end
end