<?php

declare(strict_types=1);

namespace App\Tests\Doctrine\Billing;

use App\Doctrine\Billing\CurrentResidenceAreaExtension;
use App\Entity\Access\Access;
use App\Entity\Billing\BillingSetting;
use App\Entity\Billing\ResidenceArea;
use App\Entity\Organization\Organization;
use Doctrine\ORM\QueryBuilder;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\Security;

class CurrentResidenceAreaExtensionTest extends TestCase
{
    private Security|MockObject $security;
    private QueryBuilder|MockObject $queryBuilder;
    private CurrentResidenceAreaExtension $extension;
    private Access|MockObject $user;
    private Organization|MockObject $organization;
    private BillingSetting|MockObject $billingSetting;

    protected function setUp(): void
    {
        $this->security = $this->createMock(Security::class);
        $this->queryBuilder = $this->createMock(QueryBuilder::class);
        $this->user = $this->createMock(Access::class);
        $this->organization = $this->createMock(Organization::class);
        $this->billingSetting = $this->createMock(BillingSetting::class);

        $this->extension = new CurrentResidenceAreaExtension($this->security);
    }

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

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

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

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

        $this->organization->method('getBillingSetting')->willReturn($this->billingSetting);

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

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

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

        $reflection = new \ReflectionClass($this->extension);
        $method = $reflection->getMethod('addWhere');
        $method->setAccessible(true);
        $method->invoke($this->extension, $this->queryBuilder, ResidenceArea::class, null);
    }

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

        $this->user->expects(self::never())->method('getOrganization');

        $this->organization->expects(self::never())->method('getBillingSetting');

        $this->queryBuilder->expects(self::never())->method('getRootAliases');

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

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

        $reflection = new \ReflectionClass($this->extension);
        $method = $reflection->getMethod('addWhere');
        $method->setAccessible(true);
        $method->invoke($this->extension, $this->queryBuilder, ResidenceArea::class, null);
    }

    public function testAddWhereMissingOrganization(): void
    {
        $this->security->method('getUser')->willReturn($this->user);

        $this->user->expects(self::once())->method('getOrganization')->willReturn(null);

        $this->organization->expects(self::never())->method('getBillingSetting');

        $this->queryBuilder->expects(self::never())->method('getRootAliases');

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

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

        $reflection = new \ReflectionClass($this->extension);
        $method = $reflection->getMethod('addWhere');
        $method->setAccessible(true);
        $method->invoke($this->extension, $this->queryBuilder, ResidenceArea::class, null);
    }
}
