<?php

declare(strict_types=1);

namespace App\Tests\Unit\Doctrine\Access;

use App\Doctrine\Access\CurrentAccessExtension;
use App\Entity\Access\Access;
use App\Service\ServiceIterator\CurrentAccessExtensionIterator;
use App\Tests\Unit\Doctrine\ExtensionTestCase;
use PHPUnit\Framework\MockObject\MockObject;

class CurrentAccessExtensionTest extends ExtensionTestCase
{
    private CurrentAccessExtension $extension;
    private CurrentAccessExtensionIterator|MockObject $currentAccessExtensionIterator;

    protected function setUp(): void
    {
        parent::setUp();
        $this->currentAccessExtensionIterator = $this->createMock(CurrentAccessExtensionIterator::class);
        $this->extension = new CurrentAccessExtension($this->security, $this->currentAccessExtensionIterator);
    }

    public function testSupportsReturnsTrueForAccessClass(): void
    {
        $this->assertTrue(
            $this->extension->supports(Access::class, null)
        );
    }

    public function testSupportsReturnsFalseForOtherClasses(): void
    {
        $this->assertFalse(
            $this->extension->supports('OtherClass', null)
        );
    }

    public function testAddWhere(): void
    {
        $this->security->method('getToken')->willReturn($this->token);

        $this->token->method('getUser')->willReturn($this->user);

        $this->user->method('getOrganization')->willReturn($this->organization);

        $this->queryBuilder->method('getRootAliases')->willReturn(['a']);

        $this->queryBuilder->expects($this->once())
            ->method('andWhere')
            ->with('a.organization = :current_organization')
            ->willReturn($this->queryBuilder);

        $this->queryBuilder->expects($this->once())
            ->method('setParameter')
            ->with('current_organization', $this->organization)
            ->willReturn($this->queryBuilder);

        $this->currentAccessExtensionIterator->expects($this->once())
            ->method('addWhere')
            ->with($this->queryBuilder, '');

        $this->invokeAddWhere($this->extension, $this->queryBuilder, Access::class, null);
    }

    public function testAddWhereMissingToken(): void
    {
        $this->security->method('getToken')->willReturn(null);

        $this->token->expects(self::never())->method('getUser');
        $this->user->expects(self::never())->method('getOrganization');
        $this->queryBuilder->expects(self::never())->method('getRootAliases');
        $this->queryBuilder->expects(self::never())->method('andWhere');
        $this->queryBuilder->expects($this->never())->method('setParameter');
        $this->currentAccessExtensionIterator->expects($this->never())->method('addWhere');

        $this->invokeAddWhere($this->extension, $this->queryBuilder, Access::class, null);
    }

    public function testAddWhereMissingAccess(): void
    {
        $this->security->expects(self::once())->method('getToken')->willReturn($this->token);
        $this->token->expects(self::once())->method('getUser')->willReturn(null);

        $this->user->expects(self::never())->method('getOrganization');
        $this->queryBuilder->expects(self::never())->method('getRootAliases');
        $this->queryBuilder->expects(self::never())->method('andWhere');
        $this->queryBuilder->expects($this->never())->method('setParameter');
        $this->currentAccessExtensionIterator->expects($this->never())->method('addWhere');

        $this->invokeAddWhere($this->extension, $this->queryBuilder, Access::class, null);
    }

    public function testAddWhereNoRootAlias(): void
    {
        $this->security->expects(self::once())->method('getToken')->willReturn($this->token);
        $this->token->expects(self::once())->method('getUser')->willReturn($this->user);
        $this->user->expects(self::once())->method('getOrganization')->willReturn($this->organization);
        $this->queryBuilder->expects(self::once())->method('getRootAliases')->willReturn([]);

        $this->queryBuilder->expects(self::never())->method('andWhere');
        $this->queryBuilder->expects($this->never())->method('setParameter');
        $this->currentAccessExtensionIterator->expects($this->never())->method('addWhere');

        $this->expectException(\LogicException::class);

        $this->invokeAddWhere($this->extension, $this->queryBuilder, Access::class, null);
    }
}
