<?php

declare(strict_types=1);

use App\Framework\Database\ConnectionInterface;
use App\Framework\Vault\DatabaseVault;
use App\Framework\Vault\ValueObjects\SecretKey;
use App\Framework\Vault\ValueObjects\SecretValue;
use App\Framework\Vault\VaultAuditLogger;

beforeEach(function () {
    // Mock Connection für Tests
    $this->connection = Mockery::mock(ConnectionInterface::class);
    $this->auditLogger = Mockery::mock(VaultAuditLogger::class);

    // Generate test encryption key
    $this->encryptionKey = DatabaseVault::generateEncryptionKey();

    $this->vault = new DatabaseVault(
        connection: $this->connection,
        encryptionKey: $this->encryptionKey,
        auditLogger: $this->auditLogger
    );
});

afterEach(function () {
    Mockery::close();
});

describe('SecretKey Value Object', function () {
    it('creates valid secret key', function () {
        $key = SecretKey::from('database.password');

        expect($key->value)->toBe('database.password');
    });

    it('rejects empty key', function () {
        expect(fn () => SecretKey::from(''))
            ->toThrow(InvalidArgumentException::class, 'Secret key cannot be empty');
    });

    it('rejects invalid characters', function () {
        expect(fn () => SecretKey::from('invalid key!'))
            ->toThrow(InvalidArgumentException::class);
    });

    it('accepts valid characters', function () {
        $key = SecretKey::from('api.stripe.secret-key_2024');

        expect($key->value)->toBe('api.stripe.secret-key_2024');
    });
});

describe('SecretValue Value Object', function () {
    it('stores secret value', function () {
        $value = new SecretValue('my-secret-password');

        expect($value->reveal())->toBe('my-secret-password');
    });

    it('masks value in __toString', function () {
        $value = new SecretValue('my-secret-password');

        expect((string) $value)->toBe('[SECRET]');
    });

    it('redacts value in var_dump', function () {
        $value = new SecretValue('my-secret-password');
        $debugInfo = $value->__debugInfo();

        expect($debugInfo['value'])->toBe('[REDACTED]');
        expect($debugInfo['length'])->toBe(18);
    });

    it('checks if value is empty', function () {
        $empty = new SecretValue('');
        $notEmpty = new SecretValue('value');

        expect($empty->isEmpty())->toBeTrue();
        expect($notEmpty->isEmpty())->toBeFalse();
    });
});

describe('DatabaseVault Key Generation', function () {
    it('generates valid encryption key', function () {
        $key = DatabaseVault::generateEncryptionKey();

        expect(strlen($key))->toBe(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
    });

    it('encodes and decodes key correctly', function () {
        $originalKey = DatabaseVault::generateEncryptionKey();
        $encoded = DatabaseVault::encodeKey($originalKey);
        $decoded = DatabaseVault::decodeKey($encoded);

        expect($decoded)->toBe($originalKey);
    });
});

describe('DatabaseVault Basic Operations', function () {
    it('requires correct key length', function () {
        $connection = Mockery::mock(ConnectionInterface::class);
        $auditLogger = Mockery::mock(VaultAuditLogger::class);

        expect(function () use ($connection, $auditLogger) {
            new DatabaseVault(
                connection: $connection,
                encryptionKey: 'too-short',
                auditLogger: $auditLogger
            );
        })->toThrow(InvalidArgumentException::class);
    });

    it('checks if libsodium is available', function () {
        if (! extension_loaded('sodium')) {
            $this->markTestSkipped('Sodium extension not available');
        }

        expect(extension_loaded('sodium'))->toBeTrue();
    });
});
