<?php

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\File;

uses(RefreshDatabase::class);

describe('GenerateKeys Command', function () {
    
    beforeEach(function () {
        // Clean up any existing test keys before each test
        $keyDir = storage_path('testing/oauth');
        if (File::exists($keyDir)) {
            File::deleteDirectory($keyDir);
        }
    });
    
    afterEach(function () {
        // Clean up test keys after each test
        $keyDir = storage_path('testing/oauth');
        if (File::exists($keyDir)) {
            File::deleteDirectory($keyDir);
        }
    });

    it('generates RSA key pair successfully', function () {
        $keyDir = storage_path('testing/oauth');
        $privatePath = "$keyDir/private.pem";
        $publicPath = "$keyDir/public.pem";

        $this->artisan('app:generate-keys')
            ->expectsOutput('Generating RSA key pair...')
            ->expectsOutput('✅ Keys generated:')
            ->expectsOutput("- Private: $privatePath")
            ->expectsOutput("- Public : $publicPath")
            ->assertExitCode(0);

        // Verify files were created
        expect(File::exists($privatePath))->toBe(true);
        expect(File::exists($publicPath))->toBe(true);

        // Verify file permissions
        expect(substr(sprintf('%o', fileperms($privatePath)), -3))->toBe('600');
        expect(substr(sprintf('%o', fileperms($publicPath)), -3))->toBe('644');

        // Verify key content is valid
        $privateKeyContent = File::get($privatePath);
        $publicKeyContent = File::get($publicPath);
        
        expect($privateKeyContent)->toContain('-----BEGIN PRIVATE KEY-----');
        expect($privateKeyContent)->toContain('-----END PRIVATE KEY-----');
        expect($publicKeyContent)->toContain('-----BEGIN PUBLIC KEY-----');
        expect($publicKeyContent)->toContain('-----END PUBLIC KEY-----');

        // Verify the keys are actually valid RSA keys
        $privateKey = openssl_pkey_get_private($privateKeyContent);
        $publicKey = openssl_pkey_get_public($publicKeyContent);
        
        expect($privateKey)->not()->toBe(false);
        expect($publicKey)->not()->toBe(false);
    });

    it('prevents overwriting existing keys', function () {
        $keyDir = storage_path('testing/oauth');
        $privatePath = "$keyDir/private.pem";
        $publicPath = "$keyDir/public.pem";

        // Create directory and dummy files
        File::makeDirectory($keyDir, 0700, true);
        File::put($privatePath, 'dummy private key');
        File::put($publicPath, 'dummy public key');

        $this->artisan('app:generate-keys')
            ->expectsOutput('Keys already exist. Aborting.')
            ->assertExitCode(1);

        // Verify original files are unchanged
        expect(File::get($privatePath))->toBe('dummy private key');
        expect(File::get($publicPath))->toBe('dummy public key');
    });

    it('creates oauth directory if it does not exist', function () {
        $keyDir = storage_path('testing/oauth');
        
        // Ensure directory doesn't exist
        expect(File::exists($keyDir))->toBe(false);

        $this->artisan('app:generate-keys')
            ->assertExitCode(0);

        // Verify directory was created with correct permissions
        expect(File::exists($keyDir))->toBe(true);
        expect(substr(sprintf('%o', fileperms($keyDir)), -3))->toBe('700');
    });

    it('generates different keys on multiple runs', function () {
        // First generation
        $this->artisan('app:generate-keys')->assertExitCode(0);
        
        $keyDir = storage_path('testing/oauth');
        $privatePath = "$keyDir/private.pem";
        $publicPath = "$keyDir/public.pem";
        
        $firstPrivateKey = File::get($privatePath);
        $firstPublicKey = File::get($publicPath);

        // Clean up and generate again
        File::deleteDirectory($keyDir);
        
        $this->artisan('app:generate-keys')->assertExitCode(0);
        
        $secondPrivateKey = File::get($privatePath);
        $secondPublicKey = File::get($publicPath);

        // Keys should be different
        expect($firstPrivateKey)->not()->toBe($secondPrivateKey);
        expect($firstPublicKey)->not()->toBe($secondPublicKey);
    });

    it('generates 2048-bit RSA keys', function () {
        $this->artisan('app:generate-keys')->assertExitCode(0);

        $keyDir = storage_path('testing/oauth');
        $privatePath = "$keyDir/private.pem";
        
        $privateKeyContent = File::get($privatePath);
        $privateKey = openssl_pkey_get_private($privateKeyContent);
        $details = openssl_pkey_get_details($privateKey);

        expect($details['bits'])->toBe(2048);
        expect($details['type'])->toBe(OPENSSL_KEYTYPE_RSA);
    });

    it('supports custom path option', function () {
        $customDir = storage_path('testing/custom-oauth');
        $privatePath = "$customDir/private.pem";
        $publicPath = "$customDir/public.pem";

        $this->artisan("app:generate-keys --path={$customDir}")
            ->expectsOutput('Generating RSA key pair...')
            ->expectsOutput('✅ Keys generated:')
            ->expectsOutput("- Private: $privatePath")
            ->expectsOutput("- Public : $publicPath")
            ->assertExitCode(0);

        // Verify files were created in custom directory
        expect(File::exists($privatePath))->toBe(true);
        expect(File::exists($publicPath))->toBe(true);

        // Clean up custom directory
        File::deleteDirectory($customDir);
    });
});