<?php

namespace PeytzMail\Tests;

use PeytzMail\PeytzMail;
use PeytzMail\Entity\Subscriber;

class SubscriberTest extends TestCase
{
    public function testASubscriberIsFound()
    {
        $json = <<<JSON
{
    "subscriber" : {
        "id"                      : "foo-bar-org",
        "email"                   : "foo@bar.org",
        "first_name"              : "Foo",
        "last_name"               : "Bar",
        "mailinglist_ids"         : [],
        "pending_mailinglist_ids" : [],
        "extra_fields"            : {
            "city"       : "Copenhagen",
            "first_name" : "Foo",
            "gender"     : "Male",
            "last_name"  : "Bar",
            "mailinglist_scores" : [{
                "_id" : "524172b5098fd8a491000002",
                "i"   : 1,
                "ll"  : "open",
                "m"   : "oplev-danmark",
                "s"   : 0.25
            }]
        }
    }
}
JSON;

        $peytzMail = new PeytzMail($this->getMockClientReturns($json));
        $subscriber = $peytzMail->getSubscriber('foo-bar-org');

        $this->assertInstanceOf('\\PeytzMail\\Entity\\Subscriber', $subscriber);
        $this->assertSame('foo@bar.org', $subscriber->email);
        $this->assertSame('Foo', $subscriber->firstName);
        $this->assertSame('Bar', $subscriber->lastName);
        $this->assertTrue(is_array($subscriber->mailinglistIds));
        $this->assertTrue(is_array($subscriber->pendingMailinglistIds));
        $this->assertTrue(is_object($subscriber->extraFields));
        $this->assertInstanceOf('stdClass', $subscriber->extraFields);
        $this->assertSame('Copenhagen', $subscriber->extraFields->city);
        $this->assertSame('Foo', $subscriber->extraFields->first_name);
        $this->assertSame('Male', $subscriber->extraFields->gender);
        $this->assertSame('Bar', $subscriber->extraFields->last_name);
        $this->assertTrue(is_array($subscriber->extraFields->mailinglist_scores));
        $this->assertSame('524172b5098fd8a491000002', $subscriber->extraFields->mailinglist_scores[0]->_id);
        $this->assertSame(1, $subscriber->extraFields->mailinglist_scores[0]->i);
        $this->assertSame('open', $subscriber->extraFields->mailinglist_scores[0]->ll);
        $this->assertSame('oplev-danmark', $subscriber->extraFields->mailinglist_scores[0]->m);
        $this->assertSame(0.25, $subscriber->extraFields->mailinglist_scores[0]->s);
    }

    public function testCannotAddSubscriberWithEmail()
    {
        $this->setExpectedException(
            '\\PeytzMail\\Exception\\InvalidArgumentException',
            '[Peytz Mail] The "email" property is mandatory for adding or updating a subscriber.'
        );

        $peytzMail = new PeytzMail($this->getStubClient());
        $peytzMail->addSubscriber(new Subscriber);
    }

    public function testCannotUpdateSubscriber()
    {
        $this->setExpectedException(
            '\\PeytzMail\\Exception\\InvalidArgumentException',
            '[Peytz Mail] The "id" property is mandatory for updating a subscriber.'
        );

        $peytzMail = new PeytzMail($this->getStubClient());
        $peytzMail->updateSubscriber(new Subscriber);
    }

    public function testSubscriberUpdated()
    {
        $updatedSubscriber = new Subscriber;
        $updatedSubscriber->id = 'foo-bar-org';
        $updatedSubscriber->email = 'new-foo@bar.org';

        $json = <<<JSON
{
    "subscriber" : {
        "id"                      : "foo-bar-org",
        "email"                   : "new-foo@bar.org",
        "first_name"              : null,
        "last_name"               : null,
        "mailinglist_ids"         : null,
        "pending_mailinglist_ids" : [],
        "extra_fields"            : {}
    }
}
JSON;

        $peytzMail = new PeytzMail($this->getMockClientReturns($json));
        $returnedSubscriber = $peytzMail->updateSubscriber($updatedSubscriber);

        $this->assertInstanceOf('\\PeytzMail\\Entity\\Subscriber', $returnedSubscriber);
        $this->assertSame('foo-bar-org', $returnedSubscriber->id);
        $this->assertSame('new-foo@bar.org', $returnedSubscriber->email);
        $this->assertNull($returnedSubscriber->firstName);
        $this->assertNull($returnedSubscriber->lastName);
        $this->assertNull($returnedSubscriber->mailinglistIds);
        $this->assertTrue(is_array($returnedSubscriber->pendingMailinglistIds));
        $this->assertTrue(empty($returnedSubscriber->pendingMailinglistIds));
        $this->assertTrue(is_object($returnedSubscriber->extraFields));
        $this->assertInstanceOf('stdClass', $returnedSubscriber->extraFields);
    }

    public function testSubscriberAdded()
    {
        $newSubscriber = new Subscriber;
        $newSubscriber->email = 'foo@bar.org';

        $json = <<<JSON
{
    "subscriber" : {
        "id"                      : "foo-bar-org",
        "email"                   : "foo@bar.org",
        "first_name"              : null,
        "last_name"               : null,
        "mailinglist_ids"         : null,
        "pending_mailinglist_ids" : [],
        "extra_fields"            : {}
    }
}
JSON;

        $peytzMail = new PeytzMail($this->getMockClientReturns($json));
        $returnedSubscriber = $peytzMail->addSubscriber($newSubscriber);

        $this->assertInstanceOf('\\PeytzMail\\Entity\\Subscriber', $returnedSubscriber);
        $this->assertSame('foo-bar-org', $returnedSubscriber->id);
        $this->assertSame('foo@bar.org', $returnedSubscriber->email);
        $this->assertNull($returnedSubscriber->firstName);
        $this->assertNull($returnedSubscriber->lastName);
        $this->assertNull($returnedSubscriber->mailinglistIds);
        $this->assertTrue(is_array($returnedSubscriber->pendingMailinglistIds));
        $this->assertTrue(empty($returnedSubscriber->pendingMailinglistIds));
        $this->assertTrue(is_object($returnedSubscriber->extraFields));
        $this->assertInstanceOf('stdClass', $returnedSubscriber->extraFields);
    }

    public function testSubscriberAddedWithDefinedProperties()
    {
        $newSubscriber             = new Subscriber;
        $newSubscriber->email      = 'foo@bar.org';
        $newSubscriber->first_name = 'Foo';
        $newSubscriber->last_name  = 'Bar';

        $json = <<<JSON
{
    "subscriber" : {
        "id"                      : "foo-bar-org",
        "email"                   : "foo@bar.org",
        "first_name"              : "Foo",
        "last_name"               : "Bar",
        "mailinglist_ids"         : null,
        "pending_mailinglist_ids" : [],
        "extra_fields"            : {}
    }
}
JSON;

        $peytzMail = new PeytzMail($this->getMockClientReturns($json));
        $returnedSubscriber = $peytzMail->addSubscriber($newSubscriber);

        $this->assertInstanceOf('\\PeytzMail\\Entity\\Subscriber', $returnedSubscriber);
        $this->assertSame('Foo', $returnedSubscriber->first_name);
        $this->assertSame('Bar', $returnedSubscriber->last_name);
    }

    public function testSubscriberAddedWithACustomProperties()
    {
        $newSubscriber          = new Subscriber;
        $newSubscriber->email   = 'foo@bar.org';
        $newSubscriber->string  = 'foobar';
        $newSubscriber->integer = 33;
        $newSubscriber->float   = 0.999;
        $newSubscriber->array   = array(
            'string'  => 'foobar',
            'integer' => 33,
            'float'   => 0.999,
            'array'   => array(
                'foo',
                'bar',
                'baz',
            ),
        );

        $json = <<<JSON
{
    "subscriber" : {
        "id"                      : "foo-bar-org",
        "email"                   : "foo@bar.org",
        "first_name"              : null,
        "last_name"               : null,
        "mailinglist_ids"         : null,
        "pending_mailinglist_ids" : [],
        "extra_fields"            : {
            "string"  : "foobar",
            "integer" : 33,
            "float"   : 0.999,
            "array"   : {
                "string"  : "foobar",
                "integer" : 33,
                "float"   : 0.999,
                "array"   : [ "foo", "bar", "baz" ]
            }
        }
    }
}
JSON;

        $peytzMail = new PeytzMail($this->getMockClientReturns($json));
        $returnedSubscriber = $peytzMail->addSubscriber($newSubscriber);

        $this->assertInstanceOf('\\PeytzMail\\Entity\\Subscriber', $returnedSubscriber);
        $this->assertTrue(is_object($returnedSubscriber->extraFields));
        $this->assertInstanceOf('stdClass', $returnedSubscriber->extraFields);
        $this->assertSame('foobar', $returnedSubscriber->extraFields->string);
        $this->assertSame(33, $returnedSubscriber->extraFields->integer);
        $this->assertSame(0.999, $returnedSubscriber->extraFields->float);
        $this->assertTrue(is_object($returnedSubscriber->extraFields->array));
        $this->assertInstanceOf('stdClass', $returnedSubscriber->extraFields->array);
        $this->assertSame('foobar', $returnedSubscriber->extraFields->array->string);
        $this->assertSame(33, $returnedSubscriber->extraFields->array->integer);
        $this->assertSame(0.999, $returnedSubscriber->extraFields->array->float);
        $this->assertTrue(is_array($returnedSubscriber->extraFields->array->array));
        $this->assertSame('foo', $returnedSubscriber->extraFields->array->array[0]);
        $this->assertSame('bar', $returnedSubscriber->extraFields->array->array[1]);
        $this->assertSame('baz', $returnedSubscriber->extraFields->array->array[2]);
    }

    public function testNoSubscribersFound()
    {
        $this->setExpectedException(
            '\\PeytzMail\\Exception\\NoResultException',
            '[Peytz Mail] No subscribers match your criteria.'
        );

        $json = <<<JSON
{
    "current_page"  : 1,
    "total_records" : 0,
    "total_pages"   : 0,
    "next_page"     : null,
    "subscribers"   : []
}
JSON;

        $peytzMail = new PeytzMail($this->getMockClientReturns($json));
        $peytzMail->findSubscribers(array());
    }

    public function testOnlyOneSubscriberFound()
    {
        $criteria = array(
            'email' => 'foo@bar.org',
        );

        $json = <<<JSON
{
    "current_page"  : 1,
    "total_records" : 1,
    "total_pages"   : 1,
    "next_page"     : null,
    "subscribers"   : [{
        "id"                      : "foo-bar-org",
        "email"                   : "foo@bar.org",
        "first_name"              : "Foo",
        "last_name"               : "Bar",
        "mailinglist_ids"         : [],
        "pending_mailinglist_ids" : [],
        "extra_fields"            : {}
    }]
}
JSON;

        $peytzMail = new PeytzMail($this->getMockClientReturns($json));
        $foundSubscribers = $peytzMail->findSubscribers($criteria);

        $this->assertTrue(is_array($foundSubscribers));
        $this->assertCount(1, $foundSubscribers);

        $foundSubscriber = $foundSubscribers[0];

        $this->assertInstanceOf('\\PeytzMail\\Entity\\Subscriber', $foundSubscriber);
        $this->assertSame('foo-bar-org', $foundSubscriber->id);
        $this->assertSame('foo@bar.org', $foundSubscriber->email);
        $this->assertSame('Foo', $foundSubscriber->firstName);
        $this->assertSame('Bar', $foundSubscriber->lastName);
        $this->assertTrue(is_array($foundSubscriber->mailinglistIds));
        $this->assertTrue(empty($foundSubscriber->mailinglistIds));
        $this->assertTrue(is_array($foundSubscriber->pendingMailinglistIds));
        $this->assertTrue(empty($foundSubscriber->pendingMailinglistIds));
        $this->assertTrue(is_object($foundSubscriber->extraFields));
        $this->assertInstanceOf('stdClass', $foundSubscriber->extraFields);
    }

    public function testManySubscribersFound()
    {
        $criteria = array(
            'first_name' => 'Foo',
        );

        $json = <<<JSON
{
    "current_page"  : 1,
    "total_records" : 2,
    "total_pages"   : 1,
    "next_page"     : null,
    "subscribers"   : [
        {
            "id"         : "foo-bar-org",
            "email"      : "foo@bar.org",
            "first_name" : "Foo",
            "last_name"  : "Bar"
        },
        {
            "id"          : "foo-baz-org",
            "email"       : "foo@baz.org",
            "first_name"  : "Foo",
            "last_name"   : "Baz"
        }
    ]
}
JSON;

        $peytzMail = new PeytzMail($this->getMockClientReturns($json));
        $foundSubscribers = $peytzMail->findSubscribers($criteria);

        $this->assertTrue(is_array($foundSubscribers));
        $this->assertCount(2, $foundSubscribers);

        $foobar = $foundSubscribers[0];
        $this->assertInstanceOf('\\PeytzMail\\Entity\\Subscriber', $foobar);
        $this->assertSame('foo-bar-org', $foobar->id);
        $this->assertSame('foo@bar.org', $foobar->email);
        $this->assertSame('Foo', $foobar->firstName);
        $this->assertSame('Bar', $foobar->lastName);

        $foobaz = $foundSubscribers[1];
        $this->assertInstanceOf('\\PeytzMail\\Entity\\Subscriber', $foobaz);
        $this->assertSame('foo-baz-org', $foobaz->id);
        $this->assertSame('foo@baz.org', $foobaz->email);
        $this->assertSame('Foo', $foobaz->firstName);
        $this->assertSame('Baz', $foobaz->lastName);
    }

    public function testMultipleSubscribersAdded()
    {
        $firstSubscriber = new Subscriber;
        $firstSubscriber->email = 'foo@bar.org';

        $secondSubscriber = new Subscriber;
        $secondSubscriber->email = 'baz@bar.org';

        $json = <<<JSON
{
    "subscribers" : [
        {
            "id"         : "foo-bar-org",
            "email"      : "foo@bar.org",
            "foreign_id" : "",
            "state"      : "ok"
        },
        {
            "id"         : "baz-bar-org",
            "email"      : "baz@bar.org",
            "foreign_id" : "",
            "state"      : "ok"
        }
    ]
}
JSON;

        $peytzMail = new PeytzMail($this->getMockClientReturns($json));
        $returnedSubscribers = $peytzMail->addSubscribers(array($firstSubscriber, $secondSubscriber));

        $this->assertTrue(is_array($returnedSubscribers));
        $this->assertCount(2, $returnedSubscribers);

        $foo = $returnedSubscribers[0];
        $this->assertInstanceOf('\\PeytzMail\\Entity\\Subscriber', $foo);
        $this->assertSame('foo-bar-org', $foo->id);
        $this->assertSame('foo@bar.org', $foo->email);
        $this->assertSame('ok', $foo->state);

        $baz = $returnedSubscribers[1];
        $this->assertInstanceOf('\\PeytzMail\\Entity\\Subscriber', $baz);
        $this->assertSame('baz-bar-org', $baz->id);
        $this->assertSame('baz@bar.org', $baz->email);
        $this->assertSame('ok', $baz->state);
    }

    public function testMultipleSubscribersAddedWithACustomProperties()
    {
        $firstSubscriber = new Subscriber;
        $firstSubscriber->email      = 'foo@bar.org';
        $firstSubscriber->foreign_id = 'my_foreign_id';
        $firstSubscriber->string     = 'foo';
        $firstSubscriber->integer    = 33;
        $firstSubscriber->float      = 0.999;
        $firstSubscriber->array      = array(
            'string'  => 'foo',
            'integer' => 33,
            'float'   => 0.999,
            'array'   => array(
                'foo',
                'bar',
                'baz',
            ),
        );

        $secondSubscriber = new Subscriber;
        $secondSubscriber->email = 'no@such.email';

        $json = <<<JSON
{
    "subscribers" : [
        {
            "id"         : "foo-bar-org",
            "email"      : "foo@bar.org",
            "foreign_id" : "my_foreign_id",
            "state"      : "ok"
        },
        {
            "email"      : "no-such-email",
            "state"      : "failed",
            "message"    : "Failed creating or updating subscriber: Email is not a valid email address (no-such-email)"
        }
    ]
}
JSON;

        $peytzMail = new PeytzMail($this->getMockClientReturns($json));
        $returnedSubscribers = $peytzMail->addSubscribers(array($firstSubscriber, $secondSubscriber));

        $this->assertTrue(is_array($returnedSubscribers));
        $this->assertCount(2, $returnedSubscribers);

        $foo = $returnedSubscribers[0];
        $this->assertInstanceOf('\\PeytzMail\\Entity\\Subscriber', $foo);
        $this->assertSame('foo-bar-org', $foo->id);
        $this->assertSame('foo@bar.org', $foo->email);
        $this->assertSame('my_foreign_id', $foo->foreignId);
        $this->assertSame('ok', $foo->state);

        $baz = $returnedSubscribers[1];
        $this->assertInstanceOf('\\PeytzMail\\Entity\\Subscriber', $baz);
        $this->assertSame('no-such-email', $baz->email);
        $this->assertSame('failed', $baz->state);
        $this->assertSame('Failed creating or updating subscriber: Email is not a valid email address (no-such-email)', $baz->message);
    }
}
