<?php

namespace App\Tests\Unit\Service\ApiLegacy;

use App\Entity\Access\Access;
use App\Entity\Person\Person;
use App\Service\ApiLegacy\ApiLegacyRequestService;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;

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

    const internalRequestsToken = 'azerty';

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

    public function getApiLegacyRequestServiceMockFor(string $methodName): ApiLegacyRequestService | MockObject {
        return $this
            ->getMockBuilder(ApiLegacyRequestService::class)
            ->setConstructorArgs([$this->apiLegacyClient, $this->security, $this->jwtManager, self::internalRequestsToken])
            ->setMethodsExcept([$methodName])
            ->getMock();
    }

    /**
     * @see ApiLegacyRequestService::request()
     */
    public function testRequest(): void
    {
        $api1RequestService = $this->getApiLegacyRequestServiceMockFor('request');

        $person = $this->getMockBuilder(Person::class)->disableOriginalConstructor()->getMock();

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

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

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

        $this->jwtManager->method('create')->with($person)->willReturn('XYZ');

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

        $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 testRequestSwitchUser(): void
    {
        $api1RequestService = $this->getApiLegacyRequestServiceMockFor('request');

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

        $person = $this->getMockBuilder(Person::class)->disableOriginalConstructor()->getMock();

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

        $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);

        $this->jwtManager->method('create')->with($person)->willReturn('123');

        $_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',
            'internal-requests-token' => self::internalRequestsToken

        ];

        $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->getApiLegacyRequestServiceMockFor('request');

        $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');
    }
}
