<?php

declare(strict_types=1);

namespace PHPMax\Transport;

use PHPMax\Exception\ProtocolException;

final class TlsStreamEnabler
{
    




    public static function enable($stream, string $peerName, float $timeout): void
    {
        if (!is_resource($stream)) {
            throw new ProtocolException('TLS transport stream is not available');
        }
        if (trim($peerName) === '') {
            throw new ProtocolException('TLS peer name must not be empty');
        }

        $deadline = microtime(true) + max(0.001, $timeout);

        stream_context_set_option($stream, 'ssl', 'SNI_enabled', true);
        stream_context_set_option($stream, 'ssl', 'peer_name', $peerName);
        stream_context_set_option($stream, 'ssl', 'verify_peer', true);
        stream_context_set_option($stream, 'ssl', 'verify_peer_name', true);

        if (!stream_set_blocking($stream, false)) {
            throw new ProtocolException('Failed to prepare TLS transport');
        }

        try {
            while (true) {
                set_error_handler(static function (): bool {
                    return true;
                });
                try {
                    $enabled = stream_socket_enable_crypto($stream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
                } finally {
                    restore_error_handler();
                }

                if ($enabled === true) {
                    return;
                }
                if ($enabled === false) {
                    throw new ProtocolException('TLS handshake failed');
                }

                $remaining = $deadline - microtime(true);
                if ($remaining <= 0.0) {
                    throw new ProtocolException('Timed out during TLS handshake');
                }

                $read = [$stream];
                $write = [$stream];
                $except = [];
                $seconds = (int) floor($remaining);
                $microseconds = (int) max(0, ($remaining - $seconds) * 1000000);

                set_error_handler(static function (): bool {
                    return true;
                });
                try {
                    $ready = stream_select($read, $write, $except, $seconds, $microseconds);
                } finally {
                    restore_error_handler();
                }

                if ($ready === false) {
                    throw new ProtocolException('Failed while waiting for TLS handshake');
                }
                if ($ready === 0) {
                    throw new ProtocolException('Timed out during TLS handshake');
                }

                 
                 
                if ($read === []) {
                    usleep((int) min(10000, max(1000, $remaining * 1000000)));
                }
            }
        } finally {
            if (is_resource($stream)) {
                stream_set_blocking($stream, true);
            }
        }
    }
}
