require 'securerandom'
require 'tmpdir'

module Unleash
  class Configuration
    attr_accessor \
      :url,
      :app_name,
      :environment,
      :instance_id,
      :custom_http_headers,
      :disable_client,
      :disable_metrics,
      :timeout,
      :retry_limit,
      :refresh_interval,
      :metrics_interval,
      :backup_file,
      :logger,
      :log_level

    def initialize(opts = {})
      ensure_valid_opts(opts)
      set_defaults

      initialize_default_logger if opts[:logger].nil?

      merge(opts)
      refresh_backup_file!
    end

    def metrics_interval_in_millis
      self.metrics_interval * 1_000
    end

    def validate!
      return if self.disable_client

      raise ArgumentError, "URL and app_name are required parameters." if self.app_name.nil? || self.url.nil?
      raise ArgumentError, "custom_http_headers must be a hash." unless self.custom_http_headers.is_a?(Hash)
    end

    def refresh_backup_file!
      self.backup_file = Dir.tmpdir + "/unleash-#{app_name}-repo.json" if self.backup_file.nil?
    end

    def http_headers
      {
        'UNLEASH-INSTANCEID' => self.instance_id,
        'UNLEASH-APPNAME' => self.app_name
      }.merge(custom_http_headers.dup)
    end

    def fetch_toggles_url
      self.url + '/client/features'
    end

    def client_metrics_url
      self.url + '/client/metrics'
    end

    def client_register_url
      self.url + '/client/register'
    end

    private

    def ensure_valid_opts(opts)
      unless opts[:custom_http_headers].is_a?(Hash) || opts[:custom_http_headers].nil?
        raise ArgumentError, "custom_http_headers must be a hash."
      end
    end

    def set_defaults
      self.app_name         = nil
      self.environment      = 'default'
      self.url              = nil
      self.instance_id      = SecureRandom.uuid
      self.disable_client   = false
      self.disable_metrics  = false
      self.refresh_interval = 10
      self.metrics_interval = 30
      self.timeout          = 30
      self.retry_limit      = 1
      self.backup_file      = nil
      self.log_level        = Logger::WARN

      self.custom_http_headers = {}
    end

    def initialize_default_logger
      self.logger = Logger.new(STDOUT)

      # on default logger, use custom formatter that includes thread_name:
      self.logger.formatter = proc do |severity, datetime, _progname, msg|
        thread_name = (Thread.current[:name] || "Unleash").rjust(16, ' ')
        "[#{datetime.iso8601(6)} #{thread_name} #{severity.ljust(5, ' ')}] : #{msg}\n"
      end
    end

    def merge(opts)
      opts.each_pair{ |opt, val| set_option(opt, val) }
      self
    end

    def set_option(opt, val)
      __send__("#{opt}=", val)
    rescue NoMethodError
      raise ArgumentError, "unknown configuration parameter '#{val}'"
    end
  end
end
