require 'test_helper'

class Logic::SignupTest < ActiveSupport::TestCase

  setup do
    @provider = Factory(:provider_account)
    @service = @provider.first_service!

    @provider.update_attribute :default_account_plan,  nil
    @service.update_attribute :default_service_plan,  nil
    @service.update_attribute :default_application_plan,  nil
  end

  class SignupWithPlansTest < Logic::SignupTest
    setup do
      @provider.signup_with_plans [] do |buyer, user|
        @buyer, @user = buyer, user
      end
    end

    test 'be Account and User' do
      assert_instance_of Account, @buyer
      assert_instance_of User, @user
    end

    test 'set user role to admin' do
      assert_equal :admin, @user.role
    end

    test 'set validate_fields for buyer and user' do
      assert @buyer.validate_fields?
      assert @user.validate_fields?
    end

    test 'set buyer flag' do
      assert @buyer.buyer?
    end
  end

  class WithAllDefaultPlansTest < Logic::SignupTest
    setup do
      @account_plan = Factory(:account_plan, :issuer => @provider)
      @service_plan = Factory(:service_plan, :issuer => @service)
      @application_plan = Factory(:application_plan, :issuer => @service)

      @provider.update_attribute :default_account_plan, @account_plan
      @service.update_attribute :default_service_plan, @service_plan
      @service.update_attribute :default_application_plan, @application_plan
    end

    test 'publish account created event' do
      Accounts::AccountCreatedEvent.expects(:create).once.returns({})

      @provider.signup_with_plans [] do |buyer, user|
        buyer.org_name = 'Test account'

        user.attributes = {
          password_confirmation: 'testtest',
          password: 'testtest',
          username: 'testaccount',
          email:    'test@account.com'
        }
      end
    end

    test 'create new buyer with user and all contracts' do
      @provider.signup_with_plans [] do |buyer, user|
        @buyer, @user = buyer, user

        @buyer.org_name = 'Test account'
        @user.attributes = {
          :password => 'testtest', :password_confirmation => 'testtest',
          :username => 'testaccount', :email => 'test@account.com'
        }
      end

      assert !@buyer.new_record?
      assert !@user.new_record?
      assert_equal @account_plan, @buyer.bought_account_plan
      assert_equal [@service_plan], @buyer.bought_service_plans
      assert_equal [@application_plan], @buyer.bought_application_plans
    end


    test "not signup users with same email" do
      params = {
        :org_name => 'test account',
        :username => 'testaccount', :email => 'test@account.com'
      }

      signup = {}

      2.times do |i|
        signup[i] = {}
        signup[i][:signup] = @provider.signup_with_plans [] do |buyer, user|
          signup[i][:buyer], signup[i][:user] = buyer, user

          buyer.unflattened_attributes = params

          user.signup_type = :minimal
          user.unflattened_attributes = params
        end
      end

      assert signup[0][:signup]
      refute signup[0][:buyer].new_record?
      refute signup[0][:user].new_record?

      refute signup[1][:signup]
      assert signup[1][:buyer].new_record?
      assert signup[1][:user].new_record?
    end
  end

  class PlansWithDefaultsTest < Logic::SignupTest
    setup do
      @plans = Logic::Signup::PlansWithDefaults.new @provider
    end

    context 'without default plans' do
      should 'be invalid on account plan without passed plans' do
        @plans.selected = []

        assert @plans.errors?
        assert_match /Account plan is required/i, @plans.errors.to_sentence
        assert @plans.to_a.empty?
      end

      should 'yield errors to proc' do
        errors = []
        @plans.error_proc = proc { |error| errors << error }
        @plans.selected = []

        assert @plans.errors?
        assert_equal errors, @plans.errors
      end
    end

    context 'with default account plan' do
      setup do
        @account_plan = Factory(:account_plan, :issuer => @provider)
        @provider.update_attribute :default_account_plan, @account_plan
      end

      should 'select default account plan and nothing else' do
        @plans.selected = []

        assert @plans.valid?
        assert_equal [@account_plan], @plans.to_a
      end

      context 'and default service plan' do
        setup do
          @service_plan = Factory(:service_plan, :issuer => @service)
          @service.update_attribute :default_service_plan, @service_plan
        end

        should 'select default account and service plan' do
          @plans.selected = []

          assert @plans.valid?
          assert_equal [@account_plan, @service_plan], @plans.to_a
        end

        should 'have error on service plan when tries to subscribe two plans' do
          service_plans = @service.service_plans
          @plans.selected = service_plans

          assert @plans.errors?
          assert_equal 1, @plans.errors.size
          assert_match /subscribe only one plan per service/, @plans.errors.to_sentence
        end

        context 'and default application plan' do
          setup do
            @application_plan = Factory(:application_plan, :issuer => @service)
            @service.update_attribute :default_application_plan, @application_plan
          end

          should 'select default account, service and application plan' do
            @plans.selected = []

            assert @plans.valid?
            assert_equal [@account_plan, @service_plan, @application_plan], @plans.to_a
          end
        end
      end

      context 'and default application plan without service plan' do
        setup do
          @application_plan = Factory(:application_plan, :issuer => @service)
          @service.update_attribute :default_application_plan, @application_plan
        end

        should 'select only account plan' do
          @plans.selected = []

          assert @plans.valid?
          assert_equal [@account_plan], @plans.to_a
        end

        should 'select all when passed service plan' do
          service_plan = @service.service_plans.first!
          @plans.selected = [service_plan]

          assert @plans.valid?
          assert_equal [@account_plan, service_plan, @application_plan], @plans.to_a
        end


        should 'have error on application plan when explicitly selected' do
          @plans.selected = [@application_plan]

          assert @plans.errors?
          assert_equal 1, @plans.errors.size
          assert_match /Couldn't find a Service Plan for/, @plans.errors.to_sentence
        end

        should 'work when provider has service plans disabled and default plan' do
          service_plan = @service.service_plans.first!
          @provider.settings.service_plans_ui_visible = false

          @plans.selected = []

          assert @plans.valid?
          assert_equal [ @account_plan, service_plan , @application_plan], @plans.to_a
        end

        should 'work when provider has service plans disabled' do
          service_plan = @service.service_plans.first!
          @provider.settings.service_plans_ui_visible = false

          @plans.selected = [@application_plan]

          assert @plans.valid?
          assert_equal [ @account_plan, service_plan , @application_plan], @plans.to_a
        end

        should 'have error when rolling update is not active' do
          service_plan = @service.service_plans.first!
          service_plan.update_columns(state: 'published')
          @provider.settings.service_plans_ui_visible = false

          Logic::RollingUpdates.stubs(skipped?: true)

          @plans.selected = [@application_plan]

          refute @plans.valid?
          assert_equal [ @account_plan, @application_plan ], @plans.to_a
          assert_match /Couldn't find a Service Plan/, @plans.errors.to_sentence
        end

        should 'have error with hidden service plan when provider has service plans disabled' do
          service_plan = @service.service_plans.first!
          service_plan.update_columns(state: 'hidden') # default plans used to be hidden
          @provider.settings.service_plans_ui_visible = false

          @plans.selected = [@application_plan]

          refute @plans.valid?
          assert_equal [ @account_plan, @application_plan ], @plans.to_a
          assert_match /Couldn't find a Service Plan/, @plans.errors.to_sentence
        end
      end
    end
  end

end
