<?php

namespace AutoAdmin\Bundle\Tests\Unit\Seo\Redirect;

use AutoAdmin\Bundle\Content\Frontend\FrontendUrlChecker;
use AutoAdmin\Bundle\Seo\Entity\Redirect;
use AutoAdmin\Bundle\Seo\Redirect\RedirectService;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class RedirectServiceTest extends TestCase
{
    /**
     * @dataProvider dataProviderTestGetRedirectResponse
     */
    public function testGetRedirectResponse(
        Request $request,
        bool $rootRedirect,
        ?Redirect $redirect,
        ?Response $expectedResponse
    )
    {
        $frontEndUrlChecker = $this->getMockBuilder(FrontendUrlChecker::class)
            ->disableOriginalConstructor()
            ->getMock();
        $frontEndUrlChecker->method('isFrontUrl')->willReturn(false);

        $redirectService = $this->getMockBuilder(RedirectService::class)
            ->disableOriginalConstructor()
            ->getMock();
        $redirectService->method('getRedirect')->willReturn($redirect);

        $refl = new ReflectionClass(RedirectService::class);
        $refl->getProperty('frontendUrlChecker')->setValue($redirectService, $frontEndUrlChecker);
        $method = $refl->getMethod('getRedirectResponse');

        /** @var Response $response */
        $response = $method->invokeArgs($redirectService, [$request, $rootRedirect]);

        if ($response === null) {
            $this->assertEquals($expectedResponse, $response);
        } else {
            $this->assertEquals($expectedResponse->headers->get('Location'), $response->headers->get('Location'));
            $this->assertEquals($expectedResponse->getStatusCode(), $response->getStatusCode());
        }
    }

    public function dataProviderTestGetRedirectResponse(): array
    {
        return [
            'global_ok' => [
                'request' => Request::create('/myurl'),
                'rootRedirect' => true,
                'redirect' => (new Redirect())
                    ->setOrigin('/myurl')
                    ->setDestination('/redirected')
                    ->setType(Redirect::REDIRECT_302)
                    ->setRootRedirect(true)
                ,
                'response' => new Response('', Redirect::REDIRECT_302, ['Location' => '/redirected']),
            ],
            'global_ko' => [
                'request' => Request::create('/myurl'),
                'rootRedirect' => false,
                'redirect' => (new Redirect())
                    ->setOrigin('/myurl')
                    ->setDestination('/redirected')
                    ->setType(Redirect::REDIRECT_302)
                    ->setRootRedirect(true)
                ,
                'response' => new Response('', Redirect::REDIRECT_302, ['Location' => '/redirected']),
            ],
            'not_global_ok' => [
                'request' => Request::create('/myurl'),
                'rootRedirect' => false,
                'redirect' => (new Redirect())
                    ->setOrigin('/myurl')
                    ->setDestination('/redirected')
                    ->setType(Redirect::REDIRECT_302)
                    ->setRootRedirect(false)
                ,
                'response' => new Response('', Redirect::REDIRECT_302, ['Location' => '/redirected']),
            ],
            'not_global_ko' => [
                'request' => Request::create('/myurl'),
                'rootRedirect' => false,
                'redirect' => (new Redirect())
                    ->setOrigin('/myurl')
                    ->setDestination('/redirected')
                    ->setType(Redirect::REDIRECT_302)
                    ->setRootRedirect(true)
                ,
                'response' => new Response('', Redirect::REDIRECT_302, ['Location' => '/redirected']),
            ],
            'redir_code_check_301' => [
                'request' => Request::create('/myurl'),
                'rootRedirect' => true,
                'redirect' => (new Redirect())
                    ->setOrigin('/myurl')
                    ->setDestination('/redirected')
                    ->setType(Redirect::REDIRECT_301)
                    ->setRootRedirect(true)
                ,
                'response' => new Response('', Redirect::REDIRECT_301, ['Location' => '/redirected']),
            ],
            'redir_code_check_302' => [
                'request' => Request::create('/myurl'),
                'rootRedirect' => true,
                'redirect' => (new Redirect())
                    ->setOrigin('/myurl')
                    ->setDestination('/redirected')
                    ->setType(Redirect::REDIRECT_302)
                    ->setRootRedirect(true)
                ,
                'response' => new Response('', Redirect::REDIRECT_302, ['Location' => '/redirected']),
            ],
            'no_redir' => [
                'request' => Request::create('/myurl'),
                'rootRedirect' => true,
                'redirect' => null,
                'response' => null,
            ],
        ];
    }

    /**
     * @dataProvider dataProviderTestReplacePlaceholders
     */
    public function testReplacePlaceholders(string $uri, string $origin, string $destination, string $expected)
    {
        if ($uri === $expected) {
            $this->expectExceptionMessage('No match found');
        }

        $reflect = new ReflectionClass(RedirectService::class);
        $method = $reflect->getMethod('replacePlaceholders');

        $frontEndUrlChecker = $this->getMockBuilder(FrontendUrlChecker::class)
            ->disableOriginalConstructor()
            ->getMock();
        $frontEndUrlChecker->method('isFrontUrl')->willReturn(true);

        $redirectService = $this->getMockBuilder(RedirectService::class)
            ->disableOriginalConstructor()
            ->getMock();

        $redirect = $this->getMockBuilder(Redirect::class)
            ->disableOriginalConstructor()
            ->getMock();

        $redirect->expects($this->once())
            ->method('getDestination')
            ->willReturn($destination);

        $redirect->expects($this->once())
            ->method('getOrigin')
            ->willReturn($origin);

        $this->assertEquals($expected, $method->invokeArgs($redirectService, [$redirect, $uri]));
    }

    public function dataProviderTestReplacePlaceholders(): array
    {
        return [
            // Test redirect classic
            'normal' => [
                'uri' => '/old-page',
                'origin' => '/old-page',
                'destination' => '/new-page',
                'expected' => '/new-page',
            ],
            // Test redirect with '**' at the end
            'multiple-end' => [
                'uri' => '/old-page/some/path',
                'origin' => '/old-page/**',
                'destination' => '/new-page/**',
                'expected' => '/new-page/some/path',
            ],
            // Test redirect with '**' at a middle
            'multiple-middle' => [
                'uri' => '/old-page/some/path',
                'origin' => '/old-**/some/path',
                'destination' => '/new-**/some/path',
                'expected' => '/new-page/some/path',
            ],
            // Test redirect with wildcard in origin URL
            'single-end' => [
                'uri' => '/old-something/page',
                'origin' => '/old-something/*',
                'destination' => '/new-page',
                'expected' => '/new-page',
            ],
            // Test redirect with wildcard in origin URL
            'single-middle' => [
                'uri' => '/old-something/page',
                'origin' => '/old-*/page',
                'destination' => '/new-page',
                'expected' => '/new-page',
            ],
            // Test redirect with numerated wildcard in origin URL
            'single-numerated' => [
                'uri' => '/old-page/1',
                'origin' => '/old-page/*1*',
                'destination' => '/new-page/*1*',
                'expected' => '/new-page/1',
            ],
            // Test redirect with multiple wildcard in origin URL
            'multiple-wildcard' => [
                'uri' => '/old-page/1/2/3',
                'origin' => '/old-page/*1*/*2*/*3*',
                'destination' => '/new-page/*1*/*2*/*3*',
                'expected' => '/new-page/1/2/3',
            ],
            // Test redirect with multiple mixed wildcard in origin URL
            'multiple-mixed-wildcard' => [
                'uri' => '/old-page/1/2/3',
                'origin' => '/old-page/*1*/*2*/*3*',
                'destination' => '/new-page/*3*/*1*/*2*',
                'expected' => '/new-page/3/1/2',
            ],
            // Test remove prefix
            'remove-prefix' => [
                'uri' => '/old-prefix/1/2/3',
                'origin' => '/old-prefix/*/**',
                'destination' => '/*1*-*2*',
                'expected' => '/1-2/3',
            ],
            // Test something before
            'before' => [
                'uri' => '/1/something',
                'origin' => '/*1*/something',
                'destination' => '/*1*/something-different',
                'expected' => '/1/something-different',
            ],
            // Test something after
            'after' => [
                'uri' => '/something/1',
                'origin' => '/something/*1*',
                'destination' => '/something-different/*1*',
                'expected' => '/something-different/1',
            ],
            // Test something before and after
            'before-after' => [
                'uri' => '/1/something/2',
                'origin' => '/*1*/something/*2*',
                'destination' => '/*1*/something-different/*2*',
                'expected' => '/1/something-different/2',
            ],
            // Test something after and before
            'after-before' => [
                'uri' => '/1/something/2',
                'origin' => '/*1*/something/*2*',
                'destination' => '/*2*/something-different/*1*',
                'expected' => '/2/something-different/1',
            ],
            // Test redirect fail with extra segment at the end
            'match-fail-extra-segment' => [
                'uri' => '/1/something/2/3',
                'origin' => '/*1*/something/*2*',
                'destination' => '/*1*/something-different/*2*',
                'expected' => '/1/something/2/3',
            ],
            // Test trailing slash mismatch
            'trailing-slash-mismatch' => [
                'uri' => '/page/',
                'origin' => '/page',
                'destination' => '/new-page',
                'expected' => '/page/',
            ],
            // Test partial wildcard
            'partial-segment-wildcard' => [
                'uri' => '/product-123-details',
                'origin' => '/product-*1*-details',
                'destination' => '/item/*1*',
                'expected' => '/item/123',
            ],
            // Test special characters
            'special-chars' => [
                'uri' => '/caf%C3%A9',
                'origin' => '/café',
                'destination' => '/coffee',
                'expected' => '/coffee',
            ],
            // Test missing index in origin
            'missing-index-in-destination' => [
                'uri' => '/old/123',
                'origin' => '/old/*1*',
                'destination' => '/new/*2*',
                'expected' => '/old/123',
            ],
        ];
    }
}
