##############################################################################
# Copyright (c) 2016 HUAWEI TECHNOLOGIES CO.,LTD and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################

import httplib
import simplejson as json
import sys  # noqa:F401

from distutils.version import LooseVersion
from ansible import __version__ as __ansible_version__
from ansible.plugins.callback import CallbackBase

COMPASS_HOST = "compass-deck"


# def task_error(display, host, data):
#     display.display("task_error: host=%s,data=%s" % (host, data))
#
#     if isinstance(data, dict):
#         invocation = data.pop('invocation', {})
#
#     notify_host(display, COMPASS_HOST, host, "failed")


class CallbackModule(CallbackBase):
    """
    logs playbook results, per host, in /var/log/ansible/hosts
    """
    CALLBACK_VERSION = 2.0
    CALLBACK_TYPE = 'notification'
    CALLBACK_NAME = 'status_callback'
    CALLBACK_NEEDS_WHITELIST = True

    def __init__(self):
        super(CallbackModule, self).__init__()

    def v2_on_any(self, *args, **kwargs):
        pass

    def v2_runner_on_failed(self, res, ignore_errors=False):
        # task_error(self._display, host, res)
        pass

    def v2_runner_on_ok(self, res):
        pass

    def v2_runner_on_skipped(self, host, item=None):
        pass

    def v2_runner_on_unreachable(self, host, res):
        pass

    def v2_runner_on_no_hosts(self):
        pass

    def v2_runner_on_async_poll(self, host, res, jid, clock):
        pass

    def v2_runner_on_async_ok(self, host, res, jid):
        pass

    def v2_runner_on_async_failed(self, host, res, jid):
        # task_error(self._display, host, res)
        pass

    def v2_playbook_on_start(self):
        pass

    def v2_playbook_on_notify(self, host, handler):
        pass

    def v2_playbook_on_no_hosts_matched(self):
        pass

    def v2_playbook_on_no_hosts_remaining(self):
        pass

    def v2_playbook_on_task_start(self, name, is_conditional):
        pass

    def v2_playbook_on_vars_prompt(self, varname, private=True, prompt=None,
                                encrypt=None, confirm=False, salt_size=None, salt=None, default=None):   # noqa
        pass

    def v2_playbook_on_setup(self):
        pass

    def v2_playbook_on_import_for_host(self, host, imported_file):
        pass

    def v2_playbook_on_not_import_for_host(self, host, missing_file):
        pass

    def v2_playbook_on_play_start(self, play):
        self.play = play
        self.loader = self.play.get_loader()
        return

    def v2_playbook_on_stats(self, stats):
        self._display.display("playbook_on_stats enter")
        if LooseVersion(__ansible_version__) < LooseVersion("2.4"):
            all_vars = self.play.get_variable_manager().get_vars(self.loader)
        else:
            all_vars = self.play.get_variable_manager().get_vars()
        host_vars = all_vars["hostvars"]
        hosts = sorted(stats.processed.keys())
        cluster_name = host_vars[hosts[0]]['cluster_name']

        headers = {"Content-type": "application/json",
                   "Accept": "*/*"}
        conn = httplib.HTTPConnection(COMPASS_HOST, 80)
        token = auth(conn)
        headers["X-Auth-Token"] = token
        get_url = "/api/clusterhosts"
        conn.request("GET", get_url, "", headers)
        resp = conn.getresponse()
        raise_for_status(resp)
        clusterhost_data = json.loads(resp.read())
        clusterhost_mapping = {}
        for item in clusterhost_data:
            if item["clustername"] == cluster_name:
                clusterhost_mapping.update({item["hostname"]:
                                           item["clusterhost_id"]})

        force_error = False
        if "localhost" in hosts:
            summary = stats.summarize("localhost")
            if summary['failures'] > 0 or summary['unreachable'] > 0:
                force_error = True

        for hostname, hostid in clusterhost_mapping.iteritems():
            if hostname not in hosts:
                continue

            summary = stats.summarize(hostname)
            # self._display.display("host: %s \nsummary: %s\n" % (host, summary)) # noqa

            if summary['failures'] > 0 or summary['unreachable'] > 0 \
               or force_error:
                status = "error"
            else:
                status = "succ"
            self._display.display("hostname: %s" % hostname)
            notify_host(self._display, COMPASS_HOST, hostid, status)


def raise_for_status(resp):
    if resp.status < 200 or resp.status > 300:
        raise RuntimeError(
            "%s, %s, %s" %
            (resp.status, resp.reason, resp.read()))


def auth(conn):
    credential = {}
    credential['email'] = "admin@huawei.com"
    credential['password'] = "admin"
    url = "/api/users/token"
    headers = {"Content-type": "application/json",
               "Accept": "*/*"}
    conn.request("POST", url, json.dumps(credential), headers)
    resp = conn.getresponse()

    raise_for_status(resp)
    return json.loads(resp.read())["token"]


def notify_host(display, compass_host, hostid, status):
    url = "/api/clusterhosts/%s/state" % hostid
    if status == "succ":
        body = {"state": "SUCCESSFUL"}
    elif status == "error":
        body = {"state": "ERROR"}
    else:
        display.error("notify_host: hostid %s with status %s is not supported"
                      % (hostid, status))
        return

    headers = {"Content-type": "application/json",
               "Accept": "*/*"}

    conn = httplib.HTTPConnection(compass_host, 80)
    token = auth(conn)
    headers["X-Auth-Token"] = token
    display.display("host=%s,url=%s,body=%s,headers=%s" %
                    (compass_host, url, json.dumps(body), headers))
    conn.request("POST", url, json.dumps(body), headers)
    resp = conn.getresponse()
    try:
        raise_for_status(resp)
        display.display(
            "notify host status success!!! status=%s, body=%s" %
            (resp.status, resp.read()))
    except Exception as e:
        display.error("http request failed %s" % str(e))
        raise
    finally:
        conn.close()