<?php

namespace App\Tests\Service\ApiLegacy;

use App\Entity\Access\Access;
use App\Service\ApiLegacy\ApiLegacyRequestService;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\Security\Core\Authentication\Token\NullToken;
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;

class ApiLegacyRequestServiceTest extends TestCase
{
    private HttpClientInterface $apiLegacyClient;
    private Security $security;

    public function setUp(): void
    {
        $this->apiLegacyClient = $this->getMockBuilder(HttpClientInterface::class)->disableOriginalConstructor()->getMock();
        $this->security = $this->getMockBuilder(Security::class)->disableOriginalConstructor()->getMock();
    }

    /**
     * @see ApiLegacyRequestService::request()
     */
    public function testRequest(): void
    {
        $api1RequestService = $this
            ->getMockBuilder(ApiLegacyRequestService::class)
            ->setConstructorArgs([$this->apiLegacyClient, $this->security])
            ->setMethodsExcept(['request'])
            ->getMock();

        $user = $this->getMockBuilder(Access::class)->disableOriginalConstructor()->getMock();
        $user->method('getId')->willReturn(1);

        $token = $this->getMockBuilder(UsernamePasswordToken::class)->disableOriginalConstructor()->getMock();
        $token->method('getUser')->willReturn($user);

        $this->security->method('getToken')->willReturn($token);

        $_REQUEST['BEARER'] = '123';
        $expectedHeaders = [
            'custom' => 'foo',
            'authorization' => 'BEARER 123',
            'Accept' => '*/*',
            'Charset' => 'UTF-8',
            'Accept-Encoding' => 'gzip, deflate, br',
            'Content-Type' => 'application/ld+json',
            'x-accessid' => '1'
        ];

        $response = $this->getMockBuilder(ResponseInterface::class)->disableOriginalConstructor()->getMock();

        $this->apiLegacyClient
            ->expects(self::once())
            ->method('request')
            ->with('GET', 'an/url', ['headers' => $expectedHeaders])
            ->willReturn($response);

        $api1RequestService->request(
            'GET',
            '/an/url',
            [],
            ['headers' => ['custom' => 'foo']]
        );
    }

    /**
     * @see ApiLegacyRequestService::request()
     */
    public function testRequestNoToken(): void
    {
        $api1RequestService = $this
            ->getMockBuilder(ApiLegacyRequestService::class)
            ->setConstructorArgs([$this->apiLegacyClient, $this->security])
            ->setMethodsExcept(['request'])
            ->getMock();

        $this->security->method('getToken')->willReturn(null);

        $this->expectException(HttpException::class);
        $this->expectExceptionMessage('Request error : Invalid security token');

        $api1RequestService->request('GET', '/an/url');
    }

    /**
     * @see ApiLegacyRequestService::request()
     */
    public function testRequestNullToken(): void
    {
        $api1RequestService = $this
            ->getMockBuilder(ApiLegacyRequestService::class)
            ->setConstructorArgs([$this->apiLegacyClient, $this->security])
            ->setMethodsExcept(['request'])
            ->getMock();

        $token = $this->getMockBuilder(NullToken::class)->disableOriginalConstructor()->getMock();

        $this->security->method('getToken')->willReturn($token);

        $this->expectException(HttpException::class);
        $this->expectExceptionMessage('Request error : Invalid security token');

        $api1RequestService->request('GET', '/an/url');
    }

    /**
     * @see ApiLegacyRequestService::request()
     */
    public function testRequestInvalidToken(): void
    {
        $api1RequestService = $this
            ->getMockBuilder(ApiLegacyRequestService::class)
            ->setConstructorArgs([$this->apiLegacyClient, $this->security])
            ->setMethodsExcept(['request'])
            ->getMock();

        $token = $this->getMockBuilder(UsernamePasswordToken::class)->disableOriginalConstructor()->getMock();
        $token->method('getUser')->willReturn(null);

        $this->security->method('getToken')->willReturn($token);

        $this->expectException(HttpException::class);
        $this->expectExceptionMessage('Request error : Invalid security token');

        $api1RequestService->request('GET', '/an/url');
    }

    /**
     * @see ApiLegacyRequestService::request()
     */
    public function testRequestSwitchUser(): void
    {
        $api1RequestService = $this
            ->getMockBuilder(ApiLegacyRequestService::class)
            ->setConstructorArgs([$this->apiLegacyClient, $this->security])
            ->setMethodsExcept(['request'])
            ->getMock();

        $user = $this->getMockBuilder(Access::class)->disableOriginalConstructor()->getMock();
        $user->method('getId')->willReturn(10);

        $originalUser = $this->getMockBuilder(Access::class)->disableOriginalConstructor()->getMock();
        $originalUser->method('getId')->willReturn(20);

        $originalToken = $this->getMockBuilder(SwitchUserToken::class)->disableOriginalConstructor()->getMock();
        $originalToken->method('getUser')->willReturn($originalUser);

        $token = $this->getMockBuilder(SwitchUserToken::class)->disableOriginalConstructor()->getMock();
        $token->method('getUser')->willReturn($user);
        $token->method('getOriginalToken')->willReturn($originalToken);

        $this->security->method('getToken')->willReturn($token);

        $_REQUEST['BEARER'] = '123';

        $expectedHeaders = [
            'authorization' => 'BEARER 123',
            'Accept' => '*/*',
            'Charset' => 'UTF-8',
            'Accept-Encoding' => 'gzip, deflate, br',
            'Content-Type' => 'application/ld+json',
            'x-accessid' => '20',
            'x-switch-access' => '10',
        ];

        $response = $this->getMockBuilder(ResponseInterface::class)->disableOriginalConstructor()->getMock();

        $this->apiLegacyClient
            ->expects(self::once())
            ->method('request')
            ->with('GET', 'an/url', ['headers' => $expectedHeaders])
            ->willReturn($response);

        $api1RequestService->request('GET', '/an/url');
    }

    /**
     * @see ApiLegacyRequestService::request()
     */
    public function testRequestSwitchInvalidUser(): void
    {
        $api1RequestService = $this
            ->getMockBuilder(ApiLegacyRequestService::class)
            ->setConstructorArgs([$this->apiLegacyClient, $this->security])
            ->setMethodsExcept(['request'])
            ->getMock();

        $user = $this->getMockBuilder(Access::class)->disableOriginalConstructor()->getMock();
        $user->method('getId')->willReturn(10);

        $originalToken = $this->getMockBuilder(SwitchUserToken::class)->disableOriginalConstructor()->getMock();
        $originalToken->method('getUser')->willReturn(null);

        $token = $this->getMockBuilder(SwitchUserToken::class)->disableOriginalConstructor()->getMock();
        $token->method('getUser')->willReturn($user);
        $token->method('getOriginalToken')->willReturn($originalToken);

        $this->security->method('getToken')->willReturn($token);

        $this->expectException(HttpException::class);
        $this->expectExceptionMessage('Request error : Switch original user missing');

        $api1RequestService->request('GET', '/an/url');
    }
}
