from __future__ import annotations

import socket
import typing
from enum import IntEnum


class HTTPStatus(IntEnum):
    """HTTP status codes and reason phrases

    Status codes from the following RFCs are all observed:

        * RFC 7231: Hypertext Transfer Protocol (HTTP/1.1), obsoletes 2616
        * RFC 6585: Additional HTTP Status Codes
        * RFC 3229: Delta encoding in HTTP
        * RFC 4918: HTTP Extensions for WebDAV, obsoletes 2518
        * RFC 5842: Binding Extensions to WebDAV
        * RFC 7238: Permanent Redirect
        * RFC 2295: Transparent Content Negotiation in HTTP
        * RFC 2774: An HTTP Extension Framework
        * RFC 7725: An HTTP Status Code to Report Legal Obstacles
        * RFC 7540: Hypertext Transfer Protocol Version 2 (HTTP/2)
        * RFC 2324: Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0)
        * RFC 8297: An HTTP Status Code for Indicating Hints
        * RFC 8470: Using Early Data in HTTP
    """

    phrase: str
    description: str
    standard: bool

    def __new__(
        cls, value: int, phrase: str, description: str = "", is_standard: bool = True
    ) -> HTTPStatus:
        obj = int.__new__(cls, value)
        obj._value_ = value

        obj.phrase = phrase
        obj.description = description
        obj.standard = is_standard
        return obj

    # informational
    CONTINUE = 100, "Continue", "Request received, please continue"
    SWITCHING_PROTOCOLS = (
        101,
        "Switching Protocols",
        "Switching to new protocol; obey Upgrade header",
    )
    PROCESSING = 102, "Processing"
    EARLY_HINTS = 103, "Early Hints"

    # success
    OK = 200, "OK", "Request fulfilled, document follows"
    CREATED = 201, "Created", "Document created, URL follows"
    ACCEPTED = (202, "Accepted", "Request accepted, processing continues off-line")
    NON_AUTHORITATIVE_INFORMATION = (
        203,
        "Non-Authoritative Information",
        "Request fulfilled from cache",
    )
    NO_CONTENT = 204, "No Content", "Request fulfilled, nothing follows"
    RESET_CONTENT = 205, "Reset Content", "Clear input form for further input"
    PARTIAL_CONTENT = 206, "Partial Content", "Partial content follows"
    MULTI_STATUS = 207, "Multi-Status"
    ALREADY_REPORTED = 208, "Already Reported"
    IM_USED = 226, "IM Used"

    # redirection
    MULTIPLE_CHOICES = (
        300,
        "Multiple Choices",
        "Object has several resources -- see URI list",
    )
    MOVED_PERMANENTLY = (
        301,
        "Moved Permanently",
        "Object moved permanently -- see URI list",
    )
    FOUND = 302, "Found", "Object moved temporarily -- see URI list"
    SEE_OTHER = 303, "See Other", "Object moved -- see Method and URL list"
    NOT_MODIFIED = (304, "Not Modified", "Document has not changed since given time")
    USE_PROXY = (
        305,
        "Use Proxy",
        "You must use proxy specified in Location to access this resource",
    )
    TEMPORARY_REDIRECT = (
        307,
        "Temporary Redirect",
        "Object moved temporarily -- see URI list",
    )
    PERMANENT_REDIRECT = (
        308,
        "Permanent Redirect",
        "Object moved permanently -- see URI list",
    )

    # client error
    BAD_REQUEST = (400, "Bad Request", "Bad request syntax or unsupported method")
    UNAUTHORIZED = (401, "Unauthorized", "No permission -- see authorization schemes")
    PAYMENT_REQUIRED = (402, "Payment Required", "No payment -- see charging schemes")
    FORBIDDEN = (403, "Forbidden", "Request forbidden -- authorization will not help")
    NOT_FOUND = (404, "Not Found", "Nothing matches the given URI")
    METHOD_NOT_ALLOWED = (
        405,
        "Method Not Allowed",
        "Specified method is invalid for this resource",
    )
    NOT_ACCEPTABLE = (406, "Not Acceptable", "URI not available in preferred format")
    PROXY_AUTHENTICATION_REQUIRED = (
        407,
        "Proxy Authentication Required",
        "You must authenticate with this proxy before proceeding",
    )
    REQUEST_TIMEOUT = (408, "Request Timeout", "Request timed out; try again later")
    CONFLICT = 409, "Conflict", "Request conflict"
    GONE = (410, "Gone", "URI no longer exists and has been permanently removed")
    LENGTH_REQUIRED = (411, "Length Required", "Client must specify Content-Length")
    PRECONDITION_FAILED = (
        412,
        "Precondition Failed",
        "Precondition in headers is false",
    )
    REQUEST_ENTITY_TOO_LARGE = (413, "Request Entity Too Large", "Entity is too large")
    REQUEST_URI_TOO_LONG = (414, "Request-URI Too Long", "URI is too long")
    UNSUPPORTED_MEDIA_TYPE = (
        415,
        "Unsupported Media Type",
        "Entity body in unsupported format",
    )
    REQUESTED_RANGE_NOT_SATISFIABLE = (
        416,
        "Requested Range Not Satisfiable",
        "Cannot satisfy request range",
    )
    EXPECTATION_FAILED = (
        417,
        "Expectation Failed",
        "Expect condition could not be satisfied",
    )
    IM_A_TEAPOT = (
        418,
        "I'm a Teapot",
        "Server refuses to brew coffee because it is a teapot.",
    )
    MISDIRECTED_REQUEST = (
        421,
        "Misdirected Request",
        "Server is not able to produce a response",
    )
    UNPROCESSABLE_ENTITY = 422, "Unprocessable Entity"
    LOCKED = 423, "Locked"
    FAILED_DEPENDENCY = 424, "Failed Dependency"
    TOO_EARLY = 425, "Too Early"
    UPGRADE_REQUIRED = 426, "Upgrade Required"
    PRECONDITION_REQUIRED = (
        428,
        "Precondition Required",
        "The origin server requires the request to be conditional",
    )
    TOO_MANY_REQUESTS = (
        429,
        "Too Many Requests",
        "The user has sent too many requests in "
        'a given amount of time ("rate limiting")',
    )
    REQUEST_HEADER_FIELDS_TOO_LARGE = (
        431,
        "Request Header Fields Too Large",
        "The server is unwilling to process the request because its header "
        "fields are too large",
    )
    UNAVAILABLE_FOR_LEGAL_REASONS = (
        451,
        "Unavailable For Legal Reasons",
        "The server is denying access to the "
        "resource as a consequence of a legal demand",
    )

    # server errors
    INTERNAL_SERVER_ERROR = (
        500,
        "Internal Server Error",
        "Server got itself in trouble",
    )
    NOT_IMPLEMENTED = (501, "Not Implemented", "Server does not support this operation")
    BAD_GATEWAY = (502, "Bad Gateway", "Invalid responses from another server/proxy")
    SERVICE_UNAVAILABLE = (
        503,
        "Service Unavailable",
        "The server cannot process the request due to a high load",
    )
    GATEWAY_TIMEOUT = (
        504,
        "Gateway Timeout",
        "The gateway server did not receive a timely response",
    )
    HTTP_VERSION_NOT_SUPPORTED = (
        505,
        "HTTP Version Not Supported",
        "Cannot fulfill request",
    )
    VARIANT_ALSO_NEGOTIATES = 506, "Variant Also Negotiates"
    INSUFFICIENT_STORAGE = 507, "Insufficient Storage"
    LOOP_DETECTED = 508, "Loop Detected"
    NOT_EXTENDED = 510, "Not Extended"
    NETWORK_AUTHENTICATION_REQUIRED = (
        511,
        "Network Authentication Required",
        "The client needs to authenticate to gain network access",
    )


# another hack to maintain backwards compatibility
# Mapping status codes to official W3C names
responses: typing.Mapping[int, str] = {
    v: v.phrase for v in HTTPStatus.__members__.values()
}

# Default value for `blocksize` - a new parameter introduced to
# http.client.HTTPConnection & http.client.HTTPSConnection in Python 3.7
# The maximum TCP packet size is 65535 octets. But OSes can have a recv buffer greater than 65k, so
# passing the highest value does improve responsiveness.
# udp usually is set to 212992
# and tcp usually is set to 131072 (16384 * 8) or (65535 * 2)
try:
    # dynamically retrieve the kernel rcvbuf size.
    stream = socket.socket(type=socket.SOCK_STREAM)
    dgram = socket.socket(type=socket.SOCK_DGRAM)

    DEFAULT_BLOCKSIZE: int = stream.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
    UDP_DEFAULT_BLOCKSIZE: int = dgram.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)

    stream.close()
    dgram.close()
except OSError:
    DEFAULT_BLOCKSIZE = 131072
    UDP_DEFAULT_BLOCKSIZE = 212992

TCP_DEFAULT_BLOCKSIZE: int = DEFAULT_BLOCKSIZE

# Mozilla TLS recommendations for ciphers
# General-purpose servers with a variety of clients, recommended for almost all systems.
MOZ_INTERMEDIATE_CIPHERS: str = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305"

DEFAULT_BACKGROUND_WATCH_WINDOW: float = 5.0
MINIMAL_BACKGROUND_WATCH_WINDOW: float = 0.05

DEFAULT_KEEPALIVE_DELAY: float = 3600.0
DEFAULT_KEEPALIVE_IDLE_WINDOW: float = 60.0
MINIMAL_KEEPALIVE_IDLE_WINDOW: float = 1.0
