<?php

declare(strict_types=1);

namespace Apexl\Tests\User\UnitTests;

use apexl\Config\ConfigurationMock;
use apexl\encryption\Encrypt;
use apexl\Io\modules\user\entities\TwoFactorMethodEntity;
use apexl\Io\modules\user\exceptions\TwoFactorException;
use apexl\Io\modules\user\services\twoFactorAuth\TwoFactorSecretCipher;
use apexl\Io\TestInfrastructure\UnitTest;
use PHPUnit\Framework\Attributes\Test;

final class TwoFactorSecretCipherTest extends UnitTest
{
    private function cipher(string $key = 'unit-test-key-0123456789abcdef0123456789'): TwoFactorSecretCipher
    {
        return new TwoFactorSecretCipher(
            new ConfigurationMock(['auth.two_factor.encryption_key' => $key]),
            new Encrypt(),
        );
    }

    #[Test]
    public function secretRoundTripsAndIsNotStoredInPlaintext(): void
    {
        $cipher = $this->cipher();
        $method = new TwoFactorMethodEntity();
        $plain = 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ';

        $cipher->store($method, $plain);

        $this->assertNotSame($plain, $method->secret);
        $this->assertNotEmpty($method->secret);
        $this->assertNotEmpty($method->secret_iv);
        $this->assertSame($plain, $cipher->reveal($method));
    }

    #[Test]
    public function aDifferentKeyDoesNotRevealTheSecret(): void
    {
        $method = new TwoFactorMethodEntity();
        $this->cipher('key-one-000000000000000000000000000000')->store($method, 'my-secret-value');

        // Decrypting with a different key must not return the original plaintext.
        $revealed = null;
        try {
            $revealed = $this->cipher('key-two-999999999999999999999999999999')->reveal($method);
        } catch (TwoFactorException) {
            // Acceptable: openssl may yield false → cipher throws.
            $this->assertTrue(true);

            return;
        }

        $this->assertNotSame('my-secret-value', $revealed);
    }

    #[Test]
    public function missingKeyThrows(): void
    {
        $cipher = $this->cipher('');
        $this->expectException(TwoFactorException::class);
        $cipher->store(new TwoFactorMethodEntity(), 'x');
    }

    #[Test]
    public function revealingAMethodWithNoSecretThrows(): void
    {
        $this->expectException(TwoFactorException::class);
        $this->cipher()->reveal(new TwoFactorMethodEntity());
    }
}
