
from __future__ import absolute_import

import functools
import json
import logging
import os

from contextlib import closing
from six.moves import http_client

try:
    from ovirt_imageio_daemon import uhttp
except ImportError:
    uhttp = None

from vdsm import constants
from vdsm.storage import exception as se

DAEMON_SOCK = os.path.join(constants.P_VDSM_RUN, "ovirt-imageio-daemon.sock")

log = logging.getLogger('storage.imagetickets')


def requires_image_daemon(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        if not uhttp:
            raise se.ImageDaemonUnsupported()
        return func(*args, **kw)

    return wrapper


@requires_image_daemon
def add_ticket(ticket):
    body = json.dumps(ticket)
    request(uhttp.PUT, ticket["uuid"], body)


@requires_image_daemon
def get_ticket(ticket_id):
    return request(uhttp.GET, ticket_id)


@requires_image_daemon
def extend_ticket(uuid, timeout):
    body = json.dumps({"timeout": timeout})
    request(uhttp.PATCH, uuid, body)


@requires_image_daemon
def remove_ticket(uuid):
    request(uhttp.DELETE, uuid)


def request(method, uuid, body=None):
    log.debug("Sending request method=%r, ticket=%r, body=%r",
              method, uuid, body)
    if body is not None:
        body = body.encode("utf8")
    con = uhttp.UnixHTTPConnection(DAEMON_SOCK)
    with closing(con):
        try:
            con.request(method, "/tickets/%s" % uuid, body=body)
            res = con.getresponse()
        except (http_client.HTTPException, EnvironmentError) as e:
            raise se.ImageTicketsError("Error communicating with "
                                       "ovirt-imageio-daemon: "
                                       "{error}".format(error=e))

        content = _read_content(res)
        if res.status >= 300:
            raise se.ImageDaemonError(res.status, res.reason, content)
        return content


def _read_content(response):
    try:
        res_data = response.read()
    except EnvironmentError as e:
        error_info = {"explanation": "Error reading response",
                      "detail": str(e)}
        raise se.ImageDaemonError(response.status, response.reason, error_info)

    if not res_data:
        return {}

    try:
        return json.loads(res_data.decode("utf8"))
    except ValueError as e:
        error_info = {"explanation": "Invalid JSON", "detail": str(e)}
        raise se.ImageDaemonError(response.status, response.reason, error_info)
