import json
import os
import ssl
import sys
from dotenv import load_dotenv
import pika
import requests

class StopListening(Exception):
    pass


class RabbitBackend(object):

    def __init__(self, broker, user, passwd, ca=None, client_cert=None, client_key=None):
        host, port = broker.split(":")
        self.host = host
        self.port = int(port)
        self.user = user
        self.passwd = passwd
        self.credentials = pika.PlainCredentials(self.user, self.passwd)
        self.ca = ca
        self.client_cert = client_cert
        self.client_key = client_key

    def get_connection(self, vhost="/"):
        if self.ca:
            context = ssl.create_default_context(cafile=self.ca)
            context.verify_mode = ssl.CERT_REQUIRED
        else:
            context = ssl.create_default_context()
            context.check_hostname = False
            context.verify_mode = ssl.CERT_NONE
        if self.client_cert and self.client_key:
            context.load_cert_chain(certfile=self.client_cert, keyfile=self.client_key)
        return pika.BlockingConnection(pika.ConnectionParameters(
            self.host,
            self.port,
            vhost,
            self.credentials,
            ssl_options=pika.SSLOptions(context)
        ))


def request_stats(backend, sensor):
    with requests.Session() as session:
        stats_url = API_SERVER + f"/api/sensors/{sensor}/stats/"
        resp = session.get(
            stats_url
        )
    if resp.status_code == 200:
        print(resp.json())
    elif resp.status_code == 202:
        try:
            listen_notifications(backend)
        except StopListening:
            sys.exit(0)
    else:
        print(f"Response {resp.status_code}")

def notification_handler(ch, method, properties, body):
    try:
        data = json.loads(body)
        href = API_SERVER + f"/api/sensors/{sensor}/stats/"
        notification_for = data["sensor"]
    except (KeyError, json.JSONDecodeError) as e:
        print(e)
        raise StopListening
    else:
        if notification_for == sensor:
            with requests.Session() as session:
                resp = session.get(href)
            
            if resp.status_code == 200:
                print(resp.json())
            else:
                print(f"Response {resp.status_code}")
            raise StopListening

def listen_notifications(backend):
    connection = backend.get_connection(os.environ["PWP_RABBIT_VHOST"])
    channel = connection.channel()
    channel.exchange_declare(
        exchange="notifications",
        exchange_type="fanout"
    )
    result = channel.queue_declare(queue="", exclusive=True)
    channel.queue_bind(
        exchange="notifications",
        queue=result.method.queue
    )
    channel.basic_consume(
        queue=result.method.queue,
        on_message_callback=notification_handler,
        auto_ack=True
    )
    channel.start_consuming()
    

if __name__ == "__main__":
    try:
        load_dotenv(os.getenv(sys.argv[1]))
        sensor = sys.argv[2]
        rabbit = RabbitBackend(
            broker=os.environ["PWP_RABBIT_URI"],
            user=os.environ["PWP_RABBIT_USER"],
            passwd=os.environ["PWP_RABBIT_PASSWD"],
            ca=os.environ.get("CA_CERT"),
            client_cert=os.environ.get("PWP_RABBIT_CLIENT_CERT"),
            client_key=os.environ.get("PWP_RABBIT_CLIENT_KEY")
        )
        API_SERVER = os.environ["API_SERVER"]
    except IndexError:
        print("Dotenv file path and sensor handle must be supplied")
    except KeyError as e:
        print("Missing required configuration parameter: {e}")
    else:
        request_stats(rabbit, sensor)
