require 'fog/aws'
require 'tmpdir'

module Redshifter
  module Util
    class S3
      def upload_file(file_name:, body:, gzip: false)
        s3_url = ''

        Dir.mktmpdir('redshifter', ensure_app_tmp_directory) do |temp_dir|
          temp_file = write_temp_file(File.join(temp_dir, file_name), body, gzip)
          File.open(temp_file) do |file|
            s3_file = bucket.files.create(file_options(file, gzip))
            s3_url = internal_file_url(s3_file.key)
          end
        end

        s3_url
      end

      private

      def internal_file_url(file_name)
        "s3://#{bucket_name}/#{file_name}"
      end

      def conn
        @conn ||= Fog::Storage.new(
          provider: 'AWS',
          aws_access_key_id: Redshifter.config.aws_access_key_id,
          aws_secret_access_key: Redshifter.config.aws_secret_access_key,
          region: Redshifter.config.s3_bucket_region
        )
      end

      def bucket
        @bucket ||= conn.directories.get(bucket_name)
      end

      def bucket_name
        Redshifter.config.s3_bucket
      end

      def file_options(file, gzip)
        s3_file_options = {
          key: File.basename(file),
          body: file,
          public: false
        }

        s3_file_options.merge!(
          content_encoding: 'ASCII-8BIT',
          compression_mime_type: 'application/x-gzip'
        ) if gzip

        s3_file_options
      end

      def ensure_app_tmp_directory
        tmp_path = Redshifter.config.temp_directory_path
        Dir.mkdir(tmp_path) unless File.directory?(tmp_path)
        tmp_path
      end

      def write_temp_file(file_path, body, gzip)
        if gzip
          write_temp_gzipped_file(file_path, body)
        else
          write_temp_uncompressed_file(file_path, body)
        end
      end

      def write_temp_uncompressed_file(file_path, body)
        File.open(file_path, 'w') do |file|
          file << body
          file
        end
      end

      def write_temp_gzipped_file(file_path, body)
        gz_file_path = file_path + '.gz'
        File.open(gz_file_path, 'w', encoding: 'ASCII-8BIT') do |compressed_file|
          compressed_file.sync = true
          gzip = Zlib::GzipWriter.new(compressed_file)
          gzip.write(body)
          gzip.close # Important: Without this call, gzip headers won't be written
          compressed_file
        end
      end

    end
  end
end
