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

class UserType

  @registered_users = {}

  class << self
    attr_reader :registered_users

    # A method for registering UserTypes to 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 user_type [UserType] The UserType object to be associated to the RSpec ExampleGroup
    def register_user(id, user_type)
      if @registered_users.has_key?(id)
        @registered_users[id] << user_type
      else
        @registered_users[id] = [user_type]
      end
    end

    # @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
    # @return [Array] if users are registered to the RSpec Example Group
    def registered_to(id)
      if @registered_users.has_key?(id)
        return @registered_users[id]
      else
        return [] # No users registered, return an empty array so that .each calls don't break
      end
    end
  end

  DEFAULTS = {persist: false}

  attr_reader :type, :persist, :rspec_id, :user_creation_timings

  include LoggerUtils

  # @param type [String] The type of user to declare
  # @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.
  # @return [UserType]
  def initialize(type, opts = {})
    @opts = DEFAULTS.merge(opts)

    @type = type
    @persist = @opts[:persist]
    @rspec_id = @opts[:rspec_id]
    @user_creation_timings = {start_time: nil, end_time: nil}

    UserType.register_user(@rspec_id, self)

    logger.debug "[UserType] Initialized: #{type}"
  end

  # @return [UserData] An available user of that type
  def user
    unless @user # Create the UserData object unless @user already exists
      @user_creation_timings[:start_time] = Time.now if @user_creation_timings[:start_time].nil?
      @user = UserData.new(self, @opts)
      @user_creation_timings[:end_time] = Time.now
    end

    return @user
  end

  # @return [Boolean] Whether a User has been initialized
  def user_initialized?
    return !!@user
  end

  # @return [Boolean] Whether the account should persist across an entire feature
  def persist?
    return !!@persist
  end

  # @return [NilClass] if no user duration was recorded
  # @return [Int] The duration it took for a user to be fetched from Heimdall
  def user_creation_duration
    return nil if @user_creation_timings[:end_time].nil? || @user_creation_timings[:start_time].nil?
    return @user_creation_timings[:end_time] - @user_creation_timings[:start_time]
  end

  # Unlocks and Destroys a user account association
  # @return [Boolean] Whether the destroy was successful
  def destroy
    return false unless user_initialized? # Return if the user hasn't been initialized
    logger.debug "[UserType] Destroying #{@user}"
    result = @user.unlock
    @user = nil

    return result
  end

end
