import pprint
import zulip
import sys
import re
import json
import httplib2
import os
import random
import numpy as np
import pickle
import json
import nltk
from tensorflow.keras.models import load_model
from nltk.stem import WordNetLemmatizer

p = pprint.PrettyPrinter()
BOT_MAIL = "covid-vaccine-bot@chirag-jain.zulipchat.com"

lemmatizer = WordNetLemmatizer()
nltk.download("punkt")
nltk.download("wordnet")

nlpModel = load_model("chatbot_model.h5")
intents = json.loads(open("intents.json").read())
words = pickle.load(open("words.pkl", "rb"))
classes = pickle.load(open("classes.pkl", "rb"))

def clean_up_sentence(sentence):
    sentence_words = nltk.word_tokenize(sentence)
    sentence_words = [lemmatizer.lemmatize(word.lower()) for word in sentence_words]
    return sentence_words

# return bag of words array: 0 or 1 for each word in the bag that exists in the sentence
def bow(sentence, words, show_details=True):
    # tokenize the pattern
    sentence_words = clean_up_sentence(sentence)
    # bag of words - matrix of N words, vocabulary matrix
    bag = [0] * len(words)
    for s in sentence_words:
        for i, w in enumerate(words):
            if w == s:
                # assign 1 if current word is in the vocabulary position
                bag[i] = 1
                if show_details:
                    print("found in bag: %s" % w)
    return np.array(bag)


def predict_class(sentence, model):
    # filter out predictions below a threshold
    p = bow(sentence, words, show_details=False)
    res = model.predict(np.array([p]))[0]
    ERROR_THRESHOLD = 0.25
    results = [[i, r] for i, r in enumerate(res) if r > ERROR_THRESHOLD]
    # sort by strength of probability
    results.sort(key=lambda x: x[1], reverse=True)
    return_list = []
    for r in results:
        return_list.append({"intent": classes[r[0]], "probability": str(r[1])})
    return return_list


def getResponse(ints, intents_json):
    tag = ints[0]["intent"]
    list_of_intents = intents_json["intents"]
    for i in list_of_intents:
        if i["tag"] == tag:
            result = random.choice(i["responses"])
            break
    return result

def get_bot_response(msg):
	# checks is a user has given a name, in order to give a personalized feedback
	if msg.startswith('my name is'):
		name = msg[11:]
		ints = predict_class(msg, nlpModel)
		res1 = getResponse(ints, intents)
		res =res1.replace("{n}",name)
	elif msg.startswith('hi my name is'):
		name = msg[14:]
		ints = predict_class(msg, nlpModel)
		res1 = getResponse(ints, intents)
		res =res1.replace("{n}",name)
	#if no name is passed execute normally
	else:
		ints = predict_class(msg, nlpModel)
		res = getResponse(ints, intents)
	return res

class ZulipBot(object):
	def __init__(self):
		self.client = zulip.Client(site="https://chirag-jain.zulipchat.com/api/")
		self.subscribe_all()

	def subscribe_all(self):
		json = self.client.get_streams()["streams"]
		streams = [{"name": stream["name"]} for stream in json]
		self.client.add_subscriptions(streams)

	def process(self, msg):
		content = msg["content"].split()
		sender_email = msg["sender_email"]
		ttype = msg["type"]
		stream_name = msg['display_recipient']
		stream_topic = msg['subject']

		print(content)

		if sender_email == BOT_MAIL:
			return 

		print("Sucessfully heard.")

		if content[0].lower() == "covidbot" or content[0] == "@**CovidBot**":
			ip = content[1:]
			ip = " ".join(ip)
			message = get_bot_response(ip) 
			print(message)
			self.client.send_message({
				"type": "stream",
				"subject": msg["subject"],
				"to": msg["display_recipient"],
				"content": message
			})


		elif "CovidBot" in content and content[0] != "CovidBot":
			self.client.send_message({
				"type": "stream",
				"subject": msg["subject"],
				"to": msg["display_recipient"],
				"content": "Hey there! :blush:"
				})
		else:
			return

def main():
	bot = ZulipBot()
	bot.client.call_on_each_message(bot.process)

if __name__ == "__main__":
	try:
		main()
	except KeyboardInterrupt:
		print("Thanks for using Covid Bot. Bye!")
		sys.exit(0)
