<?php

namespace Alma\Vendor\Alma\Client\Tests\Unit\Application\Endpoint;

use Alma\Vendor\Alma\Client\Application\Endpoint\ShareOfCheckoutEndpoint;
use Alma\Vendor\Alma\Client\Application\Exception\ClientException;
use Alma\Vendor\Alma\Client\Application\Exception\Endpoint\ShareOfCheckoutEndpointException;
use Alma\Vendor\Alma\Client\Application\Response;
use Mockery;
use Mockery\Mock;

class ShareOfCheckoutEndpointTest extends AbstractEndpointSetUp
{
    const SHARE_OF_CHECKOUT_RESPONSE_JSON = '{
            "merchant_id": "merchant_11xYpTY1GTkww5uWFKFdOllK82S1r7j5v5",
            "start_time": 1739343778,
            "end_time": 1739343778
        }';

    /** @var ShareOfCheckoutEndpoint|Mock */
    protected $shareOfCheckoutServiceMock;

    /** @var Response|Mock */
    protected $responseMock;

    /** @var Response|Mock */
    protected $badResponseMock;

    public function setUp(): void
    {
        parent::setUp();

        // Mocks
        $this->responseMock = Mockery::mock(Response::class);
        $this->responseMock->shouldReceive('getStatusCode')->andReturn(200);
        $this->responseMock->shouldReceive('isError')->andReturn(false);
        $this->responseMock->shouldReceive('getBody')->andReturn(self::SHARE_OF_CHECKOUT_RESPONSE_JSON);
        $this->responseMock->shouldReceive('getJson')->andReturn(json_decode(self::SHARE_OF_CHECKOUT_RESPONSE_JSON, true));

        $this->badResponseMock = Mockery::mock(Response::class);
        $this->badResponseMock->shouldReceive('getStatusCode')->andReturn(500);
        $this->badResponseMock->shouldReceive('getReasonPhrase')->andReturn('Internal Server Error');
        $this->badResponseMock->shouldReceive('isError')->andReturn(true);
        $this->badResponseMock->shouldReceive('getBody')->andReturn(self::SHARE_OF_CHECKOUT_RESPONSE_JSON);

        // ShareOfCheckoutService
        $this->shareOfCheckoutServiceMock = Mockery::mock(ShareOfCheckoutEndpoint::class, [$this->clientMock])
            ->makePartial();
    }

    /**
     * Ensure we can send the share of checkout
     * @throws ShareOfCheckoutEndpointException
     */
    public function testShare(): void
    {
        // Mocks
        $this->clientMock->shouldReceive('sendRequest')->once()->andReturn($this->responseMock);

        // Call
        $result = $this->shareOfCheckoutServiceMock->share(['data' => 'test']);

        // Assertions
        $this->assertEquals(json_decode(self::SHARE_OF_CHECKOUT_RESPONSE_JSON, true), $result);
    }

    /**
     * Ensure we can catch ShareOfCheckoutServiceException
     * @throws ShareOfCheckoutEndpointException
     */
    public function testShareShareOfCheckoutServiceException(): void
    {
        // Mocks

        $this->clientMock->shouldReceive('sendRequest')->once()->andReturn($this->badResponseMock);

        // Expectations
        $this->expectException(ShareOfCheckoutEndpointException::class);

        // Call
        $this->shareOfCheckoutServiceMock->share(['data' => 'test']);
    }

    /**
     * Ensure we can catch ClientException
     * @return void
     * @throws ShareOfCheckoutEndpointException
     */
    public function testShareClientException(): void
    {
        // Mocks
        $this->clientMock->shouldReceive('sendRequest')->andThrow(ClientException::class);

        // Expectations
        $this->expectException(ShareOfCheckoutEndpointException::class);

        // Call
        $this->shareOfCheckoutServiceMock->share(['data' => 'test']);
    }

    /**
     * Ensure we can get the latest share of checkout
     * @throws ShareOfCheckoutEndpointException
     */
    public function testGetLastUpdateDates(): void
    {
        // Mocks
        $this->clientMock->shouldReceive('sendRequest')->once()->andReturn($this->responseMock);

        // Call
        $result = $this->shareOfCheckoutServiceMock->getLastUpdateDates();

        // Assertions
        $this->assertEquals(json_decode(self::SHARE_OF_CHECKOUT_RESPONSE_JSON, true), $result);
    }

    /**
     * Ensure we can catch ShareOfCheckoutServiceException
     * @throws ShareOfCheckoutEndpointException
     */
    public function testGetLastUpdateDatesShareOfCheckoutServiceException(): void
    {
        // Mocks
        $this->clientMock->shouldReceive('sendRequest')->once()->andReturn($this->badResponseMock);

        // Expectations
        $this->expectException(ShareOfCheckoutEndpointException::class);

        // Call
        $this->shareOfCheckoutServiceMock->getLastUpdateDates();
    }

    /**
     * Ensure we can catch ClientException
     * @return void
     * @throws ShareOfCheckoutEndpointException
     */
    public function testGetLastUpdateDatesClientException(): void
    {
        // Mocks
        $this->clientMock->shouldReceive('sendRequest')->andThrow(ClientException::class);

        // Expectations
        $this->expectException(ShareOfCheckoutEndpointException::class);

        // Call
        $this->shareOfCheckoutServiceMock->getLastUpdateDates();
    }

    /**
     * Ensure we can add consent to share of checkout
     * @throws ShareOfCheckoutEndpointException
     */
    public function testAddConsent(): void
    {
        // Mocks
        $this->clientMock->shouldReceive('sendRequest')->once()->andReturn($this->responseMock);

        // Assertions
        $this->assertTrue($this->shareOfCheckoutServiceMock->addConsent());
    }

    /**
     * Ensure we can catch ShareOfCheckoutServiceException
     * @throws ShareOfCheckoutEndpointException
     */
    public function testAddConsentShareOfCheckoutServiceException(): void
    {
        // Mocks
        $this->clientMock->shouldReceive('sendRequest')->once()->andReturn($this->badResponseMock);

        // Expectations
        $this->expectException(ShareOfCheckoutEndpointException::class);

        // Call
        $this->shareOfCheckoutServiceMock->addConsent();
    }

    /**
     * Ensure we can catch ClientException
     * @return void
     * @throws ShareOfCheckoutEndpointException
     */
    public function testAddConsentClientException(): void
    {
        // Mocks
        $this->clientMock->shouldReceive('sendRequest')->andThrow(ClientException::class);

        // Expectations
        $this->expectException(ShareOfCheckoutEndpointException::class);

        // Call
        $this->shareOfCheckoutServiceMock->addConsent();
    }

    /**
     * Ensure we can remove consent to share of checkout
     * @throws ShareOfCheckoutEndpointException
     */
    public function testRemoveConsent(): void
    {
        // Mocks
        $this->clientMock->shouldReceive('sendRequest')->once()->andReturn($this->responseMock);

        // Assertions
        $this->assertTrue($this->shareOfCheckoutServiceMock->removeConsent());
    }

    /**
     * Ensure we can catch ShareOfCheckoutServiceException
     * @throws ShareOfCheckoutEndpointException
     */
    public function testRemoveConsentShareOfCheckoutServiceException(): void
    {
        // Mocks
        $this->clientMock->shouldReceive('sendRequest')->once()->andReturn($this->badResponseMock);

        // Expectations
        $this->expectException(ShareOfCheckoutEndpointException::class);

        // Call
        $this->shareOfCheckoutServiceMock->removeConsent();
    }

    /**
     * Ensure we can catch ClientException
     * @return void
     * @throws ShareOfCheckoutEndpointException
     */
    public function testRemoveConsentClientException(): void
    {
        // Mocks
        $this->clientMock->shouldReceive('sendRequest')->andThrow(ClientException::class);

        // Expectations
        $this->expectException(ShareOfCheckoutEndpointException::class);

        // Call
        $this->shareOfCheckoutServiceMock->removeConsent();
    }

}

