require 'test_helper'

class Logic::ProviderSignupTest < ActiveSupport::TestCase

  def setup
    service = master_account.first_service!
    @plan = Factory(:simple_application_plan, issuer: service, name: 'base' )
    master_account.account_plans.default! Factory(:account_plan, issuer: master_account, name: 'base')
    @plan.service.service_plans.default! Factory(:service_plan, issuer: service, name: 'base')
  end

  test 'should invoke third_party_notifications!' do
    Account.any_instance.expects(:third_party_notifications!)
    signup_provider
  end

  test 'should invoke set_provider_constraint' do
    @provider = signup_provider

    refute @provider.provider_constraints.can_create_service?
    refute @provider.provider_constraints.can_create_user?
  end

  test 'should not invoke third_party_notifications!' do
    Account.any_instance.expects(:third_party_notifications!).never
    signup_provider(@plan, skip_third_party_notifications: true)
  end

  test 'master with default plans should create new provider' do
    signup_provider
    assert @provider.valid?
    assert @user.valid?

    refute @provider.new_record?
    refute @user.new_record?

    assert_equal @provider.id, @provider.reload.tenant_id
    assert_equal @provider.id, @user.reload.tenant_id

    assert_equal @provider.admins.size, 1
    admin3scale = @provider.users.admins.where(username: "3scaleadmin").first
    assert admin3scale.active?
    assert @provider.has_3scaleadmin?
  end

  test 'master with default plans should create sample data' do
    signup_provider
    @provider.create_sample_data!
    assert @provider.account_plans.default
    assert_equal 1, @provider.account_plans.count
    service = @provider.first_service!
    assert service
    assert service.service_plans.default
    assert_equal 1, service.service_plans.count
    assert service.application_plans.default
    assert_equal 2, service.application_plans.count
    assert_equal "John", @provider.buyers.first.users.first.first_name
    assert_equal "Doe", @provider.buyers.first.users.first.last_name
    assert_equal 1, @provider.buyers.size
  end

  test 'sample data should be idempotent' do
    provider = signup_provider
    provider.create_sample_data!

    refute provider.sample_data

    assert_no_differences ActiveRecord::Base.subclasses do
      provider.sample_data = true
      provider.create_sample_data!
    end

    refute provider.sample_data
  end


  test 'sample data creates missing models' do
    provider = signup_provider
    provider.create_sample_data!

    create_sample_data = Logic::ProviderSignup::SampleData.new(provider).method(:create!)

    assert_difference('AccountPlan.count', -1) { provider.account_plans.each(&:delete) }
    provider.reload
    assert_difference('AccountPlan.count', +1, &create_sample_data)

    assert_difference('Account.count', -1) { provider.buyers.each(&:delete) }
    provider.reload
    assert_difference('Account.count', +1, &create_sample_data)

    assert_difference('ServicePlan.count', -1) { provider.service_plans.each(&:delete) }
    provider.reload
    assert_difference('ServicePlan.count', +1, &create_sample_data)

    assert_difference('ApplicationPlan.count', -2) { provider.application_plans.each(&:delete) }
    provider.reload
    assert_difference('ApplicationPlan.count', +2, &create_sample_data)

    assert_difference('ApiDocs::Service.count', -1) { provider.api_docs_services.each(&:delete) }
    provider.reload
    assert_difference('ApiDocs::Service.count', +1, &create_sample_data)

    assert_difference('Account.count', -1) { provider.buyers.each(&:destroy) }
    provider.reload
    assert_difference('Cinstance.count', +1, &create_sample_data)
  end

  test 'sample data does not change service updated_at' do
    provider = signup_provider

    timestamp = 1.year.ago.round

    service = provider.first_service!
    service.update_attributes!(updated_at: timestamp)

    provider.create_sample_data!

    service.reload

    assert_equal timestamp, service.updated_at
  end

  test 'first_admin should be testaccount' do
    signup_provider
    assert_equal 'testaccount', @provider.first_admin.username
  end

  test 'account should be classified' do
    signup_provider do |_provider, user|
      user.email = 'user@3scale.net'
    end

    assert_equal 'Internal', @provider.extra_fields['account_type']

    signup_provider do |_provider, user|
      user.email = 'user@example.com'
    end

    assert_equal 'Customer', @provider.extra_fields['account_type']
  end

  test 'enqueues signup job' do
    signup_provider do |provider|
      SignupWorker.expects(:enqueue).with(provider)
    end
  end

  test 'passes marketo cookie' do
    provider, _user = signup_provider(@plan, mkt_cookie: 'abcdef')

    provider.reload

    assert_equal({ marketo_cookie: 'abcdef' }, provider.extra_fields.slice(:marketo_cookie))
  end

  private

  def signup_provider(plan = @plan, options = {})
    signup = master_account.signup_provider(plan, options) do |provider, user|
      provider.attributes = {
          :org_name => 'Test account',
          :subdomain => 'test', :self_subdomain => 'test-admin'
      }
      user.attributes = {
          :password => 'testtest', :password_confirmation => 'testtest',
          :username => 'testaccount', :email => 'test@account.com'
      }

      yield(provider, user) if block_given?

      @provider, @user = provider, user
    end

    assert signup, 'could not signup provider'

    @provider
  end


  def assert_no_differences(*models, &block)
    # Not calling memo inside the rescue block will break the chain
    # so even though assertion works, it would be running just for portion of models
    # ensuring counter enforces that all models were checked
    counter = 0

    proc = models.flatten.reduce(block) do |memo, model|
      -> do
        begin
          assert_no_difference("#{model}.count", &memo)
         rescue ActiveRecord::StatementInvalid => e
           p e.message
           memo.call
        end

        counter += 1
      end
    end

    proc.call

    assert_equal models.flatten.size, counter
  end
end
