<?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\TransactionsByDate;
use ComerciaGlobalPayments\AddonPayments\SDK\Response\ErrorResponse;
use ComerciaGlobalPayments\AddonPayments\SDK\Response\Operation;
use DateTime;
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\TransactionsByDate
 * @covers \ComerciaGlobalPayments\AddonPayments\SDK\Response\Operation
 * @covers \ComerciaGlobalPayments\AddonPayments\SDK\Response\Transaction
 * @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.LongVariable)
 * @SuppressWarnings(PHPMD.ShortVariable)
 */
class TransactionsByDateTest extends EPGJsTestBase
{
    /**
     * @covers       ::transactionsByDate
     * @covers       ::doEncryptedRequest
     * @covers       ::getEndpoints
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Request\AbstractRequest
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Request\TransactionsByDate
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Response\Transaction
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Response\AbstractResponse
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Response\Operation
     */
    public function testTransactionsByDateWithCbc(): 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 TransactionsByDate())
            ->setFrom(new DateTime())
            ->setTo(new DateTime());
        $response = $this->apiClient->transactionsByDate($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       ::transactionsByDate
     * @covers       ::doEncryptedRequest
     * @covers       ::getEndpoints
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Request\AbstractRequest
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Request\TransactionsByDate
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Response\AbstractResponse
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Response\Operation
     */
    public function testTransactionsByDateWithEcb(): 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 TransactionsByDate())
            ->setFrom(new DateTime())
            ->setTo(new DateTime());
        $response = $this->apiClient->transactionsByDate($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       ::transactionsByDate
     * @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
     * @covers       \ComerciaGlobalPayments\AddonPayments\SDK\Response\Transaction
     */
    public function testFailedTransactionsByDate(): 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\TransactionsByDate $request */
        $request = (new TransactionsByDate())
            ->setFrom(new DateTime())
            ->setTo(new DateTime())
            ->addHeader(self::EXPECTED_TO_FAIL_HEADER, $expectToFail ? 'true' : 'false');
        $response = $this->apiClient->transactionsByDate($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\TransactionsByDate
     */
    public function testJson(): void
    {
        $merchantId = uniqid();
        $from = new DateTime();
        $to = new DateTime();

        $from->setTimeStamp(random_int(0, time()));
        $to->setTimeStamp(random_int(0, time()));

        $request = (new TransactionsByDate())
            ->setMerchantId($merchantId)
            ->setFrom($from)
            ->setTo($to);

        $json = $request->jsonSerialize();

        self::assertIsArray($json);

        self::assertArrayHasKey('merchantId', $json, print_r($json, true));
        self::assertArrayHasKey('from', $json);
        self::assertArrayHasKey('to', $json);

        self::assertEquals($json['merchantId'], $merchantId);
        self::assertEquals($json['from'], $from->format('Y/m/d H:i:s'));
        self::assertEquals($json['to'], $to->format('Y/m/d H:i:s'));
    }
}
