
import warnings
from EmailService import EmailService
from keras.models import load_model
import pickle
import tensorflow as tf
import numpy as np
import os
from pathlib import Path
import datetime
import re
import win32com.client


class AutoMailClassifier:

    def __init__(self, input_mail, white_list, output_mail_list, categories, labels):
        self.input_mail = input_mail
        self.white_list = white_list
        self.output_mail_list = output_mail_list
        self.categories = categories
        self.mail_categorization = False
        self.labels = labels

        if white_list == []:
            warnings.warn("Is imperative to use a suitable whitelist with at least one valid email")
            exit(1)

        self.email_service = EmailService(self.input_mail, self.white_list, self.output_mail_list, self.categories, self.mail_categorization)

        if len(output_mail_list) == categories:
            self.mail_categorization = True
            self.email_service.mail_categorization = True

        self.classifier_file = "model.h5"
        self.tokenizer_file = "count_tokenizer.pk1"

        self.debug = False
        self.debug_path = Path.cwd() / "Log/AutoMailClassifier/LogAutoMailClassifier.txt"

        self.model = load_model(self.classifier_file)
        self.toc_filename = self.tokenizer_file
        self.tokenizer = pickle.load(open(self.toc_filename, 'rb'))

        self.limit_days = 1

        self.validate_emails()


    def validate_emails(self):
        regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b'
        print("Checking for valid email address... ")
        # Check mail
        if re.fullmatch(regex, self.input_mail):
            pass
        else:
            warnings.warn("FATAL ERROR: The email address set for the input mail = "+ self.input_mail + " is not valid")
            exit(1)
        # Check whitelist
        for item in self.white_list:
            if re.fullmatch(regex, item):
                pass
            else:
                warnings.warn("FATAL ERROR: The email address set for the white mail list = " + item + " is not valid")
                exit(1)
        # Check output list
        for item in self.output_mail_list:
            if re.fullmatch(regex, item):
                pass
            else:
                warnings.warn("FATAL ERROR: The email address set for the output mail list = " + item + " is not valid")
                exit(1)


    def reset_classifier(self):
        current_time = datetime.datetime.now().strftime("%Y/%d/%m_%H:%M:%S")
        self.tokenizer_file = "count_tokenizer.pk1"
        self.classifier_file = "model.h5"
        self.model = load_model(self.classifier_file)
        self.toc_filename = self.tokenizer_file
        self.tokenizer = pickle.load(open(self.toc_filename, 'rb'))
        if self.debug:
            f = open(self.debug_path, "a")
            f.write(current_time + " : " + "Classifier reset to default values\n")
            f.close()


    def set_classifier(self, classifier, tokenizer):
        current_time = datetime.datetime.now().strftime("%Y/%d/%m_%H:%M:%S")
        if os.path.isfile(classifier) and os.path.isfile(tokenizer):
            self.tokenizer_file = tokenizer
            self.classifier_file = classifier
            self.model = load_model(self.classifier_file)
            self.toc_filename = self.tokenizer_file
            self.tokenizer = pickle.load(open(self.toc_filename, 'rb'))
            if self.debug:
                f = open(self.debug_path, "a")
                f.write(
                    current_time + " : " + "Classifier set to = " + str(classifier) + " and tokenizer set to = " + str(tokenizer) + "\n")
                f.close()
        else:
            warnings.warn("Path set for classifier or tokenizer is not valid")
            if self.debug:
                f = open(self.debug_path, "a")
                f.write(current_time + " : " + "FATAL ERROR: Path set for classifier or tokenizer is not valid\n")
                f.close()
            exit(1)


    def reset_debug_files(self):
        if not os.path.isfile(self.debug_path):
            pass
        else:
            os.remove(self.debug_path)


    def set_processing_limit_days(self, days):
        current_time = datetime.datetime.now().strftime("%Y/%d/%m_%H:%M:%S")
        if days <= 30 and days >= 0:
            self.limit_days = days
            self.email_service.limit_days = days
            if self.debug:
                f = open(self.debug_path, "a")
                f.write(current_time + " : " + "Limit days to process set to = " + str(days) + " days\n")
                f.close()
        else:
            warnings.warn("Parameter days must be between 0 and 30")
            if self.debug:
                f = open(self.debug_path, "a")
                f.write(current_time + " : " + "ERROR: unable to set " + str(days) + " days for limit days - Parameter days must be between 0 and 30\n")
                f.close()


    def enable_disable_mail_categorization(self, enable):
        current_time = datetime.datetime.now().strftime("%Y/%d/%m_%H:%M:%S")
        if len(self.output_mail_list) == self.categories:
            self.mail_categorization = enable
            self.email_service.mail_categorization = enable
            if self.debug:
                f = open(self.debug_path, "a")
                f.write(current_time + " : " + "Mail Categorization enabled = " + str(enable) + "\n")
                f.close()
        else:
            warnings.warn("Number of categories and number of mailing addresses on output list not matching")
            if self.debug:
                f = open(self.debug_path, "a")
                f.write(current_time + " : " + "Mail Categorization cannot be enabled:Number of categories and number of mailing addresses on output list not matching\n")
                f.close()


    def enable_disable_log_debugging(self, enable):
        self.debug = enable
        current_time = datetime.datetime.now().strftime("%Y/%d/%m_%H:%M:%S")
        if not os.path.isfile(self.debug_path):
            target_folder = Path.cwd() / "Log/AutoMailClassifier"
            if not os.path.exists(target_folder):
                os.makedirs(target_folder)
                f = open(self.debug_path, "x")
                f.write(current_time + " : " + "Debug Logging enabled = " + str(enable) + "\n")
            else:
                f = open(self.debug_path, "w")
                f.write(current_time + " : " + "Debug Logging enabled = " + str(enable) + "\n")
        else:
            f = open(self.debug_path, "a")
            f.write(current_time + " : " + "Debug Logging enabled = " + str(enable) + "\n")
        f.close()
        self.email_service.enable_disable_log_debugging(enable)


    def try_service(self):
        current_time = datetime.datetime.now().strftime("%Y/%d/%m_%H:%M:%S")
        if self.debug:
            f = open(self.debug_path, "a")
            f.write(current_time + " : " + "Trying service...\n")
        text_list = self.email_service.get_messages()
        if text_list != []:
            count = 0
            for item in text_list:
                label = self.classify([item])
                if self.debug:
                    f.write(current_time + " : " + "Processing item number " + str(count) + " with label: " + str(label[0]) + "\n")
                if self.mail_categorization:
                    out = self.email_service.send_message(label[0])
                else:
                    out = self.email_service.send_message(self.labels[label[0]])
                if self.debug:
                    f.write(current_time + " : " + "Sent item: " + str(count) + " with label: " + str(label[0]) + " with outcome: " + str(out) + "\n")
                count += 1
        else:
            if self.debug:
                f.write(current_time + " : " + "Returned empty list, no input to process\n")
        if self.debug:
            f.close()


    def classify(self, text):
        current_time = datetime.datetime.now().strftime("%Y/%d/%m_%H:%M:%S")
        if self.debug:
            f = open(self.debug_path, "a")
            f.write(current_time + " : " + "Classifying...\n")
        self.tokenizer.fit_on_texts(text)
        text = self.tokenizer.texts_to_sequences(text)
        text = tf.keras.preprocessing.sequence.pad_sequences(text, maxlen=1000)
        y_pred = np.argmax(self.model.predict(text), axis=-1)
        if self.debug:
            f.write(current_time + " : " + "Output for classification not empty: " + str(y_pred) + "\n")
            f.close()
        return y_pred


    def start_service(self):
        current_time = datetime.datetime.now().strftime("%Y/%d/%m_%H:%M:%S")
        if self.debug:
            f = open(self.debug_path, "a")
            f.write(current_time + " : " + "Starting service...\n")
        while True:
            if self.debug:
                f = open(self.debug_path, "a")
                f.write(current_time + " : " + "Listening to input...\n")
            print("Listening to input...")
            text_list = self.email_service.get_messages()
            if text_list != []:
                count = 0
                for item in text_list:
                    label = self.classify([item])
                    if self.debug:
                        f.write(current_time + " : " + "Processing item number " + str(count) + " with label: " + str(label[0]) + "\n")
                    if self.mail_categorization:
                        out = self.email_service.send_message(label[0])
                    else:
                        out = self.email_service.send_message(self.labels[label[0]])
                    if self.debug:
                        f.write(current_time + " : " + "Sent item: " + str(count) + " with label: " + str(
                            label[0]) + " with outcome: " + str(out) + "\n")
                    count += 1
            else:
                if self.debug:
                    f.write(current_time + " : " + "Returned empty list, no input to process\n")
            if self.debug:
                f.close()



