<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CoreBundle\Tests\Messenger;

use Ferienpass\CoreBundle\Messenger\LocaleMiddleware;
use Ferienpass\CoreBundle\Messenger\LocaleStamp;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;
use Symfony\Component\Messenger\Stamp\ConsumedByWorkerStamp;
use Symfony\Contracts\Translation\LocaleAwareInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

final class LocaleMiddlewareTest extends TestCase
{
    public function testPassesThroughWhenTranslatorNotLocaleAware(): void
    {
        $translator = $this->createMock(TranslatorInterface::class);
        // TranslatorInterface without LocaleAwareInterface

        $next = $this->createMock(MiddlewareInterface::class);
        $envelope = new Envelope(new \stdClass());
        $next->expects(self::once())->method('handle')->willReturn($envelope);

        $stack = $this->createMock(StackInterface::class);
        $stack->method('next')->willReturn($next);

        $middleware = new LocaleMiddleware(
            $translator,
            $this->createMock(Security::class),
            new RequestStack()
        );

        $result = $middleware->handle($envelope, $stack);
        self::assertSame($envelope, $result);
    }

    public function testAddsLocaleStampWhenNoStampsPresent(): void
    {
        $translator = $this->createMock(LocaleAwareTranslatorInterface::class);
        $translator->method('getLocale')->willReturn('de');

        $next = $this->createMock(MiddlewareInterface::class);
        $next->method('handle')->willReturnCallback(static fn (Envelope $env, $stack) => $env);

        $stack = $this->createMock(StackInterface::class);
        $stack->method('next')->willReturn($next);

        $middleware = new LocaleMiddleware(
            $translator,
            $this->createMock(Security::class),
            new RequestStack()
        );

        $envelope = new Envelope(new \stdClass());
        $result = $middleware->handle($envelope, $stack);

        $stamp = $result->last(LocaleStamp::class);
        self::assertInstanceOf(LocaleStamp::class, $stamp);
        self::assertSame('de', $stamp->getLocale());
    }

    public function testSetsAndRestoresLocaleWhenConsumedByWorker(): void
    {
        $translator = $this->createMock(LocaleAwareTranslatorInterface::class);
        $translator->method('getLocale')->willReturn('en');
        $translator->expects(self::exactly(2))->method('setLocale')
            ->willReturnCallback(static function (string $locale): void {
                // First call sets 'fr', second call in finally restores 'en'
            });

        $next = $this->createMock(MiddlewareInterface::class);
        $next->method('handle')->willReturnCallback(static fn (Envelope $env, $stack) => $env);

        $stack = $this->createMock(StackInterface::class);
        $stack->method('next')->willReturn($next);

        $middleware = new LocaleMiddleware(
            $translator,
            $this->createMock(Security::class),
            new RequestStack()
        );

        $envelope = new Envelope(new \stdClass(), [
            new ConsumedByWorkerStamp(),
            new LocaleStamp('fr'),
        ]);

        $middleware->handle($envelope, $stack);
    }
}

interface LocaleAwareTranslatorInterface extends TranslatorInterface, LocaleAwareInterface
{
}
