"""Implementation module for SSL socket operations.  See the socket module
for documentation.
"""

import sys
from _typeshed import ReadableBuffer, StrOrBytesPath
from collections.abc import Callable
from ssl import (
    SSLCertVerificationError as SSLCertVerificationError,
    SSLContext,
    SSLEOFError as SSLEOFError,
    SSLError as SSLError,
    SSLObject,
    SSLSyscallError as SSLSyscallError,
    SSLWantReadError as SSLWantReadError,
    SSLWantWriteError as SSLWantWriteError,
    SSLZeroReturnError as SSLZeroReturnError,
)
from typing import Any, ClassVar, Final, Literal, TypeAlias, TypedDict, final, overload, type_check_only
from typing_extensions import NotRequired, Self, deprecated, disjoint_base

_PasswordType: TypeAlias = Callable[[], str | bytes | bytearray] | str | bytes | bytearray
_PCTRTT: TypeAlias = tuple[tuple[str, str], ...]
_PCTRTTT: TypeAlias = tuple[_PCTRTT, ...]
_PeerCertRetDictType: TypeAlias = dict[str, str | _PCTRTTT | _PCTRTT]

@type_check_only
class _Cipher(TypedDict):
    aead: bool
    alg_bits: int
    auth: str
    description: str
    digest: str | None
    id: int
    kea: str
    name: str
    protocol: str
    strength_bits: int
    symmetric: str

@type_check_only
class _CertInfo(TypedDict):
    subject: tuple[tuple[tuple[str, str], ...], ...]
    issuer: tuple[tuple[tuple[str, str], ...], ...]
    version: int
    serialNumber: str
    notBefore: str
    notAfter: str
    subjectAltName: NotRequired[tuple[tuple[str, str], ...] | None]
    OCSP: NotRequired[tuple[str, ...] | None]
    caIssuers: NotRequired[tuple[str, ...] | None]
    crlDistributionPoints: NotRequired[tuple[str, ...] | None]

def RAND_add(string: str | ReadableBuffer, entropy: float, /) -> None:
    """Mix string into the OpenSSL PRNG state.

    entropy (a float) is a lower bound on the entropy contained in
    string.  See RFC 4086.
    """

def RAND_bytes(n: int, /) -> bytes:
    """Generate n cryptographically strong pseudo-random bytes."""

if sys.version_info < (3, 12):
    @deprecated("Deprecated since Python 3.6; removed in Python 3.12. Use `ssl.RAND_bytes()` instead.")
    def RAND_pseudo_bytes(n: int, /) -> tuple[bytes, bool]:
        """Generate n pseudo-random bytes.

        Return a pair (bytes, is_cryptographic).  is_cryptographic is True
        if the bytes generated are cryptographically strong.
        """

def RAND_status() -> bool:
    """Returns True if the OpenSSL PRNG has been seeded with enough data and False if not.

    It is necessary to seed the PRNG with RAND_add() on some platforms
    before using the ssl() function.
    """

def get_default_verify_paths() -> tuple[str, str, str, str]:
    """Return search paths and environment vars that are used by SSLContext's set_default_verify_paths() to load default CAs.

    The values are 'cert_file_env', 'cert_file', 'cert_dir_env', 'cert_dir'.
    """

if sys.version_info >= (3, 15):
    def get_sigalgs() -> list[str]: ...

if sys.platform == "win32":
    _EnumRetType: TypeAlias = list[tuple[bytes, str, set[str] | bool]]
    def enum_certificates(store_name: str) -> _EnumRetType:
        """Retrieve certificates from Windows' cert store.

        store_name may be one of 'CA', 'ROOT' or 'MY'.  The system may
        provide more cert storages, too.  The function returns a list of
        (bytes, encoding_type, trust) tuples.  The encoding_type flag can be
        interpreted with X509_ASN_ENCODING or PKCS_7_ASN_ENCODING.  The
        trust setting is either a set of OIDs or the boolean True.
        """

    def enum_crls(store_name: str) -> _EnumRetType:
        """Retrieve CRLs from Windows' cert store.

        store_name may be one of 'CA', 'ROOT' or 'MY'.  The system may provide
        more cert storages, too.  The function returns a list of (bytes,
        encoding_type) tuples.  The encoding_type flag can be interpreted with
        X509_ASN_ENCODING or PKCS_7_ASN_ENCODING.
        """

def txt2obj(txt: str, name: bool = False) -> tuple[int, str, str, str]:
    """Lookup NID, short name, long name and OID of an ASN1_OBJECT.

    By default objects are looked up by OID. With name=True short and
    long name are also matched.
    """

def nid2obj(nid: int, /) -> tuple[int, str, str, str]:
    """Lookup NID, short name, long name and OID of an ASN1_OBJECT by NID."""

@disjoint_base
class _SSLContext:
    check_hostname: bool
    keylog_filename: str | None
    maximum_version: int
    minimum_version: int
    num_tickets: int
    """Control the number of TLSv1.3 session tickets."""

    options: int
    post_handshake_auth: bool
    protocol: int
    security_level: int
    """The current security level."""

    sni_callback: Callable[[SSLObject, str, SSLContext], None | int] | None
    """Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension.

    If the argument is None then the callback is disabled.  The method
    is called with the SSLSocket, the server name as a string, and the
    SSLContext object.

    See RFC 6066 for details of the SNI extension.
    """

    verify_flags: int
    verify_mode: int
    def __new__(cls, protocol: int, /) -> Self: ...
    def cert_store_stats(self) -> dict[str, int]:
        """Returns quantities of loaded X.509 certificates.

        X.509 certificates with a CA extension and certificate revocation
        lists inside the context's cert store.

        NOTE: Certificates in a capath directory aren't loaded unless they
        have been used at least once.
        """

    @overload
    def get_ca_certs(self, binary_form: Literal[False] = False) -> list[_PeerCertRetDictType]:
        """Returns a list of dicts with information of loaded CA certs.

        If the optional argument is True, returns a DER-encoded copy of the
        CA certificate.

        NOTE: Certificates in a capath directory aren't loaded unless they
        have been used at least once.
        """
    @overload
    def get_ca_certs(self, binary_form: Literal[True]) -> list[bytes]: ...
    @overload
    def get_ca_certs(self, binary_form: bool = False) -> Any: ...

    def get_ciphers(self) -> list[_Cipher]: ...
    def load_cert_chain(
        self, certfile: StrOrBytesPath, keyfile: StrOrBytesPath | None = None, password: _PasswordType | None = None
    ) -> None: ...
    def load_dh_params(self, path: str, /) -> None: ...
    def load_verify_locations(
        self,
        cafile: StrOrBytesPath | None = None,
        capath: StrOrBytesPath | None = None,
        cadata: str | ReadableBuffer | None = None,
    ) -> None: ...
    def session_stats(self) -> dict[str, int]: ...
    def set_ciphers(self, cipherlist: str, /) -> None: ...
    def set_default_verify_paths(self) -> None: ...
    def set_ecdh_curve(self, name: str, /) -> None: ...
    if sys.version_info >= (3, 15):
        def get_groups(self, *, include_aliases: bool = False) -> list[str]: ...
        def set_ciphersuites(self, ciphersuites: str, /) -> None: ...
        def set_client_sigalgs(self, sigalgslist: str, /) -> None: ...
        def set_groups(self, grouplist: str, /) -> None: ...
        def set_server_sigalgs(self, sigalgslist: str, /) -> None: ...
    if sys.version_info >= (3, 13):
        def set_psk_client_callback(self, callback: Callable[[str | None], tuple[str | None, bytes]] | None) -> None: ...
        def set_psk_server_callback(
            self, callback: Callable[[str | None], bytes] | None, identity_hint: str | None = None
        ) -> None: ...

@final
class MemoryBIO:
    eof: bool
    """Whether the memory BIO is at EOF."""

    pending: int
    """The number of bytes pending in the memory BIO."""

    def __new__(self) -> Self: ...
    def read(self, size: int = -1, /) -> bytes:
        """Read up to size bytes from the memory BIO.

        If size is not specified, read the entire buffer.
        If the return value is an empty bytes instance, this means either
        EOF or that no data is available. Use the "eof" property to
        distinguish between the two.
        """

    def write(self, b: ReadableBuffer, /) -> int:
        """Writes the bytes b into the memory BIO.

        Returns the number of bytes written.
        """

    def write_eof(self) -> None:
        """Write an EOF marker to the memory BIO.

        When all data has been read, the "eof" property will be True.
        """

@final
class SSLSession:
    __hash__: ClassVar[None]  # type: ignore[assignment]
    @property
    def has_ticket(self) -> bool:
        """Does the session contain a ticket?"""

    @property
    def id(self) -> bytes:
        """Session ID."""

    @property
    def ticket_lifetime_hint(self) -> int:
        """Ticket life time hint."""

    @property
    def time(self) -> int:
        """Session creation time (seconds since epoch)."""

    @property
    def timeout(self) -> int:
        """Session timeout (delta in seconds)."""

# _ssl.Certificate is weird: it can't be instantiated or subclassed.
# Instances can only be created via methods of the private _ssl._SSLSocket class,
# for which the relevant method signatures are:
#
# class _SSLSocket:
#     def get_unverified_chain(self) -> list[Certificate] | None: ...
#     def get_verified_chain(self) -> list[Certificate] | None: ...
#
# You can find a _ssl._SSLSocket object as the _sslobj attribute of a ssl.SSLSocket object

@final
class Certificate:
    def get_info(self) -> _CertInfo: ...

    @overload
    def public_bytes(self) -> str: ...
    @overload
    def public_bytes(self, format: Literal[1] = 1, /) -> str: ...  # ENCODING_PEM
    @overload
    def public_bytes(self, format: Literal[2], /) -> bytes: ...  # ENCODING_DER
    @overload
    def public_bytes(self, format: int, /) -> str | bytes: ...

if sys.version_info < (3, 12):
    err_codes_to_names: dict[tuple[int, int], str]
    err_names_to_codes: dict[str, tuple[int, int]]
    lib_codes_to_names: dict[int, str]

_DEFAULT_CIPHERS: Final[str]

# SSL error numbers
SSL_ERROR_ZERO_RETURN: Final = 6
SSL_ERROR_WANT_READ: Final = 2
SSL_ERROR_WANT_WRITE: Final = 3
SSL_ERROR_WANT_X509_LOOKUP: Final = 4
SSL_ERROR_SYSCALL: Final = 5
SSL_ERROR_SSL: Final = 1
SSL_ERROR_WANT_CONNECT: Final = 7
SSL_ERROR_EOF: Final = 8
SSL_ERROR_INVALID_ERROR_CODE: Final = 10

# verify modes
CERT_NONE: Final = 0
CERT_OPTIONAL: Final = 1
CERT_REQUIRED: Final = 2

# verify flags
VERIFY_DEFAULT: Final = 0
VERIFY_CRL_CHECK_LEAF: Final = 0x04
VERIFY_CRL_CHECK_CHAIN: Final = 0x0C
VERIFY_X509_STRICT: Final = 0x20
VERIFY_X509_TRUSTED_FIRST: Final = 0x8000
VERIFY_ALLOW_PROXY_CERTS: Final = 0x40
VERIFY_X509_PARTIAL_CHAIN: Final = 0x80000

# alert descriptions
ALERT_DESCRIPTION_CLOSE_NOTIFY: Final = 0
ALERT_DESCRIPTION_UNEXPECTED_MESSAGE: Final = 10
ALERT_DESCRIPTION_BAD_RECORD_MAC: Final = 20
ALERT_DESCRIPTION_RECORD_OVERFLOW: Final = 22
ALERT_DESCRIPTION_DECOMPRESSION_FAILURE: Final = 30
ALERT_DESCRIPTION_HANDSHAKE_FAILURE: Final = 40
ALERT_DESCRIPTION_BAD_CERTIFICATE: Final = 42
ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: Final = 43
ALERT_DESCRIPTION_CERTIFICATE_REVOKED: Final = 44
ALERT_DESCRIPTION_CERTIFICATE_EXPIRED: Final = 45
ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN: Final = 46
ALERT_DESCRIPTION_ILLEGAL_PARAMETER: Final = 47
ALERT_DESCRIPTION_UNKNOWN_CA: Final = 48
ALERT_DESCRIPTION_ACCESS_DENIED: Final = 49
ALERT_DESCRIPTION_DECODE_ERROR: Final = 50
ALERT_DESCRIPTION_DECRYPT_ERROR: Final = 51
ALERT_DESCRIPTION_PROTOCOL_VERSION: Final = 70
ALERT_DESCRIPTION_INSUFFICIENT_SECURITY: Final = 71
ALERT_DESCRIPTION_INTERNAL_ERROR: Final = 80
ALERT_DESCRIPTION_USER_CANCELLED: Final = 90
ALERT_DESCRIPTION_NO_RENEGOTIATION: Final = 100
ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: Final = 110
ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE: Final = 111
ALERT_DESCRIPTION_UNRECOGNIZED_NAME: Final = 112
ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE: Final = 113
ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE: Final = 114
ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY: Final = 115

# protocol versions
PROTOCOL_SSLv23: Final = 2
PROTOCOL_TLS: Final = 2
PROTOCOL_TLS_CLIENT: Final = 16
PROTOCOL_TLS_SERVER: Final = 17
PROTOCOL_TLSv1: Final = 3
PROTOCOL_TLSv1_1: Final = 4
PROTOCOL_TLSv1_2: Final = 5

# protocol options
OP_ALL: Final[int]
OP_NO_SSLv2: Final = 0x0
OP_NO_SSLv3: Final = 0x2000000
OP_NO_TLSv1: Final = 0x4000000
OP_NO_TLSv1_1: Final = 0x10000000
OP_NO_TLSv1_2: Final = 0x8000000
OP_NO_TLSv1_3: Final = 0x20000000
OP_CIPHER_SERVER_PREFERENCE: Final = 0x400000
OP_SINGLE_DH_USE: Final = 0x0
OP_NO_TICKET: Final = 0x4000
OP_SINGLE_ECDH_USE: Final = 0x0
OP_NO_COMPRESSION: Final = 0x20000
OP_ENABLE_MIDDLEBOX_COMPAT: Final = 0x100000
OP_NO_RENEGOTIATION: Final = 0x40000000
if sys.version_info >= (3, 11) or sys.platform == "linux":
    OP_IGNORE_UNEXPECTED_EOF: Final = 0x80
if sys.version_info >= (3, 12):
    OP_LEGACY_SERVER_CONNECT: Final = 0x4
    OP_ENABLE_KTLS: Final = 0x8

# host flags
HOSTFLAG_ALWAYS_CHECK_SUBJECT: Final = 0x1
HOSTFLAG_NEVER_CHECK_SUBJECT: Final = 0x20
HOSTFLAG_NO_WILDCARDS: Final = 0x2
HOSTFLAG_NO_PARTIAL_WILDCARDS: Final = 0x4
HOSTFLAG_MULTI_LABEL_WILDCARDS: Final = 0x8
HOSTFLAG_SINGLE_LABEL_SUBDOMAINS: Final = 0x10

# certificate file types
ENCODING_PEM: Final = 1
ENCODING_DER: Final = 2

# protocol versions
PROTO_MINIMUM_SUPPORTED: Final = -2
PROTO_MAXIMUM_SUPPORTED: Final = -1
PROTO_SSLv3: Final[int]
PROTO_TLSv1: Final[int]
PROTO_TLSv1_1: Final[int]
PROTO_TLSv1_2: Final[int]
PROTO_TLSv1_3: Final[int]

# feature support
HAS_SNI: Final[bool]
HAS_TLS_UNIQUE: Final[bool]
HAS_ECDH: Final[bool]
HAS_NPN: Final[bool]
if sys.version_info >= (3, 13):
    HAS_PSK: Final[bool]
if sys.version_info >= (3, 15):
    HAS_PSK_TLS13: Final[bool]
HAS_ALPN: Final[bool]
HAS_SSLv2: Final[bool]
HAS_SSLv3: Final[bool]
HAS_TLSv1: Final[bool]
HAS_TLSv1_1: Final[bool]
HAS_TLSv1_2: Final[bool]
HAS_TLSv1_3: Final[bool]
if sys.version_info >= (3, 14):
    HAS_PHA: Final[bool]

# version info
OPENSSL_VERSION_NUMBER: Final[int]
OPENSSL_VERSION_INFO: Final[tuple[int, int, int, int, int]]
OPENSSL_VERSION: Final[str]
_OPENSSL_API_VERSION: Final[tuple[int, int, int, int, int]]
