<?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\EventListener;

use Ferienpass\CoreBundle\Entity\Tenant;
use Ferienpass\CoreBundle\Entity\TenantDomain;
use Ferienpass\CoreBundle\EventListener\AdminRedirectListener;
use Ferienpass\CoreBundle\Repository\TenantDomainRepository;
use Ferienpass\CoreBundle\Tenant\HostResolver;
use Ferienpass\CoreBundle\Tenant\ResolvedHost;
use Ferienpass\CoreBundle\Tenant\Surface;
use Ferienpass\CoreBundle\Tenant\SurfaceContext;
use Ferienpass\CoreBundle\Tenant\TenantContext;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;

class AdminRedirectListenerTest extends TestCase
{
    private const string ADMIN_HOST = 'nordhorn.my-ferienpass.de';

    /**
     * The firewall sends a visitor back to the URI of the request it
     * interrupted — which is the internal one, with the segment this domain
     * does not use.
     */
    public function testTakesTheAdminSegmentOutOfALocationHeader(): void
    {
        $response = $this->respond(new RedirectResponse('/admin/angebote?page=2'));

        self::assertSame('/angebote?page=2', $response->headers->get('Location'));
    }

    public function testTakesItOutOfAnAbsoluteLocationOnTheSameHost(): void
    {
        $response = $this->respond(new RedirectResponse('https://'.self::ADMIN_HOST.'/nl/admin/angebote'));

        self::assertSame('https://'.self::ADMIN_HOST.'/nl/angebote', $response->headers->get('Location'));
    }

    /**
     * A link that deliberately leaves for the public site keeps its own path —
     * `/admin` means something else over there.
     */
    public function testLeavesAnotherHostAlone(): void
    {
        $response = $this->respond(new RedirectResponse('https://ferienpass-nordhorn.de/admin/angebote'));

        self::assertSame('https://ferienpass-nordhorn.de/admin/angebote', $response->headers->get('Location'));
    }

    public function testLeavesNonAdminLocationsAlone(): void
    {
        $response = $this->respond(new RedirectResponse('/angebote'));

        self::assertSame('/angebote', $response->headers->get('Location'));
    }

    public function testDoesNothingWhereTheAdminKeepsItsSegment(): void
    {
        $tenant = new Tenant();
        $tenant->addDomain(new TenantDomain($tenant, 'ferienpass-nordhorn.de', Surface::Frontend));

        $response = $this->respond(
            new RedirectResponse('/admin/angebote'),
            ResolvedHost::frontend('ferienpass-nordhorn.de', $tenant, true),
        );

        self::assertSame('/admin/angebote', $response->headers->get('Location'));
    }

    private function respond(Response $response, ?ResolvedHost $on = null): Response
    {
        $tenant = new Tenant();
        $tenant->addDomain(new TenantDomain($tenant, self::ADMIN_HOST, Surface::Admin));

        $tenantContext = $this->createMock(TenantContext::class);
        $tenantContext->method('getTenant')->willReturn($tenant);

        $surfaces = new SurfaceContext(new HostResolver($this->createMock(TenantDomainRepository::class), true), $tenantContext);
        $surfaces->setCurrent($on ?? ResolvedHost::admin(self::ADMIN_HOST, $tenant));

        $event = new ResponseEvent(
            $this->createStub(HttpKernelInterface::class),
            Request::create('https://'.self::ADMIN_HOST.'/angebote'),
            HttpKernelInterface::MAIN_REQUEST,
            $response,
        );

        new AdminRedirectListener($surfaces)($event);

        return $event->getResponse();
    }
}
