# frozen_string_literal: true

gem "shrine", "~> 3.0"
require 'shrine/storage/s3'

module FasterS3Url
  module Shrine
    # More or less a drop-in replacement for Shrine::Storage::S3 , that uses FasterS3Url faster S3 URL generation.
    # https://shrinerb.com/docs/storage/s3
    #
    #     require 'faster_s3_url/storage/shrine'
    #
    #     s3 = FasterS3Url::Shrine::Storage.new(
    #       bucket: "my-app", # required
    #       region: "eu-west-1", # required
    #       access_key_id: "abc",
    #       secret_access_key: "xyz"
    #     )
    #
    # A couple incompatibilities with Shrine::Storage::S3, which I don't expect to cause problems
    # for anyone but if they do please let me know.
    #
    #  * we do not support the :signer option in initialier (why would you want to use that with this? Let me know)
    #
    #  * We support a `host` option on initializer, but do NOT support the `host` option on #url (I don't underestand
    #  why it's per-call in the first place, do you need it to be?)
    #
    #  * You DO need to supply access_key_id and secret_access_key in initializer, they can not be automatically
    #  looked up from AWS environmental chain. See README.
    #
    class Storage < ::Shrine::Storage::S3
      # Same options as Shrine::Storage::S3, plus `host`
      def initialize(**options)
        if options[:signer]
          raise ArgumentError.new("#{self.class.name} does not support :signer option of Shrine::Storage::S3. Should it? Let us know.")
        end

        host = options.delete(:host)
        @faster_s3_url_builder = FasterS3Url::Builder.new(
          bucket_name: options[:bucket],
          access_key_id: options[:access_key_id],
          secret_access_key: options[:secret_access_key],
          region: options[:region],
          host: host)

        super(**options)
      end

      # unlike base Shrine::Storage::S3, does not support `host` here, do it in
      # initializer instead. Is there a really use case for doing it here?
      # If so let us know.
      #
      # options are ignored when public mode, so you can send options the same
      # for public or not, and not get an error on public for options only appropriate
      # to presigned.
      #
      # Otherwise, same options as Shrine::S3::Storage should be supported, please
      # see docs there. https://shrinerb.com/docs/storage/s3
      def url(id, public: self.public, **options)
        @faster_s3_url_builder.url(object_key(id), public: public, **options)
      end

      # For older shrine versions without it, we need this...
      unless self.method_defined?(:object_key) || self.private_method_defined?(:object_key)
        def object_key(id)
          [*prefix, id].join("/")
        end
      end
    end
  end
end
