require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")

DEPRECATED_APPLICATION_PARAMS = {
  :applicant_details => {
    :first_name => "Joe",
    :last_name => "Bloggs",
    :email => "joe@bloggs.com",
    :phone => "3125551234",
    :address => {
      :street_address => "123 Credibility St.",
      :postal_code => "60606",
      :locality => "Chicago",
      :region => "IL",
    },
    :date_of_birth => "10/9/1980",
    :ssn => "123-00-1234",
    :routing_number => "011103093",
    :account_number => "43759348798",
    :tax_id => "111223333",
    :company_name => "Joe's Junkyard"
  },
  :tos_accepted => true,
  :master_merchant_account_id => "sandbox_master_merchant_account"
}

VALID_APPLICATION_PARAMS = {
  :individual => {
    :first_name => "Joe",
    :last_name => "Bloggs",
    :email => "joe@bloggs.com",
    :phone => "3125551234",
    :address => {
      :street_address => "123 Credibility St.",
      :postal_code => "60606",
      :locality => "Chicago",
      :region => "IL",
    },
    :date_of_birth => "10/9/1980",
    :ssn => "123-00-1234",
  },
  :business => {
    :legal_name => "Joe's Bloggs",
    :dba_name => "Joe's Junkyard",
    :tax_id => "423456789",
    :address => {
      :street_address => "456 Fake St",
      :postal_code => "48104",
      :locality => "Ann Arbor",
      :region => "MI",
    }
  },
  :funding => {
    :destination => Braintree::MerchantAccount::FundingDestination::Bank,
    :routing_number => "011103093",
    :account_number => "43759348798",
    :descriptor => "Joes Bloggs MI",
  },
  :tos_accepted => true,
  :master_merchant_account_id => "sandbox_master_merchant_account"
}

describe Braintree::MerchantAccount do
  describe "all" do
    it "returns all merchant accounts" do
      gateway = Braintree::Gateway.new(
        :client_id => "client_id$#{Braintree::Configuration.environment}$integration_client_id",
        :client_secret => "client_secret$#{Braintree::Configuration.environment}$integration_client_secret",
        :logger => Logger.new("/dev/null"),
      )

      code = Braintree::OAuthTestHelper.create_grant(gateway, {
        :merchant_public_id => "integration_merchant_id",
        :scope => "read_write"
      })

      result = gateway.oauth.create_token_from_code(
        :code => code,
        :scope => "read_write",
      )

      gateway = Braintree::Gateway.new(
        :access_token => result.credentials.access_token,
        :logger => Logger.new("/dev/null"),
      )

      result = gateway.merchant_account.all
      result.should be_success
      result.merchant_accounts.count.should > 20
    end

    it "returns merchant account with correct attributes" do
      gateway = Braintree::Gateway.new(
        :client_id => "client_id$#{Braintree::Configuration.environment}$integration_client_id",
        :client_secret => "client_secret$#{Braintree::Configuration.environment}$integration_client_secret",
        :logger => Logger.new("/dev/null"),
      )

      result = gateway.merchant.create(
        :email => "name@email.com",
        :country_code_alpha3 => "USA",
        :payment_methods => ["credit_card", "paypal"],
      )

      gateway = Braintree::Gateway.new(
        :access_token => result.credentials.access_token,
        :logger => Logger.new("/dev/null"),
      )

      result = gateway.merchant_account.all
      result.should be_success
      result.merchant_accounts.count.should == 1
      result.merchant_accounts.first.currency_iso_code.should == "USD"
      result.merchant_accounts.first.status.should == "active"
      result.merchant_accounts.first.default.should == true
    end

    it "returns all merchant accounts for read_only scoped grants" do
      gateway = Braintree::Gateway.new(
        :client_id => "client_id$#{Braintree::Configuration.environment}$integration_client_id",
        :client_secret => "client_secret$#{Braintree::Configuration.environment}$integration_client_secret",
        :logger => Logger.new("/dev/null"),
      )

      code = Braintree::OAuthTestHelper.create_grant(gateway, {
        :merchant_public_id => "integration_merchant_id",
        :scope => "read_only"
      })

      result = gateway.oauth.create_token_from_code(
        :code => code,
        :scope => "read_only",
      )

      gateway = Braintree::Gateway.new(
        :access_token => result.credentials.access_token,
        :logger => Logger.new("/dev/null"),
      )

      result = gateway.merchant_account.all
      result.should be_success
      result.merchant_accounts.count.should > 20
    end
  end

  describe "create" do
    it "accepts the deprecated parameters" do
      result = Braintree::MerchantAccount.create(DEPRECATED_APPLICATION_PARAMS)

      result.should be_success
      result.merchant_account.status.should == Braintree::MerchantAccount::Status::Pending
      result.merchant_account.master_merchant_account.id.should == "sandbox_master_merchant_account"
    end

    it "creates a merchant account with the new parameters and doesn't require an id" do
      result = Braintree::MerchantAccount.create(VALID_APPLICATION_PARAMS)

      result.should be_success
      result.merchant_account.status.should == Braintree::MerchantAccount::Status::Pending
      result.merchant_account.master_merchant_account.id.should == "sandbox_master_merchant_account"
    end

    it "allows an id to be passed" do
      random_number = rand(10000)
      sub_merchant_account_id = "sub_merchant_account_id#{random_number}"
      result = Braintree::MerchantAccount.create(
        VALID_APPLICATION_PARAMS.merge(
          :id => sub_merchant_account_id,
        ),
      )

      result.should be_success
      result.merchant_account.status.should == Braintree::MerchantAccount::Status::Pending
      result.merchant_account.id.should == sub_merchant_account_id
      result.merchant_account.master_merchant_account.id.should == "sandbox_master_merchant_account"
    end

    it "handles unsuccessful results" do
      result = Braintree::MerchantAccount.create({})
      result.should_not be_success
      result.errors.for(:merchant_account).on(:master_merchant_account_id).first.code.should == Braintree::ErrorCodes::MerchantAccount::MasterMerchantAccountIdIsRequired
    end

    it "requires all fields" do
      result = Braintree::MerchantAccount.create(
        :master_merchant_account_id => "sandbox_master_merchant_account",
      )
      result.should_not be_success
      result.errors.for(:merchant_account).on(:tos_accepted).first.code.should == Braintree::ErrorCodes::MerchantAccount::TosAcceptedIsRequired
    end

    context "funding destination" do
      it "accepts a bank" do
        params = VALID_APPLICATION_PARAMS.dup
        params[:funding][:destination] = ::Braintree::MerchantAccount::FundingDestination::Bank
        result = Braintree::MerchantAccount.create(params)

        result.should be_success
      end

      it "accepts an email" do
        params = VALID_APPLICATION_PARAMS.dup
        params[:funding][:destination] = ::Braintree::MerchantAccount::FundingDestination::Email
        params[:funding][:email] = "joebloggs@compuserve.com"
        result = Braintree::MerchantAccount.create(params)

        result.should be_success
      end

      it "accepts a mobile_phone" do
        params = VALID_APPLICATION_PARAMS.dup
        params[:funding][:destination] = ::Braintree::MerchantAccount::FundingDestination::MobilePhone
        params[:funding][:mobile_phone] = "3125882300"
        result = Braintree::MerchantAccount.create(params)

        result.should be_success
      end
    end
  end

  describe "create!" do
    it "creates a merchant account with the new parameters and doesn't require an id" do
      merchant_account = Braintree::MerchantAccount.create!(VALID_APPLICATION_PARAMS)

      merchant_account.status.should == Braintree::MerchantAccount::Status::Pending
      merchant_account.master_merchant_account.id.should == "sandbox_master_merchant_account"
    end
  end

  describe "create_for_currency" do
    it "creates a new merchant account for currency" do
      result = SpecHelper::create_merchant
      result.should be_success

      gateway = Braintree::Gateway.new(
        :access_token => result.credentials.access_token,
        :logger => Logger.new("/dev/null"),
      )

      result = gateway.merchant_account.create_for_currency(
        :currency => "JPY",
      )
      result.should be_success
      result.merchant_account.currency_iso_code.should == "JPY"
    end

    it "returns error if a merchant account already exists for that currency" do
      result = SpecHelper::create_merchant
      result.should be_success

      gateway = Braintree::Gateway.new(
        :access_token => result.credentials.access_token,
        :logger => Logger.new("/dev/null"),
      )

      result = gateway.merchant_account.create_for_currency(
        :currency => "USD",
      )
      result.should be_success

      result = gateway.merchant_account.create_for_currency(
        :currency => "USD",
      )
      result.should_not be_success

      errors = result.errors.for(:merchant).on(:currency)
      errors[0].code.should == Braintree::ErrorCodes::Merchant::MerchantAccountExistsForCurrency
    end

    it "returns error if no currency is provided" do
      result = SpecHelper::create_merchant
      result.should be_success

      gateway = Braintree::Gateway.new(
        :access_token => result.credentials.access_token,
        :logger => Logger.new("/dev/null"),
      )

      result = gateway.merchant_account.create_for_currency(
        :currency => nil,
      )
      result.should_not be_success

      errors = result.errors.for(:merchant).on(:currency)
      errors[0].code.should == Braintree::ErrorCodes::Merchant::CurrencyIsRequired

      result = gateway.merchant_account.create_for_currency({})
      result.should_not be_success

      errors = result.errors.for(:merchant).on(:currency)
      errors[0].code.should == Braintree::ErrorCodes::Merchant::CurrencyIsRequired
    end

    it "returns error if a currency is not supported" do
      result = SpecHelper::create_merchant
      result.should be_success

      gateway = Braintree::Gateway.new(
        :access_token => result.credentials.access_token,
        :logger => Logger.new("/dev/null"),
      )

      result = gateway.merchant_account.create_for_currency(
        :currency => "FAKE_CURRENCY",
      )
      result.should_not be_success

      errors = result.errors.for(:merchant).on(:currency)
      errors[0].code.should == Braintree::ErrorCodes::Merchant::CurrencyIsInvalid
    end

    it "returns error if id is passed and already taken" do
      result = SpecHelper::create_merchant
      result.should be_success

      gateway = Braintree::Gateway.new(
        :access_token => result.credentials.access_token,
        :logger => Logger.new("/dev/null"),
      )

      merchant = result.merchant
      result = gateway.merchant_account.create_for_currency(
        :currency => "USD",
        :id => merchant.merchant_accounts.first.id,
      )
      result.should_not be_success

      errors = result.errors.for(:merchant).on(:id)
      errors[0].code.should == Braintree::ErrorCodes::Merchant::MerchantAccountExistsForId
    end
  end

  describe "find" do
    it "finds the merchant account with the given token" do
      result = Braintree::MerchantAccount.create(VALID_APPLICATION_PARAMS)
      result.should be_success
      result.merchant_account.status.should == Braintree::MerchantAccount::Status::Pending

      id = result.merchant_account.id
      merchant_account = Braintree::MerchantAccount.find(id)

      merchant_account.individual_details.first_name.should == VALID_APPLICATION_PARAMS[:individual][:first_name]
      merchant_account.individual_details.last_name.should == VALID_APPLICATION_PARAMS[:individual][:last_name]
    end

    it "retrieves the currency iso code for an existing master merchant account" do
      merchant_account = Braintree::MerchantAccount.find("sandbox_master_merchant_account")

      merchant_account.currency_iso_code.should == "USD"
    end

    it "raises a NotFoundError exception if merchant account cannot be found" do
      expect do
        Braintree::MerchantAccount.find("non-existant")
      end.to raise_error(Braintree::NotFoundError, "Merchant account with id non-existant not found")
    end
  end

  describe "update" do
    it "updates the Merchant Account info" do
      params = VALID_APPLICATION_PARAMS.clone
      params.delete(:tos_accepted)
      params.delete(:master_merchant_account_id)
      params[:individual][:first_name] = "John"
      params[:individual][:last_name] = "Doe"
      params[:individual][:email] = "john.doe@example.com"
      params[:individual][:date_of_birth] = "1970-01-01"
      params[:individual][:phone] = "3125551234"
      params[:individual][:address][:street_address] = "123 Fake St"
      params[:individual][:address][:locality] = "Chicago"
      params[:individual][:address][:region] = "IL"
      params[:individual][:address][:postal_code] = "60622"
      params[:business][:dba_name] = "James's Bloggs"
      params[:business][:legal_name] = "James's Bloggs Inc"
      params[:business][:tax_id] = "423456789"
      params[:business][:address][:street_address] = "999 Fake St"
      params[:business][:address][:locality] = "Miami"
      params[:business][:address][:region] = "FL"
      params[:business][:address][:postal_code] = "99999"
      params[:funding][:account_number] = "43759348798"
      params[:funding][:routing_number] = "071000013"
      params[:funding][:email] = "check@this.com"
      params[:funding][:mobile_phone] = "1234567890"
      params[:funding][:destination] = Braintree::MerchantAccount::FundingDestination::MobilePhone
      result = Braintree::MerchantAccount.update("sandbox_sub_merchant_account", params)
      result.should be_success
      result.merchant_account.status.should == "active"
      result.merchant_account.id.should == "sandbox_sub_merchant_account"
      result.merchant_account.master_merchant_account.id.should == "sandbox_master_merchant_account"
      result.merchant_account.individual_details.first_name.should == "John"
      result.merchant_account.individual_details.last_name.should == "Doe"
      result.merchant_account.individual_details.email.should == "john.doe@example.com"
      result.merchant_account.individual_details.date_of_birth.should == "1970-01-01"
      result.merchant_account.individual_details.phone.should == "3125551234"
      result.merchant_account.individual_details.address_details.street_address.should == "123 Fake St"
      result.merchant_account.individual_details.address_details.locality.should == "Chicago"
      result.merchant_account.individual_details.address_details.region.should == "IL"
      result.merchant_account.individual_details.address_details.postal_code.should == "60622"
      result.merchant_account.business_details.dba_name.should == "James's Bloggs"
      result.merchant_account.business_details.legal_name.should == "James's Bloggs Inc"
      result.merchant_account.business_details.tax_id.should == "423456789"
      result.merchant_account.business_details.address_details.street_address.should == "999 Fake St"
      result.merchant_account.business_details.address_details.locality.should == "Miami"
      result.merchant_account.business_details.address_details.region.should == "FL"
      result.merchant_account.business_details.address_details.postal_code.should == "99999"
      result.merchant_account.funding_details.account_number_last_4.should == "8798"
      result.merchant_account.funding_details.routing_number.should == "071000013"
      result.merchant_account.funding_details.email.should == "check@this.com"
      result.merchant_account.funding_details.mobile_phone.should == "1234567890"
      result.merchant_account.funding_details.destination.should == Braintree::MerchantAccount::FundingDestination::MobilePhone
      result.merchant_account.funding_details.descriptor.should == "Joes Bloggs MI"
    end

    it "does not require all fields" do
      result = Braintree::MerchantAccount.update("sandbox_sub_merchant_account", {:individual => {:first_name => "Jose"}})
      result.should be_success
    end

    it "handles validation errors for blank fields" do
      result = Braintree::MerchantAccount.update(
        "sandbox_sub_merchant_account", {
          :individual => {
            :first_name => "",
            :last_name => "",
            :email => "",
            :phone => "",
            :address => {
              :street_address => "",
              :postal_code => "",
              :locality => "",
              :region => "",
            },
            :date_of_birth => "",
            :ssn => "",
          },
          :business => {
            :legal_name => "",
            :dba_name => "",
            :tax_id => ""
          },
          :funding => {
            :destination => "",
            :routing_number => "",
            :account_number => ""
          },
        }
      )

      result.should_not be_success
      result.errors.for(:merchant_account).for(:individual).on(:first_name).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::FirstNameIsRequired]
      result.errors.for(:merchant_account).for(:individual).on(:last_name).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::LastNameIsRequired]
      result.errors.for(:merchant_account).for(:individual).on(:date_of_birth).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::DateOfBirthIsRequired]
      result.errors.for(:merchant_account).for(:individual).on(:email).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::EmailIsRequired]
      result.errors.for(:merchant_account).for(:individual).for(:address).on(:street_address).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::Address::StreetAddressIsRequired]
      result.errors.for(:merchant_account).for(:individual).for(:address).on(:postal_code).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::Address::PostalCodeIsRequired]
      result.errors.for(:merchant_account).for(:individual).for(:address).on(:locality).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::Address::LocalityIsRequired]
      result.errors.for(:merchant_account).for(:individual).for(:address).on(:region).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::Address::RegionIsRequired]
      result.errors.for(:merchant_account).for(:funding).on(:destination).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Funding::DestinationIsRequired]
      result.errors.for(:merchant_account).on(:base).should be_empty
    end

    it "handles validation errors for invalid fields" do
      result = Braintree::MerchantAccount.update(
        "sandbox_sub_merchant_account", {
          :individual => {
            :first_name => "<>",
            :last_name => "<>",
            :email => "bad",
            :phone => "999",
            :address => {
              :street_address => "nope",
              :postal_code => "1",
              :region => "QQ",
            },
            :date_of_birth => "hah",
            :ssn => "12345",
          },
          :business => {
            :legal_name => "``{}",
            :dba_name => "{}``",
            :tax_id => "bad",
            :address => {
              :street_address => "nope",
              :postal_code => "1",
              :region => "QQ",
            },
          },
          :funding => {
            :destination => "MY WALLET",
            :routing_number => "LEATHER",
            :account_number => "BACK POCKET",
            :email => "BILLFOLD",
            :mobile_phone => "TRIFOLD"
          },
        }
      )

      result.should_not be_success
      result.errors.for(:merchant_account).for(:individual).on(:first_name).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::FirstNameIsInvalid]
      result.errors.for(:merchant_account).for(:individual).on(:last_name).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::LastNameIsInvalid]
      result.errors.for(:merchant_account).for(:individual).on(:email).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::EmailIsInvalid]
      result.errors.for(:merchant_account).for(:individual).on(:phone).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::PhoneIsInvalid]
      result.errors.for(:merchant_account).for(:individual).for(:address).on(:street_address).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::Address::StreetAddressIsInvalid]
      result.errors.for(:merchant_account).for(:individual).for(:address).on(:postal_code).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::Address::PostalCodeIsInvalid]
      result.errors.for(:merchant_account).for(:individual).for(:address).on(:region).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::Address::RegionIsInvalid]
      result.errors.for(:merchant_account).for(:individual).on(:ssn).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Individual::SsnIsInvalid]

      result.errors.for(:merchant_account).for(:business).on(:legal_name).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Business::LegalNameIsInvalid]
      result.errors.for(:merchant_account).for(:business).on(:dba_name).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Business::DbaNameIsInvalid]
      result.errors.for(:merchant_account).for(:business).on(:tax_id).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Business::TaxIdIsInvalid]
      result.errors.for(:merchant_account).for(:business).for(:address).on(:street_address).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Business::Address::StreetAddressIsInvalid]
      result.errors.for(:merchant_account).for(:business).for(:address).on(:postal_code).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Business::Address::PostalCodeIsInvalid]
      result.errors.for(:merchant_account).for(:business).for(:address).on(:region).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Business::Address::RegionIsInvalid]

      result.errors.for(:merchant_account).for(:funding).on(:destination).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Funding::DestinationIsInvalid]
      result.errors.for(:merchant_account).for(:funding).on(:routing_number).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Funding::RoutingNumberIsInvalid]
      result.errors.for(:merchant_account).for(:funding).on(:account_number).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Funding::AccountNumberIsInvalid]
      result.errors.for(:merchant_account).for(:funding).on(:email).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Funding::EmailIsInvalid]
      result.errors.for(:merchant_account).for(:funding).on(:mobile_phone).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Funding::MobilePhoneIsInvalid]

      result.errors.for(:merchant_account).on(:base).should be_empty
    end

    it "handles validation errors for business fields" do
      result = Braintree::MerchantAccount.update(
        "sandbox_sub_merchant_account", {
          :business => {
            :legal_name => "",
            :tax_id => "111223333",
          },
        }
      )

      result.should_not be_success
      result.errors.for(:merchant_account).for(:business).on(:legal_name).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Business::LegalNameIsRequiredWithTaxId]
      result.errors.for(:merchant_account).for(:business).on(:tax_id).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Business::TaxIdMustBeBlank]

      result = Braintree::MerchantAccount.update(
        "sandbox_sub_merchant_account", {
          :business => {
            :legal_name => "legal_name",
            :tax_id => "",
          },
        }
      )

      result.should_not be_success
      result.errors.for(:merchant_account).for(:business).on(:tax_id).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Business::TaxIdIsRequiredWithLegalName]
    end

    it "handles validation errors for funding fields" do
      result = Braintree::MerchantAccount.update(
        "sandbox_sub_merchant_account", {
          :funding => {
            :destination => Braintree::MerchantAccount::FundingDestination::Bank,
            :routing_number => "",
            :account_number => ""
          },
        }
      )

      result.should_not be_success
      result.errors.for(:merchant_account).for(:funding).on(:routing_number).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Funding::RoutingNumberIsRequired]
      result.errors.for(:merchant_account).for(:funding).on(:account_number).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Funding::AccountNumberIsRequired]

      result = Braintree::MerchantAccount.update(
        "sandbox_sub_merchant_account", {
          :funding => {
            :destination => Braintree::MerchantAccount::FundingDestination::Email,
            :email => ""
          },
        }
      )

      result.should_not be_success
      result.errors.for(:merchant_account).for(:funding).on(:email).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Funding::EmailIsRequired]

      result = Braintree::MerchantAccount.update(
        "sandbox_sub_merchant_account", {
          :funding => {
            :destination => Braintree::MerchantAccount::FundingDestination::MobilePhone,
            :mobile_phone => ""
          },
        }
      )

      result.should_not be_success
      result.errors.for(:merchant_account).for(:funding).on(:mobile_phone).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Funding::MobilePhoneIsRequired]
    end
  end

  describe "update!" do
    it "updates the Merchant Account info" do
      params = VALID_APPLICATION_PARAMS.clone
      params.delete(:tos_accepted)
      params.delete(:master_merchant_account_id)
      params[:individual][:first_name] = "John"
      params[:individual][:last_name] = "Doe"
      merchant_account = Braintree::MerchantAccount.update!("sandbox_sub_merchant_account", params)
      merchant_account.individual_details.first_name.should == "John"
      merchant_account.individual_details.last_name.should == "Doe"
    end
  end
end