<?php

declare(strict_types=1);

namespace App\Tests\Module\Sites\Application\Service;

use App\Module\Core\Domain\Entity\User;
use App\Module\Sites\Application\Service\CurrentSiteProvider;
use App\Module\Sites\Domain\Entity\Site;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Security\Core\User\InMemoryUser;

/**
 * Tests unitaires du CurrentSiteProvider.
 *
 * Le provider lit `config/modules.php` au boot via un `require`. Pour les
 * tests, on force la valeur du flag `sitesActive` via reflection plutot
 * que de mock le filesystem : le comportement du constructeur
 * (file_exists + require) est assez trivial pour etre couvert par un
 * test d'integration si besoin ; ici on se concentre sur la logique de
 * `get()`.
 *
 * @internal
 */
final class CurrentSiteProviderTest extends TestCase
{
    public function testReturnsNullIfSitesModuleInactive(): void
    {
        $user = new User();
        $user->setCurrentSite(new Site('Site', 'Rue', null, '12345', 'Ville', '#000000'));

        $security = $this->createStub(Security::class);
        $security->method('getUser')->willReturn($user);

        $provider = $this->makeProvider($security, sitesActive: false);

        self::assertNull($provider->get());
    }

    public function testReturnsNullIfNoUser(): void
    {
        $security = $this->createStub(Security::class);
        $security->method('getUser')->willReturn(null);

        $provider = $this->makeProvider($security, sitesActive: true);

        self::assertNull($provider->get());
    }

    public function testReturnsNullIfUserIsNotAppUser(): void
    {
        // Un InMemoryUser Symfony n'est pas une instance de App\User donc
        // le provider ne peut pas lire son currentSite -> null defensif.
        $security = $this->createStub(Security::class);
        $security->method('getUser')->willReturn(new InMemoryUser('foo', 'bar'));

        $provider = $this->makeProvider($security, sitesActive: true);

        self::assertNull($provider->get());
    }

    public function testReturnsNullIfUserHasNoCurrentSite(): void
    {
        $user = new User();
        // Pas d'appel a setCurrentSite, donc null par defaut.

        $security = $this->createStub(Security::class);
        $security->method('getUser')->willReturn($user);

        $provider = $this->makeProvider($security, sitesActive: true);

        self::assertNull($provider->get());
    }

    public function testReturnsSiteWhenAllConditionsMet(): void
    {
        $site = new Site('Chatellerault', 'Rue', null, '86100', 'Chatellerault', '#056CF2');
        $user = new User();
        $user->setCurrentSite($site);

        $security = $this->createStub(Security::class);
        $security->method('getUser')->willReturn($user);

        $provider = $this->makeProvider($security, sitesActive: true);

        self::assertSame($site, $provider->get());
    }

    /**
     * Factory helper : construit un provider avec `$sitesActive` force a
     * la valeur donnee, bypassant la lecture reelle de config/modules.php.
     */
    private function makeProvider(Security $security, bool $sitesActive): CurrentSiteProvider
    {
        // Instance via reflection pour eviter l'appel reel au constructeur
        // qui require config/modules.php (non deterministe en test unit).
        $reflection = new ReflectionClass(CurrentSiteProvider::class);
        $provider   = $reflection->newInstanceWithoutConstructor();

        $securityProp = $reflection->getProperty('security');
        $securityProp->setValue($provider, $security);

        $sitesActiveProp = $reflection->getProperty('sitesActive');
        $sitesActiveProp->setValue($provider, $sitesActive);

        return $provider;
    }
}
