RSpec.shared_examples_for "a dunlop application" do |application_specific_options|
  DunlopTestKeyValuePair = Struct.new(:key, :value)
  let(:default_dunlop_test_options) {{
    allow_duplicate_model_names: false
  }}
  let(:dunlop_test_options) { default_dunlop_test_options.merge(application_specific_options || {}) }

  def test_duplicate_model_names(models)
    return if dunlop_test_options[:allow_duplicate_model_names]
    singular_names = models.map{|cls| DunlopTestKeyValuePair.new(cls.name, cls.model_name.human) }
    duplicate_singular_names = singular_names.group_by(&:value).select { |k, vs| vs.size > 1 }
    if duplicate_singular_names.present?
      message = duplicate_singular_names.map{|name, pairs| "The models #{pairs.map(&:key).join(', ')} have a duplicate singular name #{name}"}.join("\n")
      duplicate_singular_names.should be_empty, message
    end

    plural_names = models.map{|cls| DunlopTestKeyValuePair.new(cls.name, cls.model_name.human_plural) }
    duplicate_plural_names = plural_names.group_by(&:value).select { |k, vs| vs.size > 1 }
    if duplicate_plural_names.present?
      message = duplicate_plural_names.map{|name, pairs| "The models #{pairs.map(&:key).join(', ')} have a duplicate plural name #{name}"}.join("\n")
      duplicate_plural_names.should be_empty, message
    end
  end

  def check_model_columns(model, columns, options = {})
    invalid_columns = []
    columns.each do |name, spec|
      spec = {type: spec} if spec.is_a?(Symbol)
      invalid_columns.push "column #{name} with #{spec.map{|k,v| "#{k}: #{v}"}.join(', ')} is missing" and next unless column = model.columns_hash[name.to_s]
      invalid_columns.push "column #{name} has type #{column.type} but type #{spec[:type]} was expected" unless column.type == spec[:type]
    end
    invalid_columns.should(be_empty, "The model #{model.name} is invalid\n  #{invalid_columns.join("\n  ")}\n#{options[:extra_message]}")
  end

  it "has an application name" do
    Rails.application.config.application_name.should be_present
  end

  it "has a user admin column" do
    check_model_columns User, {
      admin: :boolean
    }, extra_message: <<-MIGRATION

    create a migration: bundle exec rails generate migration add_admin_to_users admin:boolean

      add_column :users, :admin, :boolean, null: false, default: false
    MIGRATION
  end

  it "has token-authentication columns" do
    check_model_columns User, {
      authentication_token: :string,
      authentication_token_created_at: :datetime
    }, extra_message: <<-MIGRATION

    create a migration: bundle exec rails generate migration add_token_authenticatable_columns_to_users

      add_column :users, :authentication_token, :string
      add_column :users, :authentication_token_created_at, :datetime
      add_index  :users, :authentication_token

    and don't forget to add:
      devise :database_authenticatable, :token_authenticatable, ...
    to the User's model to activate it
    MIGRATION
  end

  if Dunlop.has_source_files?
    it "has proper source_file columns" do
      check_model_columns(SourceFile,
        creator_id: :integer,
        creator_type: :string,
      )
    end

    it "has a unique translation for all source files" do
      test_duplicate_model_names SourceFile.classes
    end
  end

  if Dunlop.has_target_files?
    it "has a unique translation for all target files" do
      test_duplicate_model_names TargetFile.classes
    end
  end

  it "has proper log_entry columns" do
      check_model_columns(Dunlop::LogEntry,
        body: :text,
        backtrace: :text,
      )
  end
end

