from typing import (
    Any, Dict, IO, Iterable, List, Iterator, Mapping, Optional,
    Protocol, Tuple, Type, TypeVar,
    Union,
    overload,
    BinaryIO,
)
import email.message
import io
from socket import socket
import sys
import ssl
import types

_DataType = Union[bytes, IO[Any], Iterable[bytes], str]
_T = TypeVar('_T')

HTTP_PORT = ...  # type: int
HTTPS_PORT = ...  # type: int

CONTINUE = ...  # type: int
SWITCHING_PROTOCOLS = ...  # type: int
PROCESSING = ...  # type: int

OK = ...  # type: int
CREATED = ...  # type: int
ACCEPTED = ...  # type: int
NON_AUTHORITATIVE_INFORMATION = ...  # type: int
NO_CONTENT = ...  # type: int
RESET_CONTENT = ...  # type: int
PARTIAL_CONTENT = ...  # type: int
MULTI_STATUS = ...  # type: int
IM_USED = ...  # type: int

MULTIPLE_CHOICES = ...  # type: int
MOVED_PERMANENTLY = ...  # type: int
FOUND = ...  # type: int
SEE_OTHER = ...  # type: int
NOT_MODIFIED = ...  # type: int
USE_PROXY = ...  # type: int
TEMPORARY_REDIRECT = ...  # type: int

BAD_REQUEST = ...  # type: int
UNAUTHORIZED = ...  # type: int
PAYMENT_REQUIRED = ...  # type: int
FORBIDDEN = ...  # type: int
NOT_FOUND = ...  # type: int
METHOD_NOT_ALLOWED = ...  # type: int
NOT_ACCEPTABLE = ...  # type: int
PROXY_AUTHENTICATION_REQUIRED = ...  # type: int
REQUEST_TIMEOUT = ...  # type: int
CONFLICT = ...  # type: int
GONE = ...  # type: int
LENGTH_REQUIRED = ...  # type: int
PRECONDITION_FAILED = ...  # type: int
REQUEST_ENTITY_TOO_LARGE = ...  # type: int
REQUEST_URI_TOO_LONG = ...  # type: int
UNSUPPORTED_MEDIA_TYPE = ...  # type: int
REQUESTED_RANGE_NOT_SATISFIABLE = ...  # type: int
EXPECTATION_FAILED = ...  # type: int
UNPROCESSABLE_ENTITY = ...  # type: int
LOCKED = ...  # type: int
FAILED_DEPENDENCY = ...  # type: int
UPGRADE_REQUIRED = ...  # type: int
PRECONDITION_REQUIRED = ...  # type: int
TOO_MANY_REQUESTS = ...  # type: int
REQUEST_HEADER_FIELDS_TOO_LARGE = ...  # type: int

INTERNAL_SERVER_ERROR = ...  # type: int
NOT_IMPLEMENTED = ...  # type: int
BAD_GATEWAY = ...  # type: int
SERVICE_UNAVAILABLE = ...  # type: int
GATEWAY_TIMEOUT = ...  # type: int
HTTP_VERSION_NOT_SUPPORTED = ...  # type: int
INSUFFICIENT_STORAGE = ...  # type: int
NOT_EXTENDED = ...  # type: int
NETWORK_AUTHENTICATION_REQUIRED = ...  # type: int

responses = ...  # type: Dict[int, str]

class HTTPMessage(email.message.Message): ...

if sys.version_info >= (3, 5):
    # Ignore errors to work around python/mypy#5027
    class HTTPResponse(io.BufferedIOBase, BinaryIO):  # type: ignore
        msg = ...  # type: HTTPMessage
        headers = ...  # type: HTTPMessage
        version = ...  # type: int
        debuglevel = ...  # type: int
        closed = ...  # type: bool
        status = ...  # type: int
        reason = ...  # type: str
        def __init__(self, sock: socket, debuglevel: int = ...,
                     method: Optional[str] = ..., url: Optional[str] = ...) -> None: ...
        def read(self, amt: Optional[int] = ...) -> bytes: ...
        @overload
        def getheader(self, name: str) -> Optional[str]: ...
        @overload
        def getheader(self, name: str, default: _T) -> Union[str, _T]: ...
        def getheaders(self) -> List[Tuple[str, str]]: ...
        def fileno(self) -> int: ...
        def isclosed(self) -> bool: ...
        def __iter__(self) -> Iterator[bytes]: ...
        def __enter__(self) -> HTTPResponse: ...
        def __exit__(self, exc_type: Optional[Type[BaseException]],
                     exc_val: Optional[BaseException],
                     exc_tb: Optional[types.TracebackType]) -> bool: ...
        def info(self) -> email.message.Message: ...
        def geturl(self) -> str: ...
        def getcode(self) -> int: ...
        def begin(self) -> None: ...
else:
    class HTTPResponse(io.RawIOBase, BinaryIO):  # type: ignore
        msg = ...  # type: HTTPMessage
        headers = ...  # type: HTTPMessage
        version = ...  # type: int
        debuglevel = ...  # type: int
        closed = ...  # type: bool
        status = ...  # type: int
        reason = ...  # type: str
        def read(self, amt: Optional[int] = ...) -> bytes: ...
        def readinto(self, b: bytearray) -> int: ...
        @overload
        def getheader(self, name: str) -> Optional[str]: ...
        @overload
        def getheader(self, name: str, default: _T) -> Union[str, _T]: ...
        def getheaders(self) -> List[Tuple[str, str]]: ...
        def fileno(self) -> int: ...
        def __iter__(self) -> Iterator[bytes]: ...
        def __enter__(self) -> HTTPResponse: ...
        def __exit__(self, exc_type: Optional[Type[BaseException]],
                     exc_val: Optional[BaseException],
                     exc_tb: Optional[types.TracebackType]) -> bool: ...
        def info(self) -> email.message.Message: ...
        def geturl(self) -> str: ...
        def getcode(self) -> int: ...
        def begin(self) -> None: ...

# This is an API stub only for the class below, not a class itself.
# urllib.request uses it for a parameter.
class HTTPConnectionProtocol(Protocol):
    if sys.version_info >= (3, 7):
        def __call__(self, host: str, port: Optional[int] = ...,
                     timeout: int = ...,
                     source_address: Optional[Tuple[str, int]] = ...,
                     blocksize: int = ...): ...
    else:
        def __call__(self, host: str, port: Optional[int] = ...,
                     timeout: int = ...,
                     source_address: Optional[Tuple[str, int]] = ...): ...

class HTTPConnection:
    host: str = ...
    port: int = ...
    if sys.version_info >= (3, 7):
        def __init__(
            self,
            host: str, port: Optional[int] = ...,
            timeout: int = ...,
            source_address: Optional[Tuple[str, int]] = ..., blocksize: int = ...
        ) -> None: ...
    else:
        def __init__(
            self,
            host: str, port: Optional[int] = ...,
            timeout: int = ...,
            source_address: Optional[Tuple[str, int]] = ...
        ) -> None: ...
    if sys.version_info >= (3, 6):
        def request(self, method: str, url: str,
                    body: Optional[_DataType] = ...,
                    headers: Mapping[str, str] = ...,
                    *, encode_chunked: bool = ...) -> None: ...
    else:
        def request(self, method: str, url: str,
                    body: Optional[_DataType] = ...,
                    headers: Mapping[str, str] = ...) -> None: ...
    def getresponse(self) -> HTTPResponse: ...
    def set_debuglevel(self, level: int) -> None: ...
    def set_tunnel(self, host: str, port: Optional[int] = ...,
                   headers: Optional[Mapping[str, str]] = ...) -> None: ...
    def connect(self) -> None: ...
    def close(self) -> None: ...
    def putrequest(self, request: str, selector: str, skip_host: bool = ...,
                   skip_accept_encoding: bool = ...) -> None: ...
    def putheader(self, header: str, *argument: str) -> None: ...
    if sys.version_info >= (3, 6):
        def endheaders(self, message_body: Optional[_DataType] = ...,
                       *, encode_chunked: bool = ...) -> None: ...
    else:
        def endheaders(self, message_body: Optional[_DataType] = ...) -> None: ...
    def send(self, data: _DataType) -> None: ...

class HTTPSConnection(HTTPConnection):
    def __init__(self,
                 host: str, port: Optional[int] = ...,
                 key_file: Optional[str] = ...,
                 cert_file: Optional[str] = ...,
                 timeout: int = ...,
                 source_address: Optional[Tuple[str, int]] = ...,
                 *, context: Optional[ssl.SSLContext] = ...,
                 check_hostname: Optional[bool] = ...) -> None: ...

class HTTPException(Exception): ...
error = HTTPException

class NotConnected(HTTPException): ...
class InvalidURL(HTTPException): ...
class UnknownProtocol(HTTPException): ...
class UnknownTransferEncoding(HTTPException): ...
class UnimplementedFileMode(HTTPException): ...
class IncompleteRead(HTTPException): ...

class ImproperConnectionState(HTTPException): ...
class CannotSendRequest(ImproperConnectionState): ...
class CannotSendHeader(ImproperConnectionState): ...
class ResponseNotReady(ImproperConnectionState): ...

class BadStatusLine(HTTPException): ...
class LineTooLong(HTTPException): ...

if sys.version_info >= (3, 5):
    class RemoteDisconnected(ConnectionResetError, BadStatusLine): ...
