require 'marcel'

module Carrierwave
  module Base64
    # Class that decodes a base64 string, builds a StringIO for the
    # decoded bytes, and extracts the file MIME type to build a file
    # name with extension.
    class Base64StringIO < StringIO
      # @return [String] the file name without extension
      attr_reader :file_name

      # @return [String] the file extension for the uploaded file
      attr_reader :file_extension

      # Returns a StringIO with decoded bytes from the base64 encoded
      # string and builds a file name with extension for the uploaded file,
      # based on the MIME type specified in the base64 encoded string.
      #
      # @param encoded_file [String] the base64 encoded file contents
      # @param file_name [String] the file name without extention
      #
      # @raise [ArgumentError] If the base64 encoded string is empty
      #
      # @return [StringIO] StringIO with decoded bytes
      def initialize(encoded_file, file_name)
        description, encoded_bytes = encoded_file.split(',')

        raise ArgumentError unless encoded_bytes
        raise ArgumentError if encoded_bytes.eql?('(null)')

        @file_name = file_name
        bytes = ::Base64.decode64 encoded_bytes
        @file_extension = get_file_extension description, bytes

        super bytes
      end

      # Returns a file name with extension, based on the MIME type specified
      # in the base64 encoded string.
      #
      # @return [String] File name with extention
      def original_filename
        File.basename("#{@file_name}.#{@file_extension}")
      end

      private

      # Determine content type from input, with provided type as fallback
      def get_file_extension(description, bytes)
        detected_type = Marcel::Magic.by_magic(bytes)
        content_type = (detected_type && detected_type.type) ||
                       description.split(';base64').first
        mime_type = MIME::Types[content_type].last
        unless mime_type
          raise Carrierwave::Base64::UnknownMimeTypeError,
                "Unknown MIME type: #{content_type}"
        end
        mime_type.preferred_extension
      end
    end
  end
end
