require './core/data/user_type'
require './core/utils/logger_utils'

module UserUtils

  include LoggerUtils

  # Registers a user for an RSpec Feature to be used.
  # Automatically locks and unlocks the account for parallelism security
  # @param user_type [String] The type of user to register for use
  # @param opts [Hash] opts to create the object with
  # @option opts [Boolean] :persist (false) Whether to keep the UserData object initialized for all scenario in the feature
  #   If true, the UserData object will be unlocked after all scenarios within a feature have been ran.
  #   It will not persist outside of the feature.
  # @see UserType For additional options relating to the User Type object
  # @see UserData For additional options relating to the UserData object
  # @return [UserType] The user type to use
  def register_user(user_type, opts = {})
    defaults = {persist: false}
    opts = defaults.merge(opts)

    logger.debug "[UserUtils] Preparing to register user of type: #{user_type}"
    opts[:rspec_id] = self.id
    type = UserType.new(user_type, opts)

    # TODO: Don't register these blocks for every time register_user is called. Only register once
    before(:all) { UserUtils.initialize_users(self.class.id) }
    after(:all)  { UserUtils.destroy_users(self.class.id) }

    return type
  end

  class << self

    # Initializes all UserData accounts within an example group
    # @param id [String] The RSpec ExampleGroup ID (calling .id on ExampleGroup)
    #   See http://www.rubydoc.info/gems/rspec-core/RSpec/Core/ExampleGroup#id-class_method
    # @param max_422_attempts [Int] The max attempts to wait for all users within an ExampleGroup to be unlocked
    # @param max_404_attempts [Int] The max attempts to wait for all users within an ExampleGroup to be declared in Heimdall
    #   A 404 means the user_type has not yet been registered. The server may occasionally false report a 404, but this
    #   number should be low
    def initialize_users(id, max_422_attempts=27, max_404_attempts=5)
      user_types = UserType.registered_to(id)
      max_attempts = {unavailable: max_422_attempts, not_found: max_404_attempts}
      logged_attempts = {unavailable: 0, not_found: 0}

      begin
        user_types.each { |user_type| user_type.user } # Initialize each user
      rescue HeimdallExceptions::UserNotAvailable, HeimdallExceptions::UserTypeNotFound => e
        destroy_users(id)

        if e.class == HeimdallExceptions::UserNotAvailable # TODO: Look into why case statements weren't working for this
          logged_attempts[:unavailable] += 1
        elsif e.class == HeimdallExceptions::UserTypeNotFound
          logged_attempts[:not_found] += 1
        else # Just incase...
          logged_attempts[:unavailable] += 1
        end

        # Loop through each logged attempt and raise error if any has reached its max attempts
        logged_attempts.each { |name, count| raise e if count >= max_attempts[name] }

        # If it gets here, none has reached its max attempt
        logger.warn "[UserUtils] Not all usernames were available. #{e.user_type.type} was unavailable."\
            " Released and going into the quiet period. Attempts: #{logged_attempts}"
        sleep(5)
        retry
      end
    end

    # Destroys all UserData accounts within an example group
    # @param id [String] The RSpec ExampleGroup ID (calling .id on ExampleGroup)
    #   See http://www.rubydoc.info/gems/rspec-core/RSpec/Core/ExampleGroup#id-class_method
    def destroy_users(id)
      UserType.registered_to(id).each { |user_type| user_type.destroy }
    end
  end
end
