<?php

namespace Omnipay\Common;

use Omnipay\Tests\TestCase;

class CreditCardTest extends TestCase
{
    /** @var CreditCard */
    private $card;

    public function setUp()
    {
        $this->card = new CreditCard;
        $this->card->setNumber('4111111111111111');
        $this->card->setFirstName('Example');
        $this->card->setLastName('Customer');
        $this->card->setExpiryMonth('4');
        $this->card->setExpiryYear(gmdate('Y')+2);
        $this->card->setCvv('123');
    }

    public function testConstructWithParams()
    {
        $card = new CreditCard(array('name' => 'Test Customer'));
        $this->assertSame('Test Customer', $card->getName());
    }

    public function testInitializeWithParams()
    {
        $card = new CreditCard;
        $card->initialize(array('name' => 'Test Customer'));
        $this->assertSame('Test Customer', $card->getName());
    }

    public function testGetParamters()
    {
        $card = new CreditCard(array(
            'name' => 'Example Customer',
            'number' => '1234',
            'expiryMonth' => 6,
            'expiryYear' => 2016,
        ));

        $parameters = $card->getParameters();
        $this->assertSame('Example', $parameters['billingFirstName']);
        $this->assertSame('Customer', $parameters['billingLastName']);
        $this->assertSame('1234', $parameters['number']);
        $this->assertSame(6, $parameters['expiryMonth']);
        $this->assertSame(2016, $parameters['expiryYear']);
    }

    public function testValidateFixture()
    {
        $this->card->validate();
    }

    /**
     * @expectedException \Omnipay\Common\Exception\InvalidCreditCardException
     * @expectedExceptionMessage The number parameter is required
     */
    public function testValidateNumberRequired()
    {
        $this->card->setNumber(null);
        $this->card->validate();
    }

    /**
     * @expectedException \Omnipay\Common\Exception\InvalidCreditCardException
     * @expectedExceptionMessage The expiryMonth parameter is required
     */
    public function testValidateExpiryMonthRequired()
    {
        $this->card->setExpiryMonth(null);
        $this->card->validate();
    }

    /**
     * @expectedException \Omnipay\Common\Exception\InvalidCreditCardException
     * @expectedExceptionMessage The expiryYear parameter is required
     */
    public function testValidateExpiryYearRequired()
    {
        $this->card->setExpiryYear(null);
        $this->card->validate();
    }

    /**
     * @expectedException \Omnipay\Common\Exception\InvalidCreditCardException
     * @expectedExceptionMessage Card has expired
     */
    public function testValidateExpiryDate()
    {
        $this->card->setExpiryYear(gmdate('Y')-1);
        $this->card->validate();
    }

    /**
     * @expectedException \Omnipay\Common\Exception\InvalidCreditCardException
     * @expectedExceptionMessage Card number is invalid
     */
    public function testValidateNumber()
    {
        $this->card->setNumber('4111111111111110');
        $this->card->validate();
    }

    public function testGetSupportedBrands()
    {
        $brands = $this->card->getSupportedBrands();
        $this->assertInternalType('array', $brands);
        $this->assertArrayHasKey(CreditCard::BRAND_VISA, $brands);
    }

    public function testCustomSupportedBrand()
    {
        $this->card->addSupportedBrand('omniexpress', '/^9\d{12}(\d{3})?$/');
        $this->assertArrayHasKey('omniexpress', $this->card->getSupportedBrands());
    }

    public function testCustomBrandWorks()
    {
        $this->card->addSupportedBrand('omniexpress', '/^9\d{12}(\d{3})?$/');
        $this->assertArrayHasKey('omniexpress', $this->card->getSupportedBrands());
        $this->card->setNumber('9111111111111110');
        $this->card->validate();
        $this->assertEquals('omniexpress', $this->card->getBrand());
    }

    public function testCustomBrandAddedTwiceReturnsFalse()
    {
        $this->assertTrue($this->card->addSupportedBrand('omniexpress', '/^9\d{12}(\d{3})?$/'));
        $this->assertArrayHasKey('omniexpress', $this->card->getSupportedBrands());
        $this->assertFalse($this->card->addSupportedBrand('omniexpress', '/^9\d{12}(\d{3})?$/'));
    }

    public function testTitle()
    {
        $this->card->setTitle('Mr.');
        $this->assertEquals('Mr.', $this->card->getTitle());
    }

    public function testFirstName()
    {
        $this->card->setFirstName('Bob');
        $this->assertEquals('Bob', $this->card->getFirstName());
    }

    public function testLastName()
    {
        $this->card->setLastName('Smith');
        $this->assertEquals('Smith', $this->card->getLastName());
    }

    public function testGetName()
    {
        $this->card->setFirstName('Bob');
        $this->card->setLastName('Smith');
        $this->assertEquals('Bob Smith', $this->card->getName());
    }

    public function testSetName()
    {
        $this->card->setName('Bob Smith');
        $this->assertEquals('Bob', $this->card->getFirstName());
        $this->assertEquals('Smith', $this->card->getLastName());
    }

    public function testSetNameWithOneName()
    {
        $this->card->setName('Bob');
        $this->assertEquals('Bob', $this->card->getFirstName());
        $this->assertEquals('', $this->card->getLastName());
    }

    public function testSetNameWithMultipleNames()
    {
        $this->card->setName('Bob John Smith');
        $this->assertEquals('Bob', $this->card->getFirstName());
        $this->assertEquals('John Smith', $this->card->getLastName());
    }

    public function testNumber()
    {
        $this->card->setNumber('4000000000000000');
        $this->assertEquals('4000000000000000', $this->card->getNumber());
    }

    public function testSetNumberStripsNonDigits()
    {
        $this->card->setNumber('4000 0000 00b00 0000');
        $this->assertEquals('4000000000000000', $this->card->getNumber());
    }

    public function testGetNumberLastFourNull()
    {
        $this->card->setNumber(null);
        $this->assertNull($this->card->getNumberLastFour());
    }

    public function testGetNumberLastFour()
    {
        $this->card->setNumber('4000000000001234');
        $this->assertSame('1234', $this->card->getNumberLastFour());
    }

    public function testGetNumberLastFourNonDigits()
    {
        $this->card->setNumber('4000 0000 0000 12x34');
        $this->assertSame('1234', $this->card->getNumberLastFour());
    }

    public function testGetNumberMasked()
    {
        $this->card->setNumber('4000000000001234');

        $this->assertSame('XXXXXXXXXXXX1234', $this->card->getNumberMasked());
    }

    public function testGetNumberMaskedNonDigits()
    {
        $this->card->setNumber('4000 0000 0000 12x34');

        $this->assertSame('XXXXXXXXXXXX1234', $this->card->getNumberMasked());
    }

    public function testGetBrandDefault()
    {
        $card = new CreditCard;
        $this->assertNull($card->getBrand());
    }

    public function testGetBrandVisa()
    {
        $card = new CreditCard(array('number' => '4242424242424242'));
        $this->assertSame(CreditCard::BRAND_VISA, $card->getBrand());
    }

    public function testGetBrandMasterCard()
    {
        $card = new CreditCard(array('number' => '5555555555554444'));
        $this->assertSame(CreditCard::BRAND_MASTERCARD, $card->getBrand());
        $card = new CreditCard(array('number' => '2230000010000006'));
        $this->assertSame(CreditCard::BRAND_MASTERCARD, $card->getBrand());
        $card = new CreditCard(array('number' => '6771890000000008'));
        $this->assertSame(CreditCard::BRAND_MASTERCARD, $card->getBrand());
    }

    public function testGetBrandAmex()
    {
        $card = new CreditCard(array('number' => '378282246310005'));
        $this->assertSame(CreditCard::BRAND_AMEX, $card->getBrand());
    }

    public function testGetBrandDiscover()
    {
        $card = new CreditCard(array('number' => '6011111111111117'));
        $this->assertSame(CreditCard::BRAND_DISCOVER, $card->getBrand());
    }

    public function testGetBrandDinersClub()
    {
        $card = new CreditCard(array('number' => '30569309025904'));
        $this->assertSame(CreditCard::BRAND_DINERS_CLUB, $card->getBrand());
    }

    public function testGetBrandJcb()
    {
        $card = new CreditCard(array('number' => '3530111333300000'));
        $this->assertSame(CreditCard::BRAND_JCB, $card->getBrand());
    }

    public function testExpiryMonth()
    {
        $this->card->setExpiryMonth(9);
        $this->assertSame(9, $this->card->getExpiryMonth());
    }

    public function testExpiryMonthLeadingZeros()
    {
        $this->card->setExpiryMonth('09');
        $this->assertSame(9, $this->card->getExpiryMonth());
    }

    public function testExpiryYear()
    {
        $this->card->setExpiryYear(2012);
        $this->assertSame(2012, $this->card->getExpiryYear());
    }

    public function testExpiryYearTwoDigits()
    {
        $this->card->setExpiryYear('12');
        $this->assertSame(2012, $this->card->getExpiryYear());
    }

    public function testExpiryDate()
    {
        $this->assertSame($this->card, $this->card->setExpiryMonth('09'));
        $this->assertSame($this->card, $this->card->setExpiryYear('2012'));
        $this->assertSame('092012', $this->card->getExpiryDate('mY'));
    }

    public function testStartMonth()
    {
        $this->card->setStartMonth(9);
        $this->assertSame(9, $this->card->getStartMonth());
    }

    public function testStartMonthLeadingZeros()
    {
        $this->card->setStartMonth('09');
        $this->assertSame(9, $this->card->getStartMonth());
    }

    public function testStartYear()
    {
        $this->card->setStartYear(2012);
        $this->assertSame(2012, $this->card->getStartYear());
    }

    public function testStartYearTwoDigits()
    {
        $this->card->setStartYear('12');
        $this->assertSame(2012, $this->card->getStartYear());
    }

    public function testStartDate()
    {
        $this->card->setStartMonth('11');
        $this->card->setStartYear('2012');
        $this->assertEquals('112012', $this->card->getStartDate('mY'));
    }

    public function testCvv()
    {
        $this->card->setCvv('456');
        $this->assertEquals('456', $this->card->getCvv());
    }

    public function testTracks()
    {
        $this->card->setTracks('%B4242424242424242^SMITH/JOHN ^1520126100000000000000444000000?;4242424242424242=15201269999944401?');
        $this->assertSame('%B4242424242424242^SMITH/JOHN ^1520126100000000000000444000000?;4242424242424242=15201269999944401?', $this->card->getTracks());
    }

    public function testShouldReturnTrack1()
    {
        $this->card->setTracks('%B4242424242424242^SMITH/JOHN ^1520126100000000000000444000000?;4242424242424242=15201269999944401?');
        $actual = $this->card->getTrack1();
        $this->assertEquals('%B4242424242424242^SMITH/JOHN ^1520126100000000000000444000000?', $actual);
    }

    public function testShouldReturnTrack2()
    {
        $this->card->setTracks('%B4242424242424242^SMITH/JOHN ^1520126100000000000000444000000?;4242424242424242=15201269999944401?');
        $actual = $this->card->getTrack2();
        $this->assertEquals(';4242424242424242=15201269999944401?', $actual);
    }

    public function testIssueNumber()
    {
        $this->card->setIssueNumber('12');
        $this->assertSame('12', $this->card->getIssueNumber());
    }

    public function testBillingTitle()
    {
        $this->card->setBillingTitle('Mrs.');
        $this->assertEquals('Mrs.', $this->card->getBillingTitle());
        $this->assertEquals('Mrs.', $this->card->getTitle());
    }

    public function testBillingFirstName()
    {
        $this->card->setBillingFirstName('Bob');
        $this->assertEquals('Bob', $this->card->getBillingFirstName());
        $this->assertEquals('Bob', $this->card->getFirstName());
    }

    public function testBillingLastName()
    {
        $this->card->setBillingLastName('Smith');
        $this->assertEquals('Smith', $this->card->getBillingLastName());
        $this->assertEquals('Smith', $this->card->getLastName());
    }

    public function testBillingName()
    {
        $this->card->setBillingFirstName('Bob');
        $this->card->setBillingLastName('Smith');
        $this->assertEquals('Bob Smith', $this->card->getBillingName());

        $this->card->setBillingName('John Foo');
        $this->assertEquals('John', $this->card->getBillingFirstName());
        $this->assertEquals('Foo', $this->card->getBillingLastName());
    }

    public function testBillingCompany()
    {
        $this->card->setBillingCompany('SuperSoft');
        $this->assertEquals('SuperSoft', $this->card->getBillingCompany());
        $this->assertEquals('SuperSoft', $this->card->getCompany());
    }

    public function testBillingAddress1()
    {
        $this->card->setBillingAddress1('31 Spooner St');
        $this->assertEquals('31 Spooner St', $this->card->getBillingAddress1());
        $this->assertEquals('31 Spooner St', $this->card->getAddress1());
    }

    public function testBillingAddress2()
    {
        $this->card->setBillingAddress2('Suburb');
        $this->assertEquals('Suburb', $this->card->getBillingAddress2());
        $this->assertEquals('Suburb', $this->card->getAddress2());
    }

    public function testBillingCity()
    {
        $this->card->setBillingCity('Quahog');
        $this->assertEquals('Quahog', $this->card->getBillingCity());
        $this->assertEquals('Quahog', $this->card->getCity());
    }

    public function testBillingPostcode()
    {
        $this->card->setBillingPostcode('12345');
        $this->assertEquals('12345', $this->card->getBillingPostcode());
        $this->assertEquals('12345', $this->card->getPostcode());
    }

    public function testBillingState()
    {
        $this->card->setBillingState('RI');
        $this->assertEquals('RI', $this->card->getBillingState());
        $this->assertEquals('RI', $this->card->getState());
    }

    public function testBillingCountry()
    {
        $this->card->setBillingCountry('US');
        $this->assertEquals('US', $this->card->getBillingCountry());
        $this->assertEquals('US', $this->card->getCountry());
    }

    public function testBillingPhone()
    {
        $this->card->setBillingPhone('12345');
        $this->assertSame('12345', $this->card->getBillingPhone());
        $this->assertSame('12345', $this->card->getPhone());
    }

    public function testBillingPhoneExtension()
    {
        $this->card->setBillingPhoneExtension('001');
        $this->assertSame('001', $this->card->getBillingPhoneExtension());
        $this->assertSame('001', $this->card->getPhoneExtension());
    }

    public function testBillingFax()
    {
        $this->card->setBillingFax('54321');
        $this->assertSame('54321', $this->card->getBillingFax());
        $this->assertSame('54321', $this->card->getFax());
    }

    public function testShippingTitle()
    {
        $this->card->setShippingTitle('Dr.');
        $this->assertEquals('Dr.', $this->card->getShippingTitle());
    }

    public function testShippingFirstName()
    {
        $this->card->setShippingFirstName('James');
        $this->assertEquals('James', $this->card->getShippingFirstName());
    }

    public function testShippingLastName()
    {
        $this->card->setShippingLastName('Doctor');
        $this->assertEquals('Doctor', $this->card->getShippingLastName());
    }

    public function testShippingName()
    {
        $this->card->setShippingFirstName('Bob');
        $this->card->setShippingLastName('Smith');
        $this->assertEquals('Bob Smith', $this->card->getShippingName());

        $this->card->setShippingName('John Foo');
        $this->assertEquals('John', $this->card->getShippingFirstName());
        $this->assertEquals('Foo', $this->card->getShippingLastName());
    }

    public function testShippingCompany()
    {
        $this->card->setShippingCompany('SuperSoft');
        $this->assertEquals('SuperSoft', $this->card->getShippingCompany());
    }

    public function testShippingAddress1()
    {
        $this->card->setShippingAddress1('31 Spooner St');
        $this->assertEquals('31 Spooner St', $this->card->getShippingAddress1());
    }

    public function testShippingAddress2()
    {
        $this->card->setShippingAddress2('Suburb');
        $this->assertEquals('Suburb', $this->card->getShippingAddress2());
    }

    public function testShippingCity()
    {
        $this->card->setShippingCity('Quahog');
        $this->assertEquals('Quahog', $this->card->getShippingCity());
    }

    public function testShippingPostcode()
    {
        $this->card->setShippingPostcode('12345');
        $this->assertEquals('12345', $this->card->getShippingPostcode());
    }

    public function testShippingState()
    {
        $this->card->setShippingState('RI');
        $this->assertEquals('RI', $this->card->getShippingState());
    }

    public function testShippingCountry()
    {
        $this->card->setShippingCountry('US');
        $this->assertEquals('US', $this->card->getShippingCountry());
    }

    public function testShippingPhone()
    {
        $this->card->setShippingPhone('12345');
        $this->assertEquals('12345', $this->card->getShippingPhone());
    }

    public function testShippingPhoneExtension()
    {
        $this->card->setShippingPhoneExtension('001');
        $this->assertEquals('001', $this->card->getShippingPhoneExtension());
    }

    public function testShippingFax()
    {
        $this->card->setShippingFax('54321');
        $this->assertEquals('54321', $this->card->getShippingFax());
    }

    public function testCompany()
    {
        $this->card->setCompany('FooBar');
        $this->assertEquals('FooBar', $this->card->getCompany());
        $this->assertEquals('FooBar', $this->card->getBillingCompany());
        $this->assertEquals('FooBar', $this->card->getShippingCompany());
    }

    public function testAddress1()
    {
        $this->card->setAddress1('31 Spooner St');
        $this->assertEquals('31 Spooner St', $this->card->getAddress1());
        $this->assertEquals('31 Spooner St', $this->card->getBillingAddress1());
        $this->assertEquals('31 Spooner St', $this->card->getShippingAddress1());
    }

    public function testAddress2()
    {
        $this->card->setAddress2('Suburb');
        $this->assertEquals('Suburb', $this->card->getAddress2());
        $this->assertEquals('Suburb', $this->card->getBillingAddress2());
        $this->assertEquals('Suburb', $this->card->getShippingAddress2());
    }

    public function testCity()
    {
        $this->card->setCity('Quahog');
        $this->assertEquals('Quahog', $this->card->getCity());
        $this->assertEquals('Quahog', $this->card->getBillingCity());
        $this->assertEquals('Quahog', $this->card->getShippingCity());
    }

    public function testPostcode()
    {
        $this->card->setPostcode('12345');
        $this->assertEquals('12345', $this->card->getPostcode());
        $this->assertEquals('12345', $this->card->getBillingPostcode());
        $this->assertEquals('12345', $this->card->getShippingPostcode());
    }

    public function testState()
    {
        $this->card->setState('RI');
        $this->assertEquals('RI', $this->card->getState());
        $this->assertEquals('RI', $this->card->getBillingState());
        $this->assertEquals('RI', $this->card->getShippingState());
    }

    public function testCountry()
    {
        $this->card->setCountry('US');
        $this->assertEquals('US', $this->card->getCountry());
        $this->assertEquals('US', $this->card->getBillingCountry());
        $this->assertEquals('US', $this->card->getShippingCountry());
    }

    public function testPhone()
    {
        $this->card->setPhone('12345');
        $this->assertEquals('12345', $this->card->getPhone());
        $this->assertEquals('12345', $this->card->getBillingPhone());
        $this->assertEquals('12345', $this->card->getShippingPhone());
    }

    public function testPhoneExtension()
    {
        $this->card->setPhoneExtension('001');
        $this->assertEquals('001', $this->card->getPhoneExtension());
        $this->assertEquals('001', $this->card->getBillingPhoneExtension());
        $this->assertEquals('001', $this->card->getShippingPhoneExtension());
    }

    public function testFax()
    {
        $this->card->setFax('54321');
        $this->assertEquals('54321', $this->card->getFax());
        $this->assertEquals('54321', $this->card->getBillingFax());
        $this->assertEquals('54321', $this->card->getShippingFax());
    }

    public function testEmail()
    {
        $this->card->setEmail('adrian@example.com');
        $this->assertEquals('adrian@example.com', $this->card->getEmail());
    }

    public function testBirthday()
    {
        $this->card->setBirthday('01-02-2000');
        $this->assertEquals('2000-02-01', $this->card->getBirthday());
        $this->assertEquals('01/02/2000', $this->card->getBirthday('d/m/Y'));
    }

    public function testBirthdayEmpty()
    {
        $this->card->setBirthday('');
        $this->assertNull($this->card->getBirthday());
    }

    public function testGender()
    {
        $this->card->setGender('female');
        $this->assertEquals('female', $this->card->getGender());
    }

    /**
     * @expectedException Omnipay\Common\Exception\InvalidCreditCardException
     * @expectedExceptionMessage Card number is invalid
     */
    public function testInvalidLuhn()
    {
        $this->card->setNumber('43');
        $this->card->validate();
    }

    /**
     * @expectedException Omnipay\Common\Exception\InvalidCreditCardException
     * @expectedExceptionMessage Card number should have 12 to 19 digits
     */
    public function testInvalidShortCard()
    {
        $this->card->setNumber('4440');
        $this->card->validate();
    }
}
