<?php

namespace App\Tests\integration\Infrastructure;

use App\Domain\Logger;
use App\Infrastructure\SnsNotifier;
use App\Tests\Helpers\Assert;
use App\Tests\integration\Infrastructure\Helpers\AsyncTestHelpers;
use App\Tests\unit\Utils\MockableFeatureFlags;
use App\Utils\Features;
use Aws\Sns\SnsClient;
use Aws\Sqs\SqsClient;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use function json_decode;

class SnsNotifierTest extends TestCase
{
    private SnsClient $snsClient;
    private SqsClient $sqsClient;
    private $topicArn;
    private $queueUrl;
    private const SNS_ENDPOINT = 'http://sns:9911';
    private const SQS_ENDPOINT = 'http://sqs:9324';
    private const TEST_CONFIGURATION = [
        'region' => 'us-east-1',
        'version' => 'latest',
        'credentials' => [
            'key' => 'KEY',
            'secret' => 'SECRET'
        ],
        'use_path_style_endpoint' => true,
    ];
    private const NOTIFIER_CONFIGURATION = [
        'region' => 'us-east-1',
        'credentials' => [
            'key' => 'KEY',
            'secret' => 'SECRET'
        ],
        'endpoint' => self::SNS_ENDPOINT
    ];
    private $logger;

    public function setUp(): void
    {
        $features = new MockableFeatureFlags();
        Features::create($features);
        $this->snsClient = $this->createSnsClient(self::TEST_CONFIGURATION);
        $this->topicArn = $this->createTopic();

        $this->sqsClient = $this->createSqsClient(self::TEST_CONFIGURATION, self::SQS_ENDPOINT);
        $this->queueUrl = $this->subscribeToTopic($this->topicArn);
        $this->logger = $this->prophesize(Logger::class);
    }
    
    public function tearDown(): void
    {
        $this->sqsClient->purgeQueue(['QueueUrl' => $this->queueUrl]);
        $this->snsClient->deleteTopic(['TopicArn' => $this->topicArn]);
    }

    /** @test */
    public function shouldNotifyAnEventToSnS()
    {
        $snsNotifier = new SnsNotifier(
            self::NOTIFIER_CONFIGURATION,
            $this->topicArn,
            $this->logger->reveal()
        );

        $dummyNotification = ["dummy" => 'dummy notification'];

        $snsNotifier->notify($dummyNotification);

        $this->assertNotificationWasPublished($dummyNotification);
    }

    private function assertNotificationWasPublished($expectedNotification): void
    {
        $sleepTime = 3;
        $numTries = 3;
        $errorDescription = 'Message could not be inserted in SQS';
        $probe = function () {
            $receiveMessage = $this->sqsClient->receiveMessage([
                'QueueUrl' => $this->queueUrl,
                'MaxNumberOfMessages' => 10]);
            return $receiveMessage['Messages'];
        };

        $check = function ($messages) use ($expectedNotification) {
            if (!$messages) {
                return false;
            }
            $firstMessageBody = json_decode($messages[0]['Body'], true);
            $actualNotification = json_decode($firstMessageBody['Message'], true);

            Assert::strictEquals($expectedNotification, $actualNotification);

            return true;
        };

        AsyncTestHelpers::assertWithPolling(
            $sleepTime,
            $numTries,
            $errorDescription,
            $probe,
            $check
        );
    }

    private function createSqsClient(array $baseConfiguration, string $sqsEndpoint): SqsClient
    {
        return new SqsClient(array_merge($baseConfiguration, [
            'endpoint' => $sqsEndpoint,
        ]));
    }

    private function createSnsClient(array $baseConfiguration): SnsClient
    {
        return new SnsClient(array_merge($baseConfiguration, [
            'endpoint' => self::SNS_ENDPOINT,
        ]));
    }

    private function createTopic()
    {
        $topic = $this->snsClient->createTopic([
            'Name' => 'snsName-test'
        ]);

        return $topic['TopicArn'];
    }

    private function subscribeToTopic($topicArn)
    {
        $queueName = 'sqsQueue-name';

        $queue = $this->sqsClient->createQueue([
            'QueueName' => $queueName
        ]);

        $this->snsClient->subscribe([
            'Protocol' => 'sqs',
            'Endpoint' => "aws-sqs://{$queueName}?amazonSQSEndpoint=" . self::SQS_ENDPOINT . "&accessKey=&secretKey=",
            'TopicArn' => $topicArn
        ]);

        return $queue['QueueUrl'];
    }
}