module Lopata::RSpec::Role
  def self.included(base)
    base.extend(ClassMethods)
  end

  # Filter names
  def self.filter_roles *names
    allowed = Lopata::Config.only_roles
    selected = names.flatten.select { |n| allowed.blank? || allowed.member?(n) }
    # ENV['quick'] ? [selected.first] : selected
    selected
  end

  # http://jorgemanrubia.net/2010/01/16/using-macros-to-create-custom-example-groups-in-rspec/
  module ClassMethods
    def as *names, &block
      return if current_role && !Lopata::RSpec::Role.filter_roles(*names).include?(current_role)
      if current_role
        self.class_eval(&block)
      else
        Lopata::RSpec::Role.filter_roles(*names).each do |name|
          example_group_class = describe role_description(name), :current_role => name do
            instance_exec &Lopata::Config.after_as if Lopata::Config.after_as
            define_method :current_role do
              name
            end
          end
          example_group_class.class_eval(&block)
        end
      end
    end

    def except(*names, &block)
      raise "'expecpt' block must be neseted for 'as' block" unless current_role
      return if names.include? current_role
      self.class_eval(&block)
    end

    def current_role
      metadata[:current_role]
    end

    # To be redefined in impelemntations so RSpec descriptions to be more verbal
    def role_description(name)
      Lopata::Config.role_descriptions[name] || name
    end

    def scenario(*args, &block)
      raise "scenario required a name in first argument" unless args.first.is_a? String
      example_group_class = describe(*args)
      example_group_class.nested_with_as(*args, &block)
    end

    def nested_with_as(*args, &block)
      if (args.last.is_a?(Hash) && args.last[:as])
        roles = args.last[:as]
        roles = [roles] unless roles.is_a?(Array)
        class_eval { as(*roles, &block) }
      else
        class_eval(&block)
      end
    end
  end
end

module Lopata
  # Adds the #scenario method to the top-level namespace.
  def self.scenario(*args, &block)
    raise "scenario required a name in first argument" unless args.first.is_a? String
    example_group_class = RSpec.describe(*args)
    example_group_class.nested_with_as(*args, &block)
    # example_group_class.register
  end
end