require "fog"

class S3Helper

  def initialize(s3_bucket, tombstone_prefix=nil, fog=nil)
    raise Discourse::InvalidParameters.new("s3_bucket") if s3_bucket.blank?

    @s3_bucket = s3_bucket
    @tombstone_prefix = tombstone_prefix

    check_missing_site_settings

    @fog = fog || Fog::Storage.new(s3_options)
  end

  def upload(file, unique_filename, options={})
    args = {
      body: file,
      key: unique_filename,
      public: false,
    }

    args.merge!(options)

    directory = get_or_create_directory(@s3_bucket)
    directory.files.create(args)
  end

  def remove(unique_filename, copy_to_tombstone=false)
    # copy the file in tombstone
    if copy_to_tombstone && @tombstone_prefix.present?
      @fog.copy_object(unique_filename, @s3_bucket, @tombstone_prefix + unique_filename, @s3_bucket)
    end
    # delete the file
    @fog.delete_object(@s3_bucket, unique_filename)
  rescue Excon::Errors::NotFound
    # if the file cannot be found, don't raise an error
  end

  def update_tombstone_lifecycle(grace_period)
    return if @tombstone_prefix.blank?
    # cf. http://docs.aws.amazon.com/AmazonS3/latest/dev/object-lifecycle-mgmt.html
    @fog.put_bucket_lifecycle(@s3_bucket, lifecycle(grace_period))
  end

  private

    def check_missing_site_settings
      unless SiteSetting.s3_use_iam_profile
        raise Discourse::SiteSettingMissing.new("s3_access_key_id") if SiteSetting.s3_access_key_id.blank?
        raise Discourse::SiteSettingMissing.new("s3_secret_access_key") if SiteSetting.s3_secret_access_key.blank?
      end
    end

    def s3_options
      options = { provider: 'AWS', scheme: SiteSetting.scheme }

      # cf. https://github.com/fog/fog/issues/2381
      options[:path_style] = dns_compatible?(@s3_bucket, SiteSetting.use_https?)

      options[:region] = SiteSetting.s3_region unless SiteSetting.s3_region.blank?

      if SiteSetting.s3_use_iam_profile
        options.merge!(use_iam_profile: true)
      else
        options.merge!(aws_access_key_id: SiteSetting.s3_access_key_id,
                       aws_secret_access_key: SiteSetting.s3_secret_access_key)
      end

      options
    end

    def get_or_create_directory(bucket)
      directory = @fog.directories.get(bucket)
      directory = @fog.directories.create(key: bucket) unless directory
      directory
    end

    def lifecycle(grace_period)
      {
        "Rules" => [{
          "Prefix" => @tombstone_prefix,
          "Enabled" => true,
          "Expiration" => { "Days" => grace_period }
        }]
      }
    end

    # cf. https://github.com/aws/aws-sdk-core-ruby/blob/master/aws-sdk-core/lib/aws-sdk-core/plugins/s3_bucket_dns.rb#L65-L80
    def dns_compatible?(bucket_name, ssl)
      return false unless valid_subdomain?(bucket_name)
      bucket_name.match(/\./) && ssl ? false : true
    end

    def valid_subdomain?(bucket_name)
      bucket_name.size < 64 &&
      bucket_name =~ /^[a-z0-9][a-z0-9.-]+[a-z0-9]$/ &&
      bucket_name !~ /(\d+\.){3}\d+/ &&
      bucket_name !~ /[.-]{2}/
    end

end