<?php

namespace App\Tests\Unit\Service\Rest;

use App\Service\Rest\ApiRequestService;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpClient\Exception\TransportException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;

class ApiRequestServiceTest extends TestCase
{
    private HttpClientInterface $client;

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

    /**
     * @see ApiRequestService::getJsonContent()
     */
    public function testGetJsonContent(): void
    {
        $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getJsonContent'])
            ->getMock();

        $apiRequestService->expects(self::once())
            ->method('getContent')
            ->with('path/to/data', [], [])
            ->willReturn('{"foo": "bar"}');

        $data = $apiRequestService->getJsonContent('path/to/data');

        $this->assertEquals(['foo' => 'bar'], $data);
    }

    /**
     * @see ApiRequestService::getJsonContent()
     */
    public function testGetContent(): void
    {
        $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getContent'])
            ->getMock();

        $response = $this->getMockBuilder(ResponseInterface::class)->disableOriginalConstructor()->getMock();
        $response->expects(self::once())->method('getContent')->willReturn('{foo: bar}');

        $apiRequestService->expects(self::once())
            ->method('get')
            ->with('path/to/data', [], [])
            ->willReturn($response);

        $content = $apiRequestService->getContent('path/to/data');

        $this->assertEquals('{foo: bar}', $content);
    }

    /**
     * @see ApiRequestService::getContent()
     */
    public function testGetContentWithError(): void
    {
        $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['getContent'])
            ->getMock();

        $apiRequestService->expects(self::once())
            ->method('get')
            ->willThrowException(new TransportException());

        $this->expectException(HttpException::class);
        $apiRequestService->getContent('path/to/data');
    }

    /**
     * @see ApiRequestService::get()
     */
    public function testGet(): void
    {
        $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['get'])
            ->getMock();

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

        $apiRequestService->expects(self::once())
            ->method('request')
            ->with('GET', 'path/to/data', [], [])
            ->willReturn($response);

        $actualResponse = $apiRequestService->get('path/to/data');

        $this->assertEquals($response, $actualResponse);
    }

    /**
     * @see ApiRequestService::post()
     */
    public function testPost(): void
    {
        $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['post'])
            ->getMock();

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

        $apiRequestService->expects(self::once())
            ->method('request')
            ->with('POST', 'path/to/data', [], [])
            ->willReturn($response);

        $actualResponse = $apiRequestService->post('path/to/data');

        $this->assertEquals($response, $actualResponse);
    }

    /**
     * @see ApiRequestService::put()
     */
    public function testPut(): void
    {
        $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['put'])
            ->getMock();

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

        $apiRequestService->expects(self::once())
            ->method('request')
            ->with('PUT', 'path/to/data', [], [])
            ->willReturn($response);

        $actualResponse = $apiRequestService->put('path/to/data');

        $this->assertEquals($response, $actualResponse);
    }

    /**
     * @see ApiRequestService::delete()
     */
    public function testDelete(): void
    {
        $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['delete'])
            ->getMock();

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

        $apiRequestService->expects(self::once())
            ->method('request')
            ->with('DELETE', 'path/to/data', [], [])
            ->willReturn($response);

        $actualResponse = $apiRequestService->delete('path/to/data');

        $this->assertEquals($response, $actualResponse);
    }

    /**
     * @see ApiRequestService::request()
     */
    public function testRequest(): void
    {
        $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['request'])
            ->getMock();

        $response = $this->getMockBuilder(ResponseInterface::class)->disableOriginalConstructor()->getMock();
        $this->client->expects(self::once())->method('request')->with('GET', 'path/to/data?param=1', [])->willReturn($response);

        $actualResponse = $apiRequestService->request('GET', 'path/to/data', ['param' => 1]);

        $this->assertEquals($response, $actualResponse);
    }

    /**
     * @see ApiRequestService::request()
     */
    public function testRequestWithError(): void
    {
        $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
            ->setConstructorArgs([$this->client])
            ->setMethodsExcept(['request'])
            ->getMock();

        $this->client->expects(self::once())->method('request')->willThrowException(new TransportException('error', 500));

        $this->expectException(HttpException::class);
        $apiRequestService->request('GET', 'path/to/data');
    }
}
