<?php

declare(strict_types=1);

namespace ComerciaGlobalPayments\Tests\AddonPayments\SDK\Api\Unit\EPGJs;

use ComerciaGlobalPayments\AddonPayments\SDK\Api\EPGJs;
use ComerciaGlobalPayments\AddonPayments\SDK\Encryption\Cypher\Aes256Ecb;
use ComerciaGlobalPayments\AddonPayments\SDK\Request\Transactions;
use ComerciaGlobalPayments\AddonPayments\SDK\Response\ErrorResponse;
use ComerciaGlobalPayments\AddonPayments\SDK\Response\Operation;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;

/**
 * Tests transactions through EPGJs API client.
 *
 * @coversDefaultClass \ComerciaGlobalPayments\AddonPayments\SDK\Api\EPGJs
 * @covers \ComerciaGlobalPayments\AddonPayments\SDK\Request\Transactions
 * @covers \ComerciaGlobalPayments\AddonPayments\SDK\Response\Transaction
 * @covers \ComerciaGlobalPayments\AddonPayments\SDK\Response\Operation
 * @covers \ComerciaGlobalPayments\AddonPayments\SDK\Request\AbstractEncryptedRequest
 *
 * @uses   \ComerciaGlobalPayments\AddonPayments\SDK\Api\AbstractApiClient
 * @uses   \ComerciaGlobalPayments\AddonPayments\SDK\Encryption\JWT\PrivatePublicKeysEncoder
 * @uses   \ComerciaGlobalPayments\AddonPayments\SDK\Encryption\Cypher\Aes256Cbc
 * @uses   \ComerciaGlobalPayments\AddonPayments\SDK\Encryption\Cypher\Aes256Ecb
 * @uses   \ComerciaGlobalPayments\AddonPayments\SDK\Response\ErrorResponse
 * @uses   \ComerciaGlobalPayments\AddonPayments\SDK\Request\AbstractRequest
 *
 * @SuppressWarnings(PHPMD.StaticAccess)
 * @SuppressWarnings(PHPMD.LongVariableNames)
 */
class TransactionsTest extends EPGJsTestBase
{
    /**
     * @covers       ::transactions
     * @covers       ::doEncryptedRequest
     * @covers       ::getEndpoints
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Request\AbstractRequest
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Request\Transactions
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Response\AbstractResponse
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Response\Operation
     */
    public function testTransactionsWithCbc(): void
    {
        $mockHandler = new MockHandler(
            [[$this, 'encryptedResponseHandler']]
        );

        $handler = HandlerStack::create($mockHandler);
        $httpClient = new Client(['handler' => $handler, 'delay' => random_int(1, 60)]);
        $this->apiClient = new EPGJs($this->apiClientConfig, $httpClient);
        $request = (new Transactions())
            ->setTransactions([uniqid(), uniqid()])
            ->setPostback(random_int(0, 1))
            ->setUrl('https://example.com/transactions');
        $response = $this->apiClient->transactions($request);
        self::assertInstanceOf(
            Operation::class,
            $response,
            'Test the API response is OK'
        );
        self::assertNotEmpty(
            $response->getOperations(),
            'Test the API response returns one or more transactions'
        );
        self::assertNotEmpty(
            $response->getHttpResponse(),
            'Test the API response wraps the actual HTTP response'
        );
    }

    /**
     * @covers       ::transactions
     * @covers       ::doEncryptedRequest
     * @covers       ::getEndpoints
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Request\AbstractRequest
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Request\Transactions
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Response\AbstractResponse
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Response\Operation
     */
    public function testTransactionsWithEcb(): void
    {
        $mockHandler = new MockHandler(
            [[$this, 'encryptedResponseHandler']]
        );

        $handler = HandlerStack::create($mockHandler);
        $httpClient = new Client(['handler' => $handler, 'delay' => random_int(1, 60)]);
        $config = $this->apiClientConfig;
        $config['encryption'] = Aes256Ecb::NAME;
        $this->apiClient = new EPGJs($config, $httpClient);
        $request = (new Transactions())
            ->setTransactions([uniqid(), uniqid()])
            ->setPostback(random_int(0, 1))
            ->setUrl('https://example.com/transactions');
        $response = $this->apiClient->transactions($request);
        self::assertInstanceOf(
            Operation::class,
            $response,
            'Test the API response is OK'
        );
        self::assertNotEmpty(
            $response->getOperations(),
            'Test the API response returns one or more transactions'
        );
        self::assertNotEmpty(
            $response->getHttpResponse(),
            'Test the API response wraps the actual HTTP response'
        );
    }

    /**
     * @covers       ::transactions
     * @covers       ::doEncryptedRequest
     * @covers       ::getEndpoints
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Request\AbstractRequest
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Request\transactions
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Response\AbstractResponse
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Response\Operation
     */
    public function testFailedTransactions(): void
    {
        $mockHandler = new MockHandler(
            [[$this, 'encryptedResponseHandler']]
        );

        $handler = HandlerStack::create($mockHandler);
        $httpClient = new Client(['handler' => $handler, 'delay' => random_int(1, 60)]);
        $expectToFail = 0 === (random_int(2, 5) % 2);
        $this->apiClient = new EPGJs($this->apiClientConfig, $httpClient);
        /** @var \ComerciaGlobalPayments\AddonPayments\SDK\Request\Transactions $request */
        $request = (new Transactions())
            ->setTransactions([uniqid(), uniqid()])
            ->setPostback(random_int(0, 1))
            ->setUrl('https://example.com/transactions')
            ->addHeader(self::EXPECTED_TO_FAIL_HEADER, $expectToFail ? 'true' : 'false');
        $response = $this->apiClient->transactions($request);
        if ($expectToFail) {
            self::assertInstanceOf(
                ErrorResponse::class,
                $response,
                'Test the API response is KO'
            );
            self::assertNotEmpty(
                $response->getHttpResponse(),
                'Test the API error response wraps the actual HTTP response'
            );

            return;
        }

        self::assertInstanceOf(
            Operation::class,
            $response,
            'Test the API response is OK'
        );
    }

    /**
     * @covers  \ComerciaGlobalPayments\AddonPayments\SDK\Request\Transactions
     */
    public function testJson(): void
    {
        $merchantId = uniqid();
        $token = uniqid();
        $transactions = [uniqid(), uniqid()];
        $postback = random_int(0, 1);
        $url = 'https://example.com/transactions';

        $request = (new Transactions())
            ->setMerchantId($merchantId)
            ->setToken($token)
            ->setTransactions($transactions)
            ->setPostback($postback)
            ->setUrl($url);

        $json = $request->jsonSerialize();

        self::assertIsArray($json);

        self::assertArrayHasKey('token', $json);
        self::assertArrayHasKey('merchantId', $json);
        self::assertArrayHasKey('transactions', $json);
        self::assertArrayHasKey('postback', $json);
        self::assertArrayHasKey('url', $json);

        self::assertEquals($json['token'], $token);
        self::assertEquals($json['merchantId'], $merchantId);
        self::assertEquals($json['transactions'], implode(';', $transactions));
        self::assertEquals($json['postback'], $postback);
        self::assertEquals($json['url'], $url);
    }
}
