require 'test_helper'

class FieldsDefinitionTest < ActiveSupport::TestCase

  test 'visible_for?' do
    provider_account = FactoryGirl.create(:provider_account)
    provider_user = provider_account.first_admin

    buyer_user = FactoryGirl.create(:buyer_account).first_admin
    other_provider_user = FactoryGirl.create(:provider_account).first_admin

    #
    # With a hidden field
    #
    fd = FactoryGirl.create(:fields_definition, account: provider_account, hidden: true)

    # For a buyer
    refute fd.visible_for?(buyer_user)

    # For other provider account
    refute fd.visible_for?(other_provider_user)

    # For the owner
    assert fd.visible_for?(provider_user)

    #
    # With a visible field
    #
    fd = FactoryGirl.create(:fields_definition, account: provider_account, hidden: false)

    # For a buyer
    assert fd.visible_for?(buyer_user)

    # For other provider account
    assert fd.visible_for?(other_provider_user)

    # For the owner
    assert fd.visible_for?(provider_user)
  end

  should 'not set unregistered target' do
    fd = FieldsDefinition.new
    assert FieldsDefinition.targets.exclude?("Foo")

    fd.target = "Foo"
    assert fd.target.nil?
  end

  context "validations" do
    setup do
      @provider = Factory(:provider_account)
      FieldsDefinition.delete_all(:account_id => @provider.id) # removing default ones
      @field_definition1 = FieldsDefinition.create(:account => @provider, :name => 'org_name', :target => 'Account', :label => 'foo')
    end

    should validate_presence_of :name
    should validate_presence_of :label
    should validate_presence_of :target

    should "err on equal name for the same target and provider" do
      assert_raise ActiveRecord::RecordInvalid do
        FieldsDefinition.create!(:account => @provider, :name => 'org_name', :target => 'Account', :label => 'bar2')
      end
    end

    should "not allow non alphanumeric characters in name" do
      f = FieldsDefinition.new(:account => @provider, :name => 'with space', :target => 'Account', :label => 'bar2i')
      assert !f.valid?

      f.name = "5numbercannotbefirstdigit"
      assert !f.valid?

      f.name = "_underscorecannotbefirstdigit"
      assert !f.valid?

      f.name = "-dashcannotbefirstdigit"
      assert !f.valid?

      f.name = "onlyletters"
      assert f.valid?

      f.name = "using_underscore"
      assert f.valid?

      f.name = "using-dash"
      assert f.valid?

      f.name = "using-numbers-123"
      assert f.valid?
    end

    should "allow always modifications on label" do
      assert_nothing_raised do
        FieldsDefinition.create!(:account => @provider, :target => 'User', :name => 'username', :label => 'foo')
        FieldsDefinition.create!(:account => @provider, :target => 'User', :name => 'first_name', :label => 'bar')
        FieldsDefinition.create!(:account => @provider, :target => 'User', :name => 'new_field', :label => 'baz')
      end
    end

    should "not destroy non modifiable fields" do
      assert_no_difference 'FieldsDefinition.count' do
        @field_definition1.destroy
      end
    end

    should "set required, editable and visible properties to required field on target" do
      field = FieldsDefinition.create(:account => @provider, :target => 'User', :name => 'username', :label => 'foo', :required => false, :hidden => true, :read_only => true)
      assert field.required
      assert !field.hidden
      assert !field.read_only
    end

    context "builtin fields" do

      context "required field on target" do

        should " allow choices property" do
          fd = FieldsDefinition.new(:account => @provider,
                                    :target => 'User', :name => 'username',
                                    :choices => ["opt1", "opt2"], :label => 'foo')
          assert fd.valid?
        end

      end

      context "and are modifiable fields" do
        should "not allow hidden AND required" do
          a = FieldsDefinition.new(:account => @provider, :target => 'User',
                                   :name => 'first_name',
                                   :hidden => true,
                                   :label => 'first_name',
                                   :required => true)
          assert !a.valid?
        end

        should "not allow readonly AND required" do
          a = FieldsDefinition.new(:account => @provider, :target => 'User',
                                   :name => 'first_name',
                                   :read_only => true,
                                   :required => true)
          assert !a.valid?
        end

        should "allow choices property only on string fields" do  #if type = string?
          a = FieldsDefinition.new(:account => @provider, :label => 'lal', :target => 'User', :name => 'first_name', :choices => ["a", "b"])
          assert a.valid?

          a = FieldsDefinition.new(:account => @provider, :label => 'country', :target => 'Account', :name => 'country', :choices => ["a", "b"])

          refute a.valid?
        end


      end

    end # validations

    context "choices are serialized correctly" do
      setup do
        @fd = FieldsDefinition.new(:account => @provider, :label => 'lal', :target => 'User', :name => 'first_name', :choices => ["a", "b"])
      end

      should "serialize choices as an array" do
        assert @fd.choices.is_a? Array
      end

      should "serialize choices without nesting" do
        assert_equal 'a', @fd.choices.first
      end

      should "save choices as an array on choices_for_views= " do
        @fd.choices_for_views= "changed,values"
        assert_equal 'values',  @fd.choices.last
        @fd.choices_for_views= "changed, values"
        assert_equal 'values',  @fd.choices.last
      end

      should "get choices as a string on choices_for_views" do
        @fd.choices_for_views= "changed,values"
        assert_equal 'values',  @fd.choices.last
        @fd.choices_for_views= "changed, values"
        assert_equal 'values',  @fd.choices.last
      end
    end
  end

  context  "not in database" do
    context "modificable fields" do
      should "not allow hidden AND required" do
        fd = FieldsDefinition.new(:account => @provider, :target => 'User',
                                  :name => 'new_field',
                                  :hidden => true,
                                  :label => 'lal',
                                  :required => true)
        assert !fd.valid?
        fd.hidden = false
        assert fd.valid?
      end

      should "not allow readonly AND required" do
        fd = FieldsDefinition.new(:account => @provider, :target => 'User',
                                  :name => 'new_field',
                                  :read_only => true,
                                  :label => 'lal',
                                  :required => true)
        assert !fd.valid?
        fd.read_only = false
        assert fd.valid?
      end

      should "allow choices property " do  #if type = string?
        a = FieldsDefinition.new(:account => @provider, :label => 'lal', :target => 'User', :name => 'first_name', :choices => ["a", "b"])
        assert a.valid?
      end
    end
  end

  context '.editable_by' do
    setup do
      @provider = Factory :provider_account
      FieldsDefinition.delete_all
      Factory(:fields_definition, :account => @provider, :name => "hidden",
              :hidden => true)
      Factory(:fields_definition, :account => @provider, :name => "read_only",
              :read_only => true)
      @public_field = Factory(:fields_definition, :account => @provider,
                              :name => "public")
      @buyer = Factory :buyer_account, :provider_account => @provider
    end

    should 'return all fields for provider users' do
      assert FieldsDefinition.editable_by(@provider.users.first) ==
	           FieldsDefinition.all
    end

    should 'return only public fields to buyer users' do
      assert FieldsDefinition.editable_by(@buyer.users.first) == [@public_field]
    end

    should 'return only public fields to non user' do
      assert FieldsDefinition.editable_by(nil) == [@public_field]
    end

  end

end
