<?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\Entity\Tenant;
use Ferienpass\CoreBundle\Messenger\TenantMiddleware;
use Ferienpass\CoreBundle\Messenger\TenantStamp;
use Ferienpass\CoreBundle\Repository\TenantRepository;
use Ferienpass\CoreBundle\Tenant\TenantContext;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Middleware\StackMiddleware;
use Symfony\Component\Messenger\Stamp\ConsumedByWorkerStamp;

class TenantMiddlewareTest extends TestCase
{
    public function testStampsTheDispatchingTenant(): void
    {
        $context = $this->createMock(TenantContext::class);
        $context->method('isEnabled')->willReturn(true);
        $context->method('getSlug')->willReturn('nordhorn');

        $envelope = $this->handle($context, new Envelope(new \stdClass()), null);

        $this->assertSame('nordhorn', $envelope->last(TenantStamp::class)?->getSlug());
    }

    public function testDoesNotStampWhenNoTenantIsActive(): void
    {
        $context = $this->createMock(TenantContext::class);
        $context->method('isEnabled')->willReturn(true);
        $context->method('getSlug')->willReturn(null);

        $envelope = $this->handle($context, new Envelope(new \stdClass()), null);

        $this->assertNull($envelope->last(TenantStamp::class));
    }

    public function testASingleTenantBuildNeverStamps(): void
    {
        $context = $this->createMock(TenantContext::class);
        $context->method('isEnabled')->willReturn(false);
        $context->expects($this->never())->method('getSlug');

        $envelope = $this->handle($context, new Envelope(new \stdClass()), null);

        $this->assertNull($envelope->last(TenantStamp::class));
    }

    public function testTheWorkerHandlesTheMessageInsideTheStampedTenant(): void
    {
        $tenant = new Tenant();
        $tenant->setSlug('nordhorn');

        $context = $this->createMock(TenantContext::class);
        $context->method('isEnabled')->willReturn(true);
        $context->expects($this->once())
            ->method('runFor')
            ->with($tenant, $this->isCallable())
            ->willReturnCallback(static fn (?Tenant $t, callable $callback) => $callback())
        ;

        $envelope = new Envelope(new \stdClass(), [new ConsumedByWorkerStamp(), new TenantStamp('nordhorn')]);

        $this->handle($context, $envelope, $tenant);
    }

    private function handle(TenantContext $context, Envelope $envelope, ?Tenant $resolved): Envelope
    {
        $tenants = $this->createMock(TenantRepository::class);
        $tenants->method('findOneBySlug')->willReturn($resolved);

        return new TenantMiddleware($context, $tenants)->handle($envelope, new StackMiddleware());
    }
}
