<?php

namespace Tests\Feature\Services;

use App\Facades\QrCode;
use App\Providers\TwoFAuthServiceProvider;
use App\Services\QrCodeService;
use Exception;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use Tests\Classes\LocalFile;
use Tests\FeatureTestCase;
use Zxing\ChecksumException;
use Zxing\FormatException;
use Zxing\NotFoundException;
use Zxing\QrReader;

/**
 * QrCodeServiceTest test class
 */
#[CoversClass(QrCodeService::class)]
#[CoversClass(QrCode::class)]
#[CoversClass(TwoFAuthServiceProvider::class)]
class QrCodeServiceTest extends FeatureTestCase
{
    private const STRING_TO_ENCODE = 'x';

    // private const STRING_ENCODED = 'data:image/svg+xml;base64,[encoded data, 15460 characters]';

    private const DECODED_IMAGE = 'otpauth://totp/test@test.com?secret=A4GRFHVIRBGY7UIW';

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

    // #[Test]
    // public function test_encode_returns_correct_value()
    // {
    //     $this->assertEquals(self::STRING_ENCODED, QrCode::encode(self::STRING_TO_ENCODE));
    // }

    #[Test]
    public function test_decode_valid_image_returns_correct_value()
    {
        $file = LocalFile::fake()->validQrcode();

        $this->assertEquals(self::DECODED_IMAGE, QrCode::decode($file));
    }

    #[Test]
    public function test_decode_invalid_image_throws_an_exception()
    {
        $this->expectException(\App\Exceptions\InvalidQrCodeException::class);

        QrCode::decode(LocalFile::fake()->invalidQrcode());
    }

    #[Test]
    #[DataProvider('QrReaderExceptionProvider')]
    public function test_decode_throws_an_exception($exception)
    {
        $this->expectException(\App\Exceptions\InvalidQrCodeException::class);

        // QrReader is a final class, so we need to mock it here with a new object instance
        // to then bind it to the container
        $fileContent = LocalFile::fake()->validQrcode()->get();
        $qrReader    = \Mockery::mock(new QrReader($fileContent, QrReader::SOURCE_TYPE_BLOB))->makePartial();
        $qrReader->shouldReceive('text')->andReturn('');
        $qrReader->shouldReceive('getError')->andReturn($exception);

        $this->app->bind(QrReader::class, function () use ($qrReader) {
            return $qrReader;
        });

        QrCode::decode(LocalFile::fake()->validQrcode());
    }

    public static function QrReaderExceptionProvider()
    {
        return [
            'NotFoundException' => [new NotFoundException],
            'FormatException'   => [new FormatException],
            'ChecksumException' => [new ChecksumException],
            'default'           => [new Exception],
        ];
    }
}
