# frozen_string_literal: true

require "connection_pool"
require "redis"
require "uri"

# Adapted from Sidekiq 6.x Implementation

module Bearcat
  module RedisConnection
    KNOWN_REDIS_URL_ENVS = %w[REDIS_URL REDISTOGO_URL REDISCLOUD_URL].freeze

    class << self
      def configured?(prefix, explicit: false)
        determine_redis_provider(prefix: prefix, explicit: true)
      end

      def create(options = {})
        symbolized_options = options.transform_keys(&:to_sym)

        if !symbolized_options[:url] && (u = determine_redis_provider(prefix: options[:env_prefix]))
          symbolized_options[:url] = u
        end

        size = if symbolized_options[:size]
          symbolized_options[:size]
        elsif symbolized_options[:env_prefix] && (v = ENV["#{symbolized_options[:env_prefix]}_REDIS_POOL_SIZE"])
          Integer(v)
        elsif ENV["RAILS_MAX_THREADS"]
          Integer(ENV["RAILS_MAX_THREADS"])
        else
          5
        end

        pool_timeout = symbolized_options[:pool_timeout] || 1

        ConnectionPool.new(timeout: pool_timeout, size: size) do
          namespace = symbolized_options[:namespace]
          client = Redis.new client_opts(symbolized_options)

          if namespace
            begin
              require "redis/namespace"
              Redis::Namespace.new(namespace, redis: client)
            rescue LoadError
              Rails.logger.error("Your Redis configuration uses the namespace '#{namespace}' but the redis-namespace gem is not included in the Gemfile." \
                                   "Add the gem to your Gemfile to continue using a namespace. Otherwise, remove the namespace parameter.")
              exit(-127)
            end
          else
            client
          end
        end
      end

      private

      def client_opts(options)
        opts = options.dup
        if opts[:namespace]
          opts.delete(:namespace)
        end

        if opts[:network_timeout]
          opts[:timeout] = opts[:network_timeout]
          opts.delete(:network_timeout)
        end

        opts[:reconnect_attempts] ||= 1

        opts
      end

      def determine_redis_provider(prefix: nil, explicit: false)
        vars = []

        if prefix.present?
          if (ptr = ENV["#{prefix}_REDIS_PROVIDER"]).present?
            return ENV[ptr]
          else
            vars.push(*KNOWN_REDIS_URL_ENVS.map { |e| ENV["#{prefix}_#{e}"] })
          end
        end

        if !explicit || !prefix.present?
          if (ptr = ENV["REDIS_PROVIDER"]).present?
            vars << ptr # Intentionally not a return
          else
            vars.push(*KNOWN_REDIS_URL_ENVS)
          end
        end

        vars.select!(&:present?)

        vars.each do |e|
          return ENV[e] if ENV[e].present?
        end

        nil
      end
    end
  end
end