<?php

/**
 * @see       https://github.com/laminas/laminas-code for the canonical source repository
 * @copyright https://github.com/laminas/laminas-code/blob/master/COPYRIGHT.md
 * @license   https://github.com/laminas/laminas-code/blob/master/LICENSE.md New BSD License
 */

namespace LaminasTest\Code\Scanner;

use Laminas\Code\Scanner\FileScanner;
use Laminas\Code\Scanner\MethodScanner;
use Laminas\Code\Scanner\ParameterScanner;
use LaminasTest\Code\TestAsset\AbstractClass;
use LaminasTest\Code\TestAsset\BarClass;
use LaminasTest\Code\TestAsset\FooClass;
use PHPUnit\Framework\TestCase;

class MethodScannerTest extends TestCase
{
    public function testMethodScannerHasMethodInformation()
    {
        $file   = new FileScanner(__DIR__ . '/../TestAsset/FooClass.php');
        $class  = $file->getClass(FooClass::class);
        $method = $class->getMethod('fooBarBaz');
        self::assertEquals('fooBarBaz', $method->getName());
        self::assertFalse($method->isAbstract());
        self::assertTrue($method->isFinal());
        self::assertTrue($method->isPublic());
        self::assertFalse($method->isProtected());
        self::assertFalse($method->isPrivate());
        self::assertFalse($method->isStatic());
    }

    public function testMethodScannerReturnsParameters()
    {
        $file       = new FileScanner(__DIR__ . '/../TestAsset/BarClass.php');
        $class      = $file->getClass(BarClass::class);
        $method     = $class->getMethod('three');
        $parameters = $method->getParameters();
        self::assertIsArray($parameters);
    }

    public function testMethodScannerReturnsParameterScanner()
    {
        $file   = new FileScanner(__DIR__ . '/../TestAsset/BarClass.php');
        $class  = $file->getClass(BarClass::class);
        $method = $class->getMethod('three');
        self::assertEquals(['o', 't', 'bbf'], $method->getParameters());
        $parameter = $method->getParameter('t');
        self::assertInstanceOf(ParameterScanner::class, $parameter);
        self::assertEquals('t', $parameter->getName());
    }

    public function testMethodScannerParsesClassNames()
    {
        $file   = new FileScanner(__DIR__ . '/../TestAsset/BarClass.php');
        $class  = $file->getClass(BarClass::class);
        $method = $class->getMethod('five');
        self::assertEquals(['a'], $method->getParameters());
        $parameter = $method->getParameter('a');
        self::assertEquals(AbstractClass::class, $parameter->getClass());
    }

    public function testMethodScannerReturnsPropertyWithNoDefault()
    {
        $file  = new FileScanner(__DIR__ . '/../TestAsset/BazClass.php');
        $class = $file->getClass('BazClass');
        $method = $class->getMethod('__construct');
        self::assertTrue($method->isPublic());
    }

    public function testMethodScannerReturnsLineNumbersForMethods()
    {
        $file       = new FileScanner(__DIR__ . '/../TestAsset/BarClass.php');
        $class      = $file->getClass(BarClass::class);
        $method     = $class->getMethod('three');
        self::assertEquals(27, $method->getLineStart());
        self::assertEquals(31, $method->getLineEnd());
    }

    public function testMethodScannerReturnsBodyMethods()
    {
        $file     = new FileScanner(__DIR__ . '/../TestAsset/BarClass.php');
        $class    = $file->getClass(BarClass::class);
        $method   = $class->getMethod('three');
        $expected = "\n" . '        $x = 5 + 5;' . "\n" . '        $y = \'this string\';' . "\n    ";
        self::assertEquals($expected, $method->getBody());
    }

    public function testMethodScannerMethodSignatureLatestOptionalParamHasParentheses()
    {
        $file       = new FileScanner(__DIR__ . '/../TestAsset/BarClass.php');
        $class      = $file->getClass(BarClass::class);
        $method = $class->getMethod('four');
        $paramTwo = $method->getParameter(1);
        $optionalValue = $paramTwo->getDefaultValue();
        self::assertEquals('array([array(\'default\')])', $optionalValue);
    }

    /**
     * @group issue-6893
     */
    public function testMethodScannerWorksWithSingleAbstractFunction()
    {
        $file = new FileScanner(__DIR__ . '/../TestAsset/AbstractClass.php');

        $class = $file->getClass(AbstractClass::class);
        $method = $class->getMethod('helloWorld');

        self::assertTrue($method->isAbstract());
    }

    public function testMethodScannerSetVisibilityThrowsInvalidArgumentException()
    {
        $methodScanner = new MethodScanner([]);

        // make sure test argument is invalid
        $invalidArgument = max(T_PUBLIC, T_PROTECTED, T_PRIVATE) + 1;

        $this->expectException('\Laminas\Code\Exception\InvalidArgumentException');
        $methodScanner->setVisibility($invalidArgument);
    }

    public function testMethodScannerSetVisibilityAcceptsIntegerTokens()
    {
        $methodScanner = new MethodScanner([]);

        $this->assertSame($methodScanner->setVisibility(T_PUBLIC), $methodScanner);
        $this->assertSame($methodScanner->setVisibility(T_PROTECTED), $methodScanner);
        $this->assertSame($methodScanner->setVisibility(T_PRIVATE), $methodScanner);
    }
}
