<?php

declare(strict_types=1);

use App\Framework\Config\Environment;
use App\Framework\Deployment\Ssl\HealthChecks\SslCertificateHealthCheck;
use App\Framework\Deployment\Ssl\Services\SslCertificateService;
use App\Framework\Deployment\Ssl\ValueObjects\CertificateStatus;
use App\Framework\Deployment\Ssl\ValueObjects\DomainName;
use App\Framework\Health\HealthCheckCategory;
use App\Framework\Health\HealthStatus;

describe('SslCertificateHealthCheck', 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('0');

        $this->healthCheck = new SslCertificateHealthCheck(
            $this->sslService,
            $this->environment
        );
    });

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

    it('returns healthy status for valid certificate', function () {
        $status = new CertificateStatus(
            exists: true,
            isValid: true,
            notBefore: new DateTimeImmutable('-30 days'),
            notAfter: new DateTimeImmutable('+60 days'),
            issuer: 'Let\'s Encrypt Authority X3',
            subject: 'example.com',
            daysUntilExpiry: 60,
            isExpiring: false,
            isExpired: false
        );

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

        $result = $this->healthCheck->check();

        expect($result->status)->toBe(HealthStatus::HEALTHY);
        expect($result->operation)->toBe('SSL Certificate Check');
        expect($result->details['domain'])->toBe('example.com');
        expect($result->details['days_until_expiry'])->toBe(60);
    });

    it('returns unhealthy status when certificate does not exist', function () {
        $status = CertificateStatus::notFound();

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

        $result = $this->healthCheck->check();

        expect($result->status)->toBe(HealthStatus::UNHEALTHY);
        expect($result->reason)->toBe('Certificate not found');
    });

    it('returns unhealthy status for expired certificate', function () {
        $status = new CertificateStatus(
            exists: true,
            isValid: false,
            notBefore: new DateTimeImmutable('-180 days'),
            notAfter: new DateTimeImmutable('-10 days'),
            issuer: 'Let\'s Encrypt Authority X3',
            subject: 'example.com',
            daysUntilExpiry: -10,
            isExpiring: false,
            isExpired: true
        );

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

        $result = $this->healthCheck->check();

        expect($result->status)->toBe(HealthStatus::UNHEALTHY);
        expect($result->reason)->toBe('Certificate has expired');
        expect($result->details['days_since_expiry'])->toBe(10);
    });

    it('returns warning status for expiring certificate', function () {
        $status = new CertificateStatus(
            exists: true,
            isValid: true,
            notBefore: new DateTimeImmutable('-60 days'),
            notAfter: new DateTimeImmutable('+20 days'),
            issuer: 'Let\'s Encrypt Authority X3',
            subject: 'example.com',
            daysUntilExpiry: 20,
            isExpiring: true,
            isExpired: false
        );

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

        $result = $this->healthCheck->check();

        expect($result->status)->toBe(HealthStatus::WARNING);
        expect($result->reason)->toBe('Certificate expires in 20 days');
        expect($result->details['days_until_expiry'])->toBe(20);
    });

    it('returns unhealthy status for invalid certificate', function () {
        $status = new CertificateStatus(
            exists: true,
            isValid: false,
            notBefore: new DateTimeImmutable('-30 days'),
            notAfter: new DateTimeImmutable('+60 days'),
            issuer: null,
            subject: null,
            daysUntilExpiry: 60,
            isExpiring: false,
            isExpired: false,
            errors: ['Certificate verification failed']
        );

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

        $result = $this->healthCheck->check();

        expect($result->status)->toBe(HealthStatus::UNHEALTHY);
        expect($result->reason)->toBe('Certificate is invalid');
        expect($result->details['errors'])->toContain('Certificate verification failed');
    });

    it('returns unhealthy status on exception', function () {
        $this->sslService->shouldReceive('getStatus')
            ->once()
            ->andThrow(new \RuntimeException('Connection failed'));

        $result = $this->healthCheck->check();

        expect($result->status)->toBe(HealthStatus::UNHEALTHY);
        expect($result->reason)->toBe('Health check failed');
        expect($result->exception)->toBeInstanceOf(\RuntimeException::class);
    });

    it('has correct name and category', function () {
        expect($this->healthCheck->getName())->toBe('SSL Certificate');
        expect($this->healthCheck->getCategory())->toBe(HealthCheckCategory::SECURITY);
    });

    it('has reasonable timeout', function () {
        $timeout = $this->healthCheck->getTimeout();

        expect($timeout)->toBeInt();
        expect($timeout)->toBeGreaterThan(0);
        expect($timeout)->toBeLessThanOrEqual(10000); // Max 10 seconds
    });

    it('measures response time', function () {
        $status = new CertificateStatus(
            exists: true,
            isValid: true,
            notBefore: null,
            notAfter: null,
            issuer: null,
            subject: null,
            daysUntilExpiry: 60,
            isExpiring: false,
            isExpired: false
        );

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

        $result = $this->healthCheck->check();

        expect($result->responseTimeMs)->toBeFloat();
        expect($result->responseTimeMs)->toBeGreaterThanOrEqual(0);
    });
});
