<?php

declare(strict_types=1);

namespace App\Tests\Unit\Doctrine;

use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Operation;
use Doctrine\ORM\QueryBuilder;
use PHPUnit\Framework\MockObject\MockObject;

class TestableAbstractExtension extends \App\Doctrine\AbstractExtension
{
    public function apply(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
    {
        parent::apply($queryBuilder, $resourceClass, $operation);
    }

    public function supports(string $resourceClass, ?Operation $operation): bool
    {
        throw new \Exception('This method should not be called');
    }

    public function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
    {
        throw new \Exception('This method should not be called');
    }
}

class AbstractExtensionTest extends ExtensionTestCase
{
    protected function getMockObjectFor(string $methodName): MockObject|TestableAbstractExtension
    {
        return $this
            ->getMockBuilder(TestableAbstractExtension::class)
            ->setMethodsExcept([$methodName])
            ->getMock();
    }

    public function testApplyToCollection(): void
    {
        $extension = $this->getMockObjectFor('applyToCollection');

        $queryNameGeneratorInterface = $this->getMockBuilder(QueryNameGeneratorInterface::class)->getMock();
        $operation = $this->getMockBuilder(Operation::class)->getMock();

        $extension
            ->expects($this->once())
            ->method('apply')
            ->with($this->queryBuilder, 'App\Entity\Access\Access', $operation);

        $extension->applyToCollection(
            $this->queryBuilder,
            $queryNameGeneratorInterface,
            'App\Entity\Access\Access',
            $operation
        );
    }

    public function testApplyToItem(): void
    {
        $extension = $this->getMockObjectFor('applyToItem');

        $queryNameGeneratorInterface = $this->getMockBuilder(QueryNameGeneratorInterface::class)->getMock();
        $operation = $this->getMockBuilder(Operation::class)->getMock();

        $extension
            ->expects($this->once())
            ->method('apply')
            ->with($this->queryBuilder, 'App\Entity\Access\Access', $operation);

        $extension->applyToItem(
            $this->queryBuilder,
            $queryNameGeneratorInterface,
            'App\Entity\Access\Access',
            [],
            $operation
        );
    }

    public function testApply(): void
    {
        $extension = $this->getMockObjectFor('apply');

        $operation = $this->getMockBuilder(Operation::class)->getMock();

        $extension
            ->expects($this->once())
            ->method('supports')
            ->with('App\Entity\Access\Access', $operation)
            ->willReturn(true);

        $extension
            ->expects(self::once())
            ->method('addWhere')
            ->with($this->queryBuilder, 'App\Entity\Access\Access', $operation);

        $extension->apply($this->queryBuilder, 'App\Entity\Access\Access', $operation);
    }

    public function testApplyNoSupport(): void
    {
        $extension = $this->getMockObjectFor('apply');

        $operation = $this->getMockBuilder(Operation::class)->getMock();

        $extension
            ->expects($this->once())
            ->method('supports')
            ->with('App\Entity\Access\Access', $operation)
            ->willReturn(false);

        $extension
            ->expects(self::never())
            ->method('addWhere');

        $extension->apply($this->queryBuilder, 'App\Entity\Access\Access', $operation);
    }
}
