<?php

/**
 * Test: Expanding class alias to FQN.
 */

declare(strict_types=1);

use Nette\Utils\Reflection;
use Tester\Assert;

require __DIR__ . '/../bootstrap.php';


require __DIR__ . '/fixtures.reflection/expandClass.noNamespace.php';
require __DIR__ . '/fixtures.reflection/expandClass.inBracketedNamespace.php';
require __DIR__ . '/fixtures.reflection/expandClass.inNamespace.php';

if (PHP_VERSION_ID >= 80100) {
	require __DIR__ . '/fixtures.reflection/expandClass.special.php';
	Assert::same('A\B', Reflection::expandClassName('C', new ReflectionClass(T::class)));
	Assert::same('A\B', Reflection::expandClassName('C', new ReflectionClass(I::class)));
	Assert::same('A\B', Reflection::expandClassName('C', new ReflectionClass(E::class)));
}

$rcTest = new ReflectionClass(Test::class);
$rcBTest = new ReflectionClass(BTest::class);
$rcFoo = new ReflectionClass(Test\Space\Foo::class);
$rcBar = new ReflectionClass(Test\Space\Bar::class);


Assert::exception(
	fn() => Reflection::expandClassName('', $rcTest),
	Nette\InvalidArgumentException::class,
	'Class name must not be empty.',
);


Assert::exception(
	fn() => Reflection::expandClassName('A', new ReflectionClass(new class {
	})),
	Nette\NotImplementedException::class,
	'Anonymous classes are not supported.',
);


Assert::same('A', Reflection::expandClassName('A', $rcTest));
Assert::same('A\B', Reflection::expandClassName('C', $rcTest));

Assert::same('BTest', Reflection::expandClassName('BTest', $rcBTest));

Assert::same('Test\Space\Foo', Reflection::expandClassName('self', $rcFoo));
Assert::same('Test\Space\Foo', Reflection::expandClassName('Self', $rcFoo));

Assert::same('parent', Reflection::expandClassName('parent', $rcFoo));
Assert::same('Test\Space\Foo', Reflection::expandClassName('parent', new ReflectionClass(new class extends Test\Space\Foo {
})));

foreach (['String', 'string', 'int', 'float', 'bool', 'array', 'callable', 'iterable', 'void', 'null'] as $type) {
	Assert::same(strtolower($type), Reflection::expandClassName($type, $rcFoo));
}

/*
alias to expand => [
	FQN for $rcFoo,
	FQN for $rcBar
]
*/
$cases = [
	'\Absolute' => [
		'Absolute',
		'Absolute',
	],
	'\Absolute\Foo' => [
		'Absolute\Foo',
		'Absolute\Foo',
	],

	'AAA' => [
		'Test\Space\AAA',
		'AAA',
	],
	'AAA\Foo' => [
		'Test\Space\AAA\Foo',
		'AAA\Foo',
	],

	'B' => [
		'Test\Space\B',
		'BBB',
	],
	'B\Foo' => [
		'Test\Space\B\Foo',
		'BBB\Foo',
	],

	'DDD' => [
		'Test\Space\DDD',
		'CCC\DDD',
	],
	'DDD\Foo' => [
		'Test\Space\DDD\Foo',
		'CCC\DDD\Foo',
	],

	'F' => [
		'Test\Space\F',
		'EEE\FFF',
	],
	'F\Foo' => [
		'Test\Space\F\Foo',
		'EEE\FFF\Foo',
	],

	'HHH' => [
		'Test\Space\HHH',
		'Test\Space\HHH',
	],

	'Notdef' => [
		'Test\Space\Notdef',
		'Test\Space\Notdef',
	],
	'Notdef\Foo' => [
		'Test\Space\Notdef\Foo',
		'Test\Space\Notdef\Foo',
	],

	// trim leading backslash
	'G' => [
		'Test\Space\G',
		'GGG',
	],
	'G\Foo' => [
		'Test\Space\G\Foo',
		'GGG\Foo',
	],
];
foreach ($cases as $alias => $fqn) {
	Assert::same($fqn[0], Reflection::expandClassName($alias, $rcFoo));
	Assert::same($fqn[1], Reflection::expandClassName($alias, $rcBar));
}

Assert::same(
	['C' => 'A\B'],
	Reflection::getUseStatements(new ReflectionClass('Test')),
);

Assert::same(
	[],
	Reflection::getUseStatements(new ReflectionClass('Test\Space\Foo')),
);

Assert::same(
	['AAA' => 'AAA', 'B' => 'BBB', 'DDD' => 'CCC\DDD', 'F' => 'EEE\FFF', 'G' => 'GGG'],
	Reflection::getUseStatements(new ReflectionClass('Test\Space\Bar')),
);
Assert::same(
	[],
	Reflection::getUseStatements(new ReflectionClass('stdClass')),
);

Assert::exception(
	fn() => Reflection::getUseStatements(new ReflectionClass(new class {
	})),
	Nette\NotImplementedException::class,
	'Anonymous classes are not supported.',
);
