import json
import umsgpack
from apy.container import Parameters


class Request:
    def __init__(self, method, path=None,
                 query=None, data=None, files=None, headers=None,
                 host='', protocol='http', remote_ip=None, version=None):
        """
        @type method: str
        @type path: str or None
        @type query: dict or None
        @type data: dict or None
        @type files: list or None (a list with dicts that contains "content" and "name")
        @type headers: dict or None
        @type host: str
        @type protocol: str
        @type remote_ip: str or None
        @type version: str or None
        """
        self.path = path.strip('/') if path else None
        if not self.path:
            self.path = '/'

        self.method = method.upper()

        self.query = Parameters(query if query else {})
        self.data = Parameters(data if data else {})
        self.files = files
        self.headers = Parameters(headers if headers else {})

        self.host = host
        self.protocol = protocol
        self.remote_ip = remote_ip
        self.version = version


class Response:
    # See http://www.iana.org/assignments/http-status-codes
    REASON_PHRASES = {
        100: 'Continue',
        101: 'Switching Protocols',
        102: 'Processing',  # RFC2518
        200: 'OK',
        201: 'Created',
        202: 'Accepted',
        203: 'Non-Authoritative Information',
        204: 'No Content',
        205: 'Reset Content',
        206: 'Partial Content',
        207: 'Multi-Status',  # RFC4918
        208: 'Already Reported',  # RFC5842
        226: 'IM Used', # RFC3229
        300: 'Multiple Choices',
        301: 'Moved Permanently',
        302: 'Found',
        303: 'See Other',
        304: 'Not Modified',
        305: 'Use Proxy',
        306: 'Reserved',
        307: 'Temporary Redirect',
        308: 'Permanent Redirect',  # RFC-reschke-http-status-308-07
        400: 'Bad Request',
        401: 'Unauthorized',
        402: 'Payment Required',
        403: 'Forbidden',
        404: 'Not Found',
        405: 'Method Not Allowed',
        406: 'Not Acceptable',
        407: 'Proxy Authentication Required',
        408: 'Request Timeout',
        409: 'Conflict',
        410: 'Gone',
        411: 'Length Required',
        412: 'Precondition Failed',
        413: 'Request Entity Too Large',
        414: 'Request-URI Too Long',
        415: 'Unsupported Media Type',
        416: 'Requested Range Not Satisfiable',
        417: 'Expectation Failed',
        418: 'I\'m a teapot',  # RFC2324
        422: 'Unprocessable Entity',  # RFC4918
        423: 'Locked',  # RFC4918
        424: 'Failed Dependency',  # RFC4918
        425: 'Reserved for WebDAV advanced collections expired proposal',  # RFC2817
        426: 'Upgrade Required',  # RFC2817
        428: 'Precondition Required',  # RFC6585
        429: 'Too Many Requests',  # RFC6585
        431: 'Request Header Fields Too Large',  # RFC6585
        500: 'Internal Server Error',
        501: 'Not Implemented',
        502: 'Bad Gateway',
        503: 'Service Unavailable',
        504: 'Gateway Timeout',
        505: 'HTTP Version Not Supported',
        506: 'Variant Also Negotiates (Experimental)',  # RFC2295
        507: 'Insufficient Storage',  # RFC4918
        508: 'Loop Detected',  # RFC5842
        510: 'Not Extended',  # RFC2774
        511: 'Network Authentication Required',  # RFC6585
    }

    def __init__(self, data='', status_code=200, status_text=None, headers = None):
        self._status_code = status_code
        self._status_text = status_text
        self.data = data
        self.headers = headers if headers is not None else Parameters()

    def set_status(self, code=None, text=None):
        """
        @type code: int
        @type text: str
        """
        if code:
            if code < 100 or code > 599:
                raise Exception('Invalid status code: %i' % code)
            self._status_code = code
        self._status_text = text if text else self.REASON_PHRASES[code] if code in self.REASON_PHRASES else ''
        return self

    def get_status_code(self):
        return self._status_code

    def get_status_text(self):
        return self._status_text

    def get_content(self):
        return self.data


class JsonResponse(Response):
    def get_content(self):
        return json.dumps(self.data)


class MsgpackResponse(Response):
    def get_content(self):
        return umsgpack.packb(self.data)
