<?php

declare(strict_types=1);

use App\Framework\Config\Environment;
use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ExitCode;
use App\Framework\Core\ValueObjects\Email;
use App\Framework\Deployment\Ssl\Commands\SslInitCommand;
use App\Framework\Deployment\Ssl\Services\SslCertificateService;
use App\Framework\Deployment\Ssl\ValueObjects\CertificateMode;
use App\Framework\Deployment\Ssl\ValueObjects\CertificateStatus;
use App\Framework\Deployment\Ssl\ValueObjects\DomainName;
use App\Framework\Deployment\Ssl\ValueObjects\SslConfiguration;
use App\Framework\Filesystem\FilePath;

describe('SslInitCommand', function () {
    beforeEach(function () {
        $this->sslService = Mockery::mock(SslCertificateService::class);
        $this->environment = Mockery::mock(Environment::class);

        // Setup environment mock
        $this->environment->shouldReceive('get')
            ->with('DOMAIN_NAME', 'michaelschiemer.de')
            ->andReturn('example.com');
        $this->environment->shouldReceive('get')
            ->with('SSL_EMAIL', 'mail@michaelschiemer.de')
            ->andReturn('admin@example.com');
        $this->environment->shouldReceive('get')
            ->with('LETSENCRYPT_STAGING', '0')
            ->andReturn('1');

        $this->command = new SslInitCommand(
            $this->sslService,
            $this->environment
        );

        $this->input = Mockery::mock(ConsoleInput::class);
    });

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

    it('executes successfully when test and obtain succeed', function () {
        // Mock test success
        $this->sslService->shouldReceive('test')
            ->once()
            ->andReturn(true);

        // Mock obtain success
        $status = new CertificateStatus(
            exists: true,
            isValid: true,
            notBefore: new DateTimeImmutable('-30 days'),
            notAfter: new DateTimeImmutable('+60 days'),
            issuer: 'Let\'s Encrypt',
            subject: 'example.com',
            daysUntilExpiry: 60,
            isExpiring: false,
            isExpired: false
        );

        $this->sslService->shouldReceive('obtain')
            ->once()
            ->andReturn($status);

        $exitCode = $this->command->execute($this->input);

        expect($exitCode)->toBe(ExitCode::SUCCESS);
    });

    it('fails when test fails', function () {
        // Mock test failure
        $this->sslService->shouldReceive('test')
            ->once()
            ->andReturn(false);

        // obtain should not be called
        $this->sslService->shouldNotReceive('obtain');

        $exitCode = $this->command->execute($this->input);

        expect($exitCode)->toBe(ExitCode::FAILURE);
    });

    it('fails when obtain throws exception', function () {
        // Mock test success
        $this->sslService->shouldReceive('test')
            ->once()
            ->andReturn(true);

        // Mock obtain exception
        $this->sslService->shouldReceive('obtain')
            ->once()
            ->andThrow(new \RuntimeException('Failed to obtain certificate'));

        $exitCode = $this->command->execute($this->input);

        expect($exitCode)->toBe(ExitCode::FAILURE);
    });

    it('uses staging mode from environment', function () {
        $this->sslService->shouldReceive('test')
            ->once()
            ->with(Mockery::on(function ($config) {
                return $config->mode === CertificateMode::STAGING;
            }))
            ->andReturn(true);

        $this->sslService->shouldReceive('obtain')
            ->once()
            ->with(Mockery::on(function ($config) {
                return $config->mode === CertificateMode::STAGING;
            }))
            ->andReturn(new CertificateStatus(
                exists: true,
                isValid: true,
                notBefore: null,
                notAfter: null,
                issuer: null,
                subject: null,
                daysUntilExpiry: 60,
                isExpiring: false,
                isExpired: false
            ));

        $this->command->execute($this->input);
    });

    it('displays certificate information after obtain', function () {
        $this->sslService->shouldReceive('test')
            ->once()
            ->andReturn(true);

        $status = new CertificateStatus(
            exists: true,
            isValid: true,
            notBefore: new DateTimeImmutable('2024-01-01'),
            notAfter: new DateTimeImmutable('2024-12-31'),
            issuer: 'Let\'s Encrypt Authority X3',
            subject: 'example.com',
            daysUntilExpiry: 90,
            isExpiring: false,
            isExpired: false
        );

        $this->sslService->shouldReceive('obtain')
            ->once()
            ->andReturn($status);

        $exitCode = $this->command->execute($this->input);

        expect($exitCode)->toBe(ExitCode::SUCCESS);
    });
});
