import json
import pkg_resources

from flask import Blueprint, jsonify, Response, current_app
from lxml import etree
import redis

from inventory_provider import juniper
from inventory_provider.routes import common

routes = Blueprint("inventory-data-query-routes", __name__)

API_VERSION = '0.1'


@routes.route("/version", methods=['GET', 'POST'])
@common.require_accepts_json
def version():
    return Response(
        json.dumps({
            'api': API_VERSION,
            'module':
                pkg_resources.get_distribution('inventory_provider').version
        }),
        mimetype="application/json"
    )


@routes.route("/routers", methods=['GET', 'POST'])
@common.require_accepts_json
def routers():
    redis_config = current_app.config["INVENTORY_PROVIDER_CONFIG"]["redis"]
    r = redis.StrictRedis(
        host=redis_config["hostname"],
        port=redis_config["port"])
    return Response(
        json.dumps(list([k.decode("utf-8") for k in r.keys("*")])),
        mimetype="application/json")


@routes.route("/interfaces/<hostname>", methods=['GET', 'POST'])
@common.require_accepts_json
def router_interfaces(hostname):
    redis_config = current_app.config["INVENTORY_PROVIDER_CONFIG"]["redis"]
    r = redis.StrictRedis(
        host=redis_config["hostname"],
        port=redis_config["port"])

    netconf_string = r.hget(hostname, 'netconf')
    if not netconf_string:
        return Response(
            response="no available info for '%s'" % hostname,
            status=404,
            mimetype="text/html")

    interfaces = list(juniper.list_interfaces(
        etree.XML(netconf_string.decode('utf-8'))))
    if not interfaces:
        return Response(
            response="no interfaces found for '%s'" % hostname,
            status=404,
            mimetype="text/html")

    return Response(
        json.dumps(interfaces),
        mimetype="application/json")


@routes.route("/snmp/<hostname>", methods=['GET', 'POST'])
@common.require_accepts_json
def snmp_ids(hostname):
    redis_config = current_app.config["INVENTORY_PROVIDER_CONFIG"]["redis"]
    r = redis.StrictRedis(
        host=redis_config["hostname"],
        port=redis_config["port"])
    ifc_data_string = r.hget(hostname, 'snmp-interfaces')
    if not ifc_data_string:
        return Response(
            response="no available info for '%s'" % hostname,
            status=404,
            mimetype="text/html")

    def _ifc_name(ifc):
        if 'v4InterfaceName' in ifc:
            return ifc['v4InterfaceName']
        if 'v6InterfaceName' in ifc:
            return ifc['v6InterfaceName']
        assert False, "sanity failure: no interface name found"

    ifc_data = json.loads(ifc_data_string.decode('utf-8'))
    result = [
        {'index': i['index'], 'name': _ifc_name(i)}
        for i in ifc_data]
    return Response(
        json.dumps(result),
        mimetype="application/json")


@routes.route("/debug-dump/<hostname>", methods=['GET', 'POST'])
@common.require_accepts_json
def debug_dump_router_info(hostname):
    redis_config = current_app.config["INVENTORY_PROVIDER_CONFIG"]["redis"]
    r = redis.StrictRedis(
        host=redis_config["hostname"],
        port=redis_config["port"])

    info = dict([(k.decode('utf-8'), json.loads(v.decode('utf-8')))
                 for k, v in r.hgetall(hostname).items()])
    if not info:
        return Response(
            response="no info found for router '%s'" % hostname,
            status=404,
            mimetype="text/html")

    return Response(
        json.dumps(info),
        mimetype="application/json")


@routes.route("/bgp/<hostname>", methods=['GET', 'POST'])
@common.require_accepts_json
def bgp_configs(hostname):
    redis_config = current_app.config["INVENTORY_PROVIDER_CONFIG"]["redis"]
    r = redis.StrictRedis(
        host=redis_config["hostname"],
        port=redis_config["port"])

    netconf_string = r.hget(hostname, 'netconf')
    if not netconf_string:
        return Response(
            response="no available info for '%s'" % hostname,
            status=404,
            mimetype="text/html")

    routes = list(juniper.list_bgp_routes(
        etree.XML(netconf_string.decode('utf-8'))))
    if not routes:
        return Response(
            response="no interfaces found for '%s'" % hostname,
            status=404,
            mimetype="text/html")

    return Response(
        json.dumps(routes),
        mimetype="application/json")


@routes.route("/interfaces/status/<hostname>/<path:interface>",
              methods=['GET', 'POST'])
@common.require_accepts_json
def interface_statuses(hostname, interface):
    r = common.get_redis()
    result = r.hget("interface_statuses",
                    "{}::{}".format(hostname, interface))
    if not result:
        return Response(
            response="no available info for {} {}".format(hostname, interface),
            status=404,
            mimetype="text/html")
    return jsonify({"status": result.decode('utf-8')})


@routes.route("/services/<hostname>/<path:interface>",
              methods=['GET', 'POST'])
@common.require_accepts_json
def services_for_interface(hostname, interface):
    r = common.get_redis()
    result = r.hget("interface_services",
                    "{}::{}".format(hostname, interface))
    if not result:
        return Response(
            response="no available info for {} {}".format(hostname, interface),
            status=404,
            mimetype="text/html")
    return jsonify(json.loads(result.decode('utf-8')))