from future.backports.http import cookiejar as http_cookiejar
from future.backports.http.cookiejar import http2time
from future.backports.urllib.parse import parse_qs
from future.backports.urllib.parse import urlsplit
from future.backports.urllib.parse import urlunsplit

import logging

from six import string_types

from oic.exception import UnSupported
from oic.oauth2.exception import TimeFormatError
from oic.utils.sanitize import sanitize

logger = logging.getLogger(__name__)

__author__ = 'roland'

URL_ENCODED = 'application/x-www-form-urlencoded'
JSON_ENCODED = "application/json"

DEFAULT_POST_CONTENT_TYPE = URL_ENCODED

PAIRS = {
    "port": "port_specified",
    "domain": "domain_specified",
    "path": "path_specified"
}

ATTRS = {"version": None,
         "name": "",
         "value": None,
         "port": None,
         "port_specified": False,
         "domain": "",
         "domain_specified": False,
         "domain_initial_dot": False,
         "path": "",
         "path_specified": False,
         "secure": False,
         "expires": None,
         "discard": True,
         "comment": None,
         "comment_url": None,
         "rest": "",
         "rfc2109": True}


def get_or_post(uri, method, req, content_type=DEFAULT_POST_CONTENT_TYPE,
                accept=None, **kwargs):
    """

    :param uri:
    :param method:
    :param req:
    :param content_type:
    :param accept:
    :param kwargs:
    :return:
    """
    if method in ["GET", "DELETE"]:
        if req.keys():
            _req = req.copy()
            comp = urlsplit(str(uri))
            if comp.query:
                _req.update(parse_qs(comp.query))

            _query = str(_req.to_urlencoded())
            path = urlunsplit((comp.scheme, comp.netloc, comp.path,
                               _query, comp.fragment))
        else:
            path = uri
        body = None
    elif method in ["POST", "PUT"]:
        path = uri
        if content_type == URL_ENCODED:
            body = req.to_urlencoded()
        elif content_type == JSON_ENCODED:
            body = req.to_json()
        else:
            raise UnSupported(
                "Unsupported content type: '%s'" % content_type)

        header_ext = {"Content-Type": content_type}
        if accept:
            header_ext = {"Accept": accept}

        if "headers" in kwargs.keys():
            kwargs["headers"].update(header_ext)
        else:
            kwargs["headers"] = header_ext
    else:
        raise UnSupported("Unsupported HTTP method: '%s'" % method)

    return path, body, kwargs


def set_cookie(cookiejar, kaka):
    """PLaces a cookie (a http_cookielib.Cookie based on a set-cookie header
    line) in the cookie jar.
    Always chose the shortest expires time.

    :param cookiejar:
    :param kaka: Cookie
    """

    # default rfc2109=False
    # max-age, httponly

    for cookie_name, morsel in kaka.items():
        std_attr = ATTRS.copy()
        std_attr["name"] = cookie_name
        _tmp = morsel.coded_value
        if _tmp.startswith('"') and _tmp.endswith('"'):
            std_attr["value"] = _tmp[1:-1]
        else:
            std_attr["value"] = _tmp

        std_attr["version"] = 0
        attr = ""
        # copy attributes that have values
        try:
            for attr in morsel.keys():
                if attr in ATTRS:
                    if morsel[attr]:
                        if attr == "expires":
                            std_attr[attr] = http2time(morsel[attr])
                        else:
                            std_attr[attr] = morsel[attr]
                elif attr == "max-age":
                    if morsel[attr]:
                        std_attr["expires"] = http2time(morsel[attr])
        except TimeFormatError:
            # Ignore cookie
            logger.info(
                "Time format error on %s parameter in received cookie" % (
                    sanitize(attr),))
            continue

        for att, spec in PAIRS.items():
            if std_attr[att]:
                std_attr[spec] = True

        if std_attr["domain"] and std_attr["domain"].startswith("."):
            std_attr["domain_initial_dot"] = True

        if morsel["max-age"] == 0:
            try:
                cookiejar.clear(domain=std_attr["domain"],
                                path=std_attr["path"],
                                name=std_attr["name"])
            except ValueError:
                pass
        else:
            # Fix for Microsoft cookie error
            if "version" in std_attr:
                try:
                    std_attr["version"] = std_attr["version"].split(",")[0]
                except (TypeError, AttributeError):
                    pass

            new_cookie = http_cookiejar.Cookie(**std_attr)

            cookiejar.set_cookie(new_cookie)


def match_to_(val, vlist):
    if isinstance(vlist, string_types):
        if vlist.startswith(val):
            return True
    else:
        for v in vlist:
            if v.startswith(val):
                return True
    return False


def verify_header(reqresp, body_type):
    logger.debug("resp.headers: %s" % (sanitize(reqresp.headers),))
    logger.debug("resp.txt: %s" % (sanitize(reqresp.text),))

    if body_type == "":
        _ctype = reqresp.headers["content-type"]
        if match_to_("application/json", _ctype):
            body_type = 'json'
        elif match_to_("application/jwt", _ctype):
            body_type = "jwt"
        elif match_to_(URL_ENCODED, _ctype):
            body_type = 'urlencoded'
        else:
            body_type = 'txt'  # reasonable default ??
    elif body_type == "json":
        if not match_to_("application/json", reqresp.headers["content-type"]):
            if match_to_("application/jwt", reqresp.headers["content-type"]):
                body_type = "jwt"
            else:
                raise ValueError("content-type: %s" % (reqresp.headers["content-type"],))
    elif body_type == "jwt":
        if not match_to_("application/jwt", reqresp.headers["content-type"]):
            raise ValueError("Wrong content-type in header, got: {} expected "
                             "'application/jwt'".format(reqresp.headers["content-type"]))
    elif body_type == "urlencoded":
        if not match_to_(DEFAULT_POST_CONTENT_TYPE, reqresp.headers["content-type"]):
            if not match_to_("text/plain", reqresp.headers["content-type"]):
                raise ValueError('Wrong content-type')
    else:
        raise ValueError("Unknown return format: %s" % body_type)

    return body_type
