<?php

namespace ActivityPhpTest\Type;

use ActivityPhpTest\MyCustomType;
use ActivityPhpTest\MyCustomValidator;
use ActivityPhp\Type;
use Exception;
use PHPUnit\Framework\TestCase;

class FactoryTest extends TestCase
{
	/**
	 * Valid scenarios provider
	 */
	public function getShortTypes()
	{
		return [
		    ['Activity'],
		    ['Collection'],
		    ['CollectionPage'],
		    ['IntransitiveActivity'],
		    ['Link'],
		    ['ObjectType'],
		    ['Object'],
		    ['OrderedCollection'],
		    ['OrderedCollectionPage'],
		    ['Application'],
		    ['Group'],
		    ['Organization'],
		    ['Person'],
		    ['Service'],
		    ['Accept'],
		    ['Add'],
		    ['Announce'],
		    ['Arrive'],
		    ['Block'],
		    ['Create'],
		    ['Delete'],
		    ['Dislike'],
		    ['Flag'],
		    ['Follow'],
		    ['Ignore'],
		    ['Invite'],
		    ['Join'],
		    ['Leave'],
		    ['Like'],
		    ['Listen'],
		    ['Move'],
		    ['Offer'],
		    ['Question'],
		    ['Read'],
		    ['Reject'],
		    ['Remove'],
		    ['TentativeAccept'],
		    ['TentativeReject'],
		    ['Travel'],
		    ['Undo'],
		    ['Update'],
		    ['View'],
		    ['Article'],
		    ['Audio'],
		    ['Document'],
		    ['Event'],
		    ['Image'],
		    ['Mention'],
		    ['Note'],
		    ['Page'],
		    ['Place'],
		    ['Profile'],
		    ['Relationship'],
		    ['Tombstone'],
		    ['Video'],
		];
	}


	/**
	 * Check that all core objects have a correct type property.
	 *
	 * @dataProvider getShortTypes
	 */
	public function testShortTypesInstanciation($type)
	{
		$class = Type::create($type, ['name' => strtolower($type)]);

		// Assert affectation
		$this->assertEquals(
		    strtolower($type),
		    $class->name
		);

		// Object has two names: Object and ObjectType
		if ($type == 'ObjectType') {
		    $type = 'Object';
		}

		// Assert type property
		$this->assertEquals(
		    $type,
		    $class->type
		);
	}


	/**
	 * Scenario for an undefined type
	 */
	public function testUndefinedType()
	{
		$this->expectException(Exception::class);

		$class = Type::create('UndefinedType');
	}


	/**
	 * Scenario for a custom validator
	 *
	 * - Add a validator in the pool for 'customProperty' attribute
	 * - Create a type with this property and affect a correct value
	 */
	public function testCustomValidatorSuccess()
	{
		Type::add('MyCustomType', MyCustomType::class);
		Type::addValidator('customProperty', MyCustomValidator::class);
		$type = Type::create(
		    'MyCustomType',
		    ['customProperty' => 'My value']
		);

		// Assert type property
		$this->assertEquals(
		    'My value',
		    $type->customProperty
		);
	}


	/**
	 * Scenario for instanciating a Type with a single array parameter
	 */
	public function testShortCallSuccess()
	{
		$type = Type::create([
		    'type' => 'Note',
		    'id' => 'http://example.org/missing-type'
		]);

		// Assert type property
		$this->assertEquals(
		    'Note',
		    $type->type
		);

		// Assert another property
		$this->assertEquals(
		    'http://example.org/missing-type',
		    $type->id
		);
	}


	/**
	 * Scenario for instanciating a Type with a single array parameter
	 * for a failing value (missing type property)
	 */
	public function testShortCallFailing()
	{
		$this->expectException(Exception::class);

		$type = Type::create(
		    ['id' => 'http://example.org/missing-type']
		);
	}


	/**
	 * Scenario for instanciating a Type with a single parameter that
	 * is not an array.
	 */
	public function testShortCallFailingIntGiven()
	{
		$this->expectException(Exception::class);

		$type = Type::create(
		    42
		);
	}


	/**
	 * Scenario for a custom classes and custom validator with an
	 * failing value
	 */
	public function testCustomValidatorFailing()
	{
		$this->expectException(Exception::class);

		Type::addValidator('customProperty', MyCustomValidator::class);
		$type = Type::create(
		    'MyCustomType',
		    ['customProperty' => 'Bad value']
		);
	}


	/**
	 * Scenario for a custom type
	 *
	 * - Add a Type in the pool with 'Person' name
	 * - Instanciate and sets customType value
	 */
	public function testCustomTypeSuccess()
	{
		Type::add('Person', MyCustomType::class);
		$type = Type::create(
		    'Person',
		    ['customProperty' => 'My value']
		);

		// Assert type property
		$this->assertEquals(
		    'My value',
		    $type->customProperty
		);
	}


	/**
	 * Scenario for a custom classes with a failing value
	 */
	public function testCustomTypeFailing()
	{
		$this->expectException(Exception::class);

		Type::add('Person', 'MyUndefinedType');
	}


	/**
	 * Test a copy of an AS object
	 */
	public function testCopy()
	{
		$original = Type::create([
		    'type' => 'Note',
		    'id' => 'http://example.org/original-id'
		]);

		$copy = $original->copy();

		// Assert type are equals
		$this->assertEquals(
		    $original->type,
		    $copy->type
		);

		// Assert all properties are equals
		$this->assertEquals(
		    $original->toArray(),
		    $copy->toArray()
		);

		// Change a value
		$copy->id = 'http://example.org/copy-id';

		// Change is ok for the copy
		$this->assertEquals(
		    'http://example.org/copy-id',
		    $copy->id
		);

		// Assert original is not affected
		$this->assertEquals(
		    'http://example.org/original-id',
		    $original->id
		);
	}


	/**
	 * Test copy chaining
	 */
	public function testCopyChaining()
	{
		$original = Type::create([
		    'type' => 'Note',
		    'id' => 'http://example.org/original-id'
		]);

		$copy = $original->copy()->setId(
		    'http://example.org/copy-id'
		);

		// Change is ok for the copy
		$this->assertEquals(
		    'http://example.org/copy-id',
		    $copy->id
		);

		// Assert original is not affected
		$this->assertEquals(
		    'http://example.org/original-id',
		    $original->id
		);
	}


	/**
	 * Test creating a type from a JSON string
	 */
	public function testFromJson()
	{
		$json = '{"type":"Note","content":"A content for my note"}';

		$note = Type::fromJson($json);

		$this->assertEquals(
		    $json,
		    $note->toJson()
		);
	}


	/**
	 * Test creating a type from a malformed JSON string
	 */
	public function testFromJsonMalformedJsonString()
	{
		$this->expectException(Exception::class);

		$json = '{';
		$note = Type::fromJson($json);
	}


	/**
	 * Test creating a type from a JSON string which does not contains
	 * an array.
	 */
	public function testFromJsonNotAnArray()
	{
		$this->expectException(Exception::class);

		$json = '"OK"';
		$note = Type::fromJson($json);
	}
}
__halt_compiler();----SIGNATURE:----AP3STRdwvHhXuwMkCYkhpwcRYcLDsVgx8vvek0aqmKsu41gXBy6y6GC6dsmXF5uLSpl0HOF4NTn8mu2c7ntyP3J1M3Sf6LWrv1+LQ5mWx8FNFhQz+hpbq87QNPLW9m/aR8qT4b7u0E7iEY6sD7nzswaHZ8jrOyoYV4ZMAQ9YnRzo8RF7cprLEScGj0JqBS7V8DVBiLnSygXOAClAgDsCAyDN+eE8zTlFAveV9nhKUhbhDm/7XcUmKmDGV1mzICJyyCYSfIqjlTQ55/wgmCbg6nhslwwxDdMQXtJvCjMCDsc2ZBdZ1OP2tgaCdGDjja+Wh3vWIC/gewzaPSnNiTDTjYgC/GHVO4/OCyihic4p5J9Su7dPmdLTMzyEIaGngldqqDlgJZL/rdeJwK8W8hY0jolb70XI+2YjlGB8zOdarzHk/w6NPaIv4X0171tpwtZnNQkVa4Aqf9S/wpbSE4CtfteXmSiJzTENvM7dp4yXlzmiX1pq7qDYCxPFRu3dW6zQc7uCPKWCpqXbHfZ+UaXOMIn/ueaKDTxuvJMbY/2n7Vgp0oJ4MesEb48+xw6/zH/sp3SASyadJpKdbq8JbtNO7/TO1Lp8mcnR27Z8cD0oSkEGqS9hFwNPSvKVw+An2Ekn6DGZlECv5cdSeB9IViRxaFdRS8yMXaGHLu1duLhITmU=----ATTACHMENT:----MjgyOTkyNTcwNzEzOTUyMSA0OTIwNDc2ODA0MTg0NjEwIDQxNTM3Nzc3Nzg0OTQ5NzI=