import base64
import os
import traceback
import zlib
from datetime import datetime

from flask import request
from werkzeug.utils import secure_filename

from scripts.config import app_configuration
from scripts.config.app_constants import CustomerProjects as Cuspro
from scripts.config.app_constants import DBMapping as Dbm
from scripts.config.app_constants import ProjectConfig
from scripts.config.db_connection_obj import ConnectionObj
from scripts.logging.logger import logger
from scripts.utils.mongo_utility import MongoConnect


class NewConfigUtility:
    def __init__(self):
        try:
            logger.debug("Inside the New Config Utility")
            self.mongo_obj = ConnectionObj.mongo_connection_obj
            if not ConnectionObj.mongo_connection_obj:
                self.mongo_obj = ConnectionObj.mongo_connection_obj = MongoConnect()
        except Exception as e:
            logger.exception("Exception in the New Config utility" + str(e))

    def fetch_tag(self, tag_type=None):
        """
        Definition for fetching the tag
        :param tag_type:
        :return:
        """
        tag_mapping = {}
        try:
            query = {}
            if tag_type:
                query["system_tag_type"] = tag_type
            for i in self.mongo_obj.search_record_by_query(Dbm.mongo_db_name, Dbm.tags, query, {"_id": 0}):
                try:
                    tag_mapping[i["id"]] = i["tag_name"]
                except Exception as e:
                    logger.exception(str(e))
        except Exception as e:
            logger.exception(str(e))
        return tag_mapping

    def get_site_level_hierarchy(self, input_data):
        """
        Definition for giving the site level hierarchy
        :param input_data:
        :return:
        """
        final_list = []
        try:
            logger.debug("inside the site_level")
            project_data = self.mongo_obj.find_one(
                db_name=Dbm.mongo_db_name, collection_name=Dbm.customer_projects,
                query={Cuspro.customer_project_id: input_data["project_id"]}, search_json={"_id": 0})
            site_template_id = project_data[Cuspro.site_templt_id]
            site_template_data = self.mongo_obj.find_one(
                db_name=Dbm.mongo_db_name, collection_name=Dbm.constants, query={"type": "site_template"},
                search_json={"_id": 0})
            key_list = list()
            for each_template in site_template_data["data"]:
                if each_template["site_templt_id"] == site_template_id:
                    key_list = each_template["key_list"]
                    break
            if "type" in input_data and input_data["type"] != "":
                user_id = self.get_usr_id()
                access_site = self.mongo_obj.distinct_query(Dbm.mongo_db_name, Dbm.user, "AccessLevel.sites.parent_id",
                                                            {"user_id": user_id})
                site_conf_data = self.mongo_obj.search_record_by_query2(
                    db_name=Dbm.mongo_db_name, collection_name=Dbm.site_conf,
                    query_json={"customer_project_id": input_data["project_id"], "site_id": {"$in": access_site}})
            else:
                site_conf_data = self.mongo_obj.search_record_by_query2(
                    db_name=Dbm.mongo_db_name, collection_name=Dbm.site_conf,
                    query_json={"customer_project_id": input_data["project_id"]})
            for site_json in site_conf_data:
                main_key = site_json["site_id"]
                main_value = site_json['site_name']
                mapp_dict = {}
                for i in range(0, len(key_list)):
                    try:
                        res = key_list[:i]
                        res += [key_list[i]]
                        if len(res) == 1:
                            final_list.append({"id": main_key, "name": main_value,
                                              "node_id": main_key, "site_id": main_key, "type": key_list[i]})
                        else:
                            try:
                                for each_obj in site_json[key_list[i]]:
                                    try:
                                        temp_list = []
                                        temp_value_list = []
                                        node_id = ""
                                        for each_element in res:
                                            try:
                                                if each_element == "site":
                                                    temp_list.append(main_key)
                                                    temp_value_list.append(main_value)
                                                else:
                                                    if key_list[i] == each_element:
                                                        mapp_dict[each_obj[each_element + "_id"]] = \
                                                            each_obj[each_element + "_name"]
                                                        node_id = each_obj[each_element + "_id"]
                                                    if each_obj[each_element + "_id"] in mapp_dict:
                                                        temp_list.append(each_obj[each_element + "_id"])
                                                        temp_value_list.append(mapp_dict[each_obj[
                                                            each_element + "_id"]])
                                            except Exception as e:
                                                logger.exception(str(e))
                                        final_list.append({"id": "$".join(temp_list), "name": ">".join(temp_value_list),
                                                          "node_id": node_id, "type": key_list[i], "site_id": main_key})
                                    except Exception as e:
                                        logger.exception(str(e))
                            except Exception as e:
                                logger.exception(str(e))
                    except Exception as e:
                        logger.exception(str(e))
        except Exception as e:
            logger.exception(str(e))
            traceback.print_exc()
        return final_list

    def get_site_hierarchy(self, input_data):
        """
        Utility definition to get the site hierarchy
        :return:
        """
        project_data = self.mongo_obj.find_one(
            db_name=Dbm.mongo_db_name, collection_name=Dbm.customer_projects,
            query={Cuspro.customer_project_id: input_data["project_id"]}, search_json={"_id": 0})
        site_template_id = project_data[Cuspro.site_templt_id]
        site_template_data = self.mongo_obj.find_one(
            db_name=Dbm.mongo_db_name, collection_name=Dbm.constants,
            query={"type": "site_template"}, search_json={"_id": 0})
        template_list = list()
        for each_template in site_template_data["data"]:
            if each_template["site_templt_id"] == site_template_id:
                template_list += each_template["key_list"]
        if "type" in input_data and input_data["type"] != "":
            user_id = self.get_usr_id()
            access_site = self.mongo_obj.distinct_query(Dbm.mongo_db_name, Dbm.user, "AccessLevel.sites.parent_id",
                                                        {"user_id": user_id})
            site_conf_data = self.mongo_obj.search_record_by_query2(
                db_name=Dbm.mongo_db_name, collection_name=Dbm.site_conf,
                query_json={"customer_project_id": input_data["project_id"], "site_id": {"$in": access_site}})
        else:
            site_conf_data = self.mongo_obj.search_record_by_query2(
                db_name=Dbm.mongo_db_name, collection_name=Dbm.site_conf,
                query_json={"customer_project_id": input_data["project_id"]})
        site_hierarchy_data = []
        for each_conf in site_conf_data:
            json_to_append = dict()
            mapping = self.get_template_mapping(site_conf_data=site_conf_data, template_list=template_list)
            # keys_list = list(mapping.keys())
            json_to_append["name"] = each_conf["site_name"]
            json_to_append["id"] = each_conf["site_id"]
            json_to_append["parent_id"] = each_conf["site_id"]
            json_to_append["type"] = "site"
            json_to_append["node_id"] = each_conf["site_id"]
            if each_conf["site_id"] in mapping and len(mapping[each_conf["site_id"]]):
                json_to_append["children"] = self.get_children(mapping, each_conf["site_id"])
            site_hierarchy_data.append(json_to_append)
        return site_hierarchy_data

    def get_children(self, mapping, template_id):
        if template_id in mapping.keys():
            list_of_json = list()
            for each_template in mapping[template_id]:
                template = list(each_template.keys())[0].split("_")[0]
                json_to_add = {"name": each_template[template + "_name"], "id": each_template[template + "_id"],
                               "node_id": each_template[template + "_id"], "parent_id": template_id,
                               "type": template}
                if each_template[template + "_id"] in mapping and len(mapping[each_template[template + "_id"]]) != 0:
                    json_to_add["children"] = self.get_children(mapping, each_template[template + "_id"])

                list_of_json.append(json_to_add)
            return list_of_json

    @staticmethod
    def get_template_mapping(site_conf_data, template_list):
        """
        :param site_conf_data:
        :param template_list:
        :return:
        """
        mapping_dict = dict()
        for each_site_conf in site_conf_data:
            try:
                for i in range(len(template_list)):
                    try:
                        if i == 0:
                            mapping_dict[each_site_conf[template_list[i] + "_id"]] = list()
                            if template_list[i + 1] in each_site_conf:
                                for each_template in each_site_conf[template_list[i + 1]]:
                                    try:
                                        mapping_dict[each_site_conf[template_list[i] + "_id"]].append(
                                            {template_list[i + 1] + "_id": each_template[template_list[i + 1] + "_id"],
                                             template_list[i + 1] + "_name": each_template[template_list[i + 1] +
                                                                                           "_name"]})
                                    except Exception as e:
                                        logger.exception(str(e))
                        elif template_list[i] in each_site_conf.keys():
                            if i + 1 < len(template_list) and len(each_site_conf[template_list[i]]) != 0 and \
                                    template_list[i + 1] in each_site_conf.keys():
                                for each_template in each_site_conf[template_list[i + 1]]:
                                    try:
                                        if each_template[template_list[i] + "_id"] not in mapping_dict.keys() and \
                                                each_template[template_list[i] + "_id"] is not None:
                                            mapping_dict[each_template[template_list[i] + "_id"]] = list()
                                        if each_template[template_list[i] + "_id"] is not None:
                                            mapping_dict[each_template[template_list[i] + "_id"]].append(
                                                {template_list[i + 1] + "_id": each_template[template_list[i + 1] +
                                                                                             "_id"],
                                                 template_list[i + 1] + "_name": each_template[template_list[i + 1] +
                                                                                               "_name"]})
                                    except Exception as e:
                                        logger.exception(str(e))
                    except Exception as e:
                        logger.exception(str(e))
            except Exception as e:
                logger.exception(str(e))
        return mapping_dict

    @staticmethod
    def get_usr_id(input_data=None):
        """
        Definition for getting the user id
        :param input_data:
        :return:
        """
        user_id = None
        try:
            if "user_id" in request.cookies:
                user_id = request.cookies.get("user_id")
                return user_id
            else:
                if "user_id" in input_data and input_data["user_id"] != "":
                    user_id = input_data["user_id"]
                    return user_id
                else:
                    return user_id
        except Exception as e:
            logger.exception(str(e))
        return user_id

    def get_usr_name(self, input_data=None):
        """
        Definition for getting the user id
        :param input_data:
        :return:
        """
        user_name = None
        try:
            if "user_id" in request.cookies:
                user_id = request.cookies.get("user_id")
                user_name = self.mongo_obj.find_one(Dbm.mongo_db_name, Dbm.user, {"user_id": user_id})["username"]
                return user_name
            else:
                if "user_id" in input_data and input_data["user_id"] != "":
                    user_id = input_data["user_id"]
                    user_name = self.mongo_obj.find_one(Dbm.mongo_db_name, Dbm.user, {"user_id": user_id})["username"]
                    return user_name
                else:
                    return user_name
        except Exception as e:
            logger.exception(str(e))
        return user_name

    def get_update_details(self):
        """
        Definition for getting the updated details
        :return:
        """
        json_to_return = {"status": "failed", "message": "", "data": []}
        try:
            logger.debug("Inside the get update details definition")
            client_id = ""
            user_id = self.get_usr_id()
            last_updated = datetime.now().strftime(ProjectConfig.date_format)
            json_to_return['data'].append(
                {"client_id": client_id, "last_updated_by": user_id, "last_updated": last_updated}, )
            json_to_return['status'] = "success"
        except Exception as e:
            logger.exception("Exception -> %s" % str(e))
        return json_to_return

    @staticmethod
    def upload_image_file(image_str, file_name, project_id):
        """
        Definition for uploading the image in the sections as well as product
        :param image_str:
        :param file_name:
        :param project_id
        :return:
        """
        nfile_name = None
        try:
            if "/" in file_name:
                file_name = file_name.split("/")[-1]
            file_name = str(file_name).strip().replace(" ", "_")
            nfile_name = str(project_id) + "_" + file_name
            if not os.path.isdir(app_configuration.IMAGE_PATH):
                os.makedirs(app_configuration.IMAGE_PATH)
            output_file = open(app_configuration.IMAGE_PATH + nfile_name, 'wb')
            a = base64.b64decode(str(image_str))
            output_file.write(a)
            output_file.close()
        except Exception as es:
            logger.exception(str(es))
        return nfile_name

    @staticmethod
    def upload_profile_image_file(image_str, file_name, user_id):
        """
        Definition for uploading the image in the sections as well as product
        :param image_str:
        :param file_name:
        :param user_id
        :return:
        """
        nfile_name = None
        try:
            if "/" in file_name:
                file_name = file_name.split("/")[-1]
            file_name = str(file_name).strip().replace(" ", "_")
            nfile_name = str(user_id) + "_" + file_name
            if not os.path.isdir(app_configuration.IMAGE_PATH):
                os.makedirs(app_configuration.IMAGE_PATH)
            output_file = open(app_configuration.IMAGE_PATH + nfile_name, 'wb')
            a = base64.b64decode(str(image_str))
            output_file.write(a)
            output_file.close()
        except Exception as es:
            logger.exception(str(es))
        return nfile_name

    def image_file_upload(self, image_str, process_id, image_path=None):
        """
        Definition for uploading the image in the sections as well as product
        :param image_str:
        :param process_id:
        :return: filename
        """
        file_name = None
        try:
            file_name = secure_filename(process_id) + ".jpg"
            if image_path:
                image_directory = app_configuration.IMAGE_PATH + image_path + "/"
            else:
                image_directory = app_configuration.IMAGE_PATH
            if not os.path.isdir(image_directory):
                os.makedirs(image_directory)
            output_file = open(image_directory + file_name, 'wb')
            img = base64.b64decode(str(image_str))
            output_file.write(img)
            output_file.close()
        except Exception as es:
            logger.exception(str(es))
        return file_name

    @staticmethod
    def save_image_to_mongo(image_str):
        try:
            img = image_str.encode('utf-8')
            # img = base64.encode(image_str)
            compressed_image = zlib.compress(img)
            check_sum = zlib.crc32(img)
            return dict(img=compressed_image, chksm=check_sum)
        except Exception as e:
            logger.exception(str(e))
            return dict(img=str(), chksm=str())

    @staticmethod
    def get_customer_logo(input_data):
        try:
            image_encoded = input_data.get("img")
            img_str = zlib.decompress(image_encoded)
            if not image_encoded:
                raise FileNotFoundError
            # image_decoded = base64.b64encode(img_str)
            return img_str
        except Exception as e:
            logger.exception(e)
            return str()

    def write_customer_logo(self, input_data):
        try:
            img_str = self.mongo_obj.find_one(db_name=Dbm.mongo_db_name,
                                              collection_name=Dbm.customer_logos,
                                              query={"project_id": input_data["project_id"]})
            image_directory = app_configuration.IMAGE_PATH + input_data["type"]
            with open(f"{image_directory}", "wb") as img:
                img.write(base64.b64decode(zlib.decompress(img_str.get("img"))))
            return True
        except Exception as e:
            logger.exception(e)
            return False