import datetime
import traceback
from scripts.config import app_constants
from scripts.config.app_constants import DBMapping
from scripts.utils.ilens_metadata_util import CommonUtils
from scripts.utils.get_new_id import GetNewId
from scripts.logging.logger import logger
from scripts.utils.mongo_utility import MongoConnect
from scripts.config.db_connection_obj import ConnectionObj


cmn_utils_obj = CommonUtils()
property_category_collection = app_constants.DBMapping.property_category
property_collection = app_constants.DBMapping.property
db_ilens_hmi = app_constants.DBMapping.ilens_hmi_db


class PropertyCategoryHandler(object):
    def __init__(self):
        if ConnectionObj.mongo_connection_obj is None:
            ConnectionObj.mongo_connection_obj = MongoConnect()
        self.common_utils_obj = CommonUtils()

    def fetch_property_category(self, input_json):
        try:
            final_json = {"status": "success", "data": {}}
            if "property_category_id" in input_json:
                validation_status = self.validate_property_category_id(input_json["property_category_id"])
                if validation_status["status"] == "success":
                    property_category_record = ConnectionObj.mongo_connection_obj.\
                        find_one(db_name=db_ilens_hmi, collection_name=DBMapping.property_category,
                                 query={"property_category_id": input_json["property_category_id"]},
                                 search_json={"_id": 0,
                                              "property_category_id": 1,
                                              "property_category_name": 1,
                                              "property_category_icon": 1,
                                              "description": 1,
                                              "property_list": 1})
                    final_json["data"] = property_category_record
                else:
                    return {"status": "failed",
                            "message": "no property category exists with the given property_category_id",
                            "data": {}}
            else:
                return {"status": "failed", "message": "property_category_id is missing in input"}
            return final_json
        except Exception as e:
            logger.exception("Exception while fetching property category" + str(e))
            traceback.print_exc()
            return {"status": "failed", "message": "unable to fetch property category"}

    def save_property_category(self, input_json):
        """

        :param input_json: input json from UI
        :return:
        """
        try:
            if "edit" in input_json and input_json["edit"] is True:
                field_list = app_constants.RequiredFields.property_category_edit
                # doing field and space validation for the keys which is having value of type "str" only
                required_filed_validator_status = self.common_utils_obj.field_validator_with_empty_space(field_list, input_json)
                if required_filed_validator_status is not None:
                    return {"status": "failed", "message": required_filed_validator_status + " key is missing",
                            "data": {}}
                if not self.duplicate_name_check(input_json=input_json, edit=True):
                    return {"status": "failed", "message": "property category name already exists"}
                return_message = self.update_property_category_and_return_message(input_json=input_json)
                return return_message
            response_to_check_user_id_in_cookies = self.common_utils_obj.get_user_id(input_json)
            if response_to_check_user_id_in_cookies["status"] == "success":
                user_id = response_to_check_user_id_in_cookies["user_id"]
            else:
                return {"status": "failed", "message": "user not authorized"}
            # response_to_check_client_id_in_cookies = self.data_security_obj.get_client_id(input_json)
            # if response_to_check_client_id_in_cookies["status"] == "success":
            #     client_id = response_to_check_client_id_in_cookies["client_id"]
            # else:
            #     return {"status": "failed", "message": "user not authorized"}
            field_list = app_constants.RequiredFields.property_category_create
            required_filed_validator_status = self.common_utils_obj.field_validator_with_empty_space(field_list, input_json)
            if required_filed_validator_status is not None:
                return {"status": "failed", "message": required_filed_validator_status + " is missing",
                        "data": {}}
            if not self.duplicate_name_check(input_json=input_json):
                return {"status": "failed", "message": "property category name already exists"}
            new_id = "property_category_" + GetNewId.get_next_id("property_category")
            input_json["property_category_id"] = new_id
            user_name = self.common_utils_obj.get_user_name_for_user_id(user_id=user_id)
            input_json["property_category_name"] = input_json["property_category_name"].strip(" ")
            input_json["created_by"] = user_id
            input_json["updated_by"] = user_name
            input_json["created_on"] = input_json["updated_on"] = datetime.datetime.now().strftime("%d %b, %Y %I:%M %p")
            # input_json["client_id"] = client_id
            input_json["is_deleted"] = "false"
            ConnectionObj.mongo_connection_obj.database_insertion(db_name=db_ilens_hmi,
                                                                  collection_name=DBMapping.property_category,
                                                                  query_json=input_json)
            return {"status": "success", "message": "property category created successfully",
                    "data": {"property_category_name": input_json["property_category_name"],
                             "property_category_id": new_id}}
        except Exception as e:
            logger.exception("Exception" + str(e))
            traceback.print_exc()
            return {"status": "failed", "message": "unable to create property category"}

    def list_property_category(self, input_json):
        """
        this service will fetch list of property category based on the user requirement
        :param input_json: which will consist of following keys ["columns","order","start","length"] and etc.,
        :return:
        """
        try:
            field_list = app_constants.RequiredFields.tag_category_list
            required_filed_validator_status = self.common_utils_obj.field_validator_with_empty_space(field_list, input_json)
            if required_filed_validator_status is not None:
                return {"status": "failed", "data": [], "message": required_filed_validator_status + " key is missing"}
            list_of_tuples_to_sort_while_querying_mongo = self.fetch_column_order(
                columns=input_json["columns"], order=input_json["order"])
            if list_of_tuples_to_sort_while_querying_mongo is None:
                return {"status": "failed", "data": [],
                        "message": "failed to list property categories as order is not proper"}
            final_json = {"status": "success", "data": [], "message": "property category list fetched successfully",
                          "records_filtered": 0, "records_total": 0}
            query_json = self.form_query_json(input_json=input_json)
            fetched_records = ConnectionObj.mongo_connection_obj.find_with_sort_and_skip(
                db_name=db_ilens_hmi,
                collection_name=DBMapping.property_category,
                query_json=query_json,
                search_option={"_id": 0, "property_category_name": 1, "property_category_id": 1, "description": 1,
                               "updated_by": 1, "updated_on": 1, "property_category_icon": 1},
                sort_json=list_of_tuples_to_sort_while_querying_mongo,
                skip=int(input_json["start"]),
                limit=int(input_json["length"])
            )
            final_json["data"] = fetched_records
            final_json["records_total"] = ConnectionObj.mongo_connection_obj.\
                find_record_count(db_name=db_ilens_hmi, collection_name=DBMapping.property_category,
                                  query_json={"is_deleted": "false"})
            if len(query_json["$and"]) > 1:
                final_json["records_filtered"] = len(fetched_records)
            else:
                final_json["records_filtered"] = final_json["records_total"]
            return final_json
        except Exception as e:
            logger.exception("Exception" + str(e))
            traceback.print_exc()
            return {"status": "failed", "message": "unable to list property types"}

    @staticmethod
    def delete_property_category(input_json):
        """
        this service will delete the property based on property_type_id
        :param input_json: property_type_id
        :return:
        """
        try:
            field_list = app_constants.RequiredFields.property_category_delete
            required_filed_validator_status = cmn_utils_obj.field_validator_with_empty_space(field_list, input_json)
            if required_filed_validator_status is not None:
                return {"status": "failed", "data": [], "message": required_filed_validator_status + " key is missing"}
            if ConnectionObj.mongo_connection_obj.update_one(db_name=db_ilens_hmi,
                                                             collection_name=DBMapping.property_category,
                                                             query={"property_category_id": input_json[
                                                                 "property_category_id"]},
                                                             set_json={"is_deleted": "true"}):
                return {"status": "success", "message": "property category deleted successfully"}
            else:
                return {"status": "failed",
                        "message": "no property category exists with the given property_category_id"}
        except Exception as e:
            logger.exception("Exception" + str(e))
            traceback.print_exc()
            return {"status": "failed", "message": "unable to delete property category"}

    @staticmethod
    def fetch_column_order(columns, order):
        """
        it will form json to sort the records while querying mongo to fetch property type list
        :param columns: list of dict where each dict corresponds to each column and it's fields
        :param order: list of single dict like [{"column": 0,"dir": "asc"}]
        :return: {column_name:1} or {column_name:-1} based on the "dir" in the "order"
        """
        try:
            column_name_to_sort = columns[int(order[0]["column"])]["name"]
            if order[0]["dir"] == "asc":
                return_list = [(column_name_to_sort, 1)]
            elif order[0]["dir"] == "desc":
                return_list = [(column_name_to_sort, -1)]
            else:
                return_list = [(column_name_to_sort, 1)]
            return return_list
        except Exception as e:
            logger.exception("failed to fetch column order:" + str(e))
            return None

    def update_property_category_and_return_message(self, input_json):
        try:
            input_json["property_category_name"] = input_json["property_category_name"].strip(" ")
            query_json = {"property_category_id": input_json["property_category_id"]}
            response_to_check_user_id_in_cookies = self.common_utils_obj.get_user_id(input_json)
            if response_to_check_user_id_in_cookies["status"] == "success":
                user_id = response_to_check_user_id_in_cookies["user_id"]
            else:
                user_id = ""
            user_name = self.common_utils_obj.get_user_name_for_user_id(user_id=user_id)
            update_json = {
                "property_category_name": input_json["property_category_name"],
                "property_list": input_json["property_list"],
                "updated_on": datetime.datetime.now().strftime("%d %b, %Y %I:%M %p"),
                "updated_by": user_name,
                "description": input_json["description"],
                "property_category_icon": input_json["property_category_icon"]
            }
            validation_status = self.validate_property_category_id(input_json["property_category_id"])
            if validation_status["status"] == "success":
                ConnectionObj.mongo_connection_obj.update_one(db_name=db_ilens_hmi,
                                                              collection_name=DBMapping.property_category,
                                                              query=query_json,
                                                              set_json=update_json,
                                                              upsert=True)
                return {"status": "success", "message": "property type updated successfully",
                        "data": {"property_category_name": input_json["property_category_name"],
                                 "property_category_id": input_json["property_category_id"]}}
            else:
                return {"status": "failed",
                        "message": "no property type exists with the given property_type_id",
                        "data": {}}
        except Exception as e:
            return {"status": "failed",
                    "message": "no property type exists with the given property_type_id:" + str(e),
                    "data": {}}

    @staticmethod
    def validate_property_category_id(property_category_id):
        try:
            query_json = {"property_category_id": property_category_id}
            if ConnectionObj.mongo_connection_obj.find_record_count(db_name=db_ilens_hmi,
                                                                    collection_name=DBMapping.property_category,
                                                                    query_json=query_json,
                                                                    ) > 0:
                return {"status": "success"}
            else:
                return {"status": "failed"}
        except Exception as e:
            logger.exception("exception while validating the property category id:" + str(e))
            return {"status": "failed"}

    @staticmethod
    def form_query_json(input_json):
        query_json = {"$and": []}
        query_json["$and"].append({"is_deleted": "false"})
        try:
            search_columns = input_json["column_search"]
            for each_column_name, search_value in search_columns.items():
                if search_value.strip(" ") != "":
                    query_json["$and"].append({
                        each_column_name: {"$regex": search_value.strip(" "), "$options": "i"}
                    })
        except Exception as e:
            logger.exception("exception while forming query json:" + str(e))
            return query_json
        return query_json

    @staticmethod
    def duplicate_name_check(input_json, edit=False):
        """

        :param input_json:
        :param edit:
        :return:
        """
        try:
            if edit:
                query = {"property_category_name": input_json["property_category_name"],
                         "property_category_id": {"$nin": [input_json["property_category_id"]]},
                         "is_deleted": "false"}
            else:
                query = {"property_category_name": input_json["property_category_name"],
                         "is_deleted": "false"}
            record_count = ConnectionObj.mongo_connection_obj.\
                find_record_count(db_name=db_ilens_hmi, collection_name=DBMapping.property_category, query_json=query)
            if record_count > 0:
                return False
            else:
                return True
        except Exception as e:
            logger.exception("exception while validating the duplicate name check:" + str(e))
            return False
