import os

import pymongo
import requests
from config import Config
from db.db import MongoDatabse
from flask import jsonify, current_app
from utils.utils import to_snake_case, convert_dict_keys_to_snake_case, validate_snake_case_keys, get_nested_value, convert_value_to_original_type, convert_nested_values


class HelperOperations:

    def __init__(self):
        self.db = MongoDatabse()
        self.mimetype = 'application/json'
        self.config = Config().get_config()

    def get_invokers(self, uuid, invoker_id, page, page_size, sort_order):
        current_app.logger.debug(f"Getting the invokers")
        invoker_col = self.db.get_col_by_name(self.db.invoker_col)

        total_invokers = invoker_col.count_documents({})

        filter = {}
        if uuid:
            filter["uuid"]=uuid
        if invoker_id:
            filter["api_invoker_id"]=invoker_id
        
        sort_direction = pymongo.DESCENDING if sort_order == "desc" else pymongo.ASCENDING

        if page_size and page:
            index = (page - 1) * page_size
            documents = invoker_col.find(filter,{"_id":0}).sort("onboarding_date", sort_direction).skip(index).limit(page_size)
            pages = (total_invokers + page_size - 1) // page_size
        else:
            documents = invoker_col.find(filter,{"_id":0}).sort("onboarding_date", sort_direction)
            pages = 1

        list_invokers= list(documents)
        long = len(list_invokers)
        
        return jsonify(message="Invokers returned successfully", 
                        invokers=list_invokers,
                        total = total_invokers,
                        long = long,
                        totalPages = pages,
                        sortOrder = sort_order), 200
    

    def get_providers(self, uuid, provider_id, page, page_size, sort_order):
        current_app.logger.debug(f"Getting the providers")
        provider_col = self.db.get_col_by_name(self.db.provider_col)

        total_providers = provider_col.count_documents({})

        filter = {}
        if uuid:
            filter["uuid"]=uuid
        if provider_id:
            filter["api_prov_dom_id"]=provider_id
        
        sort_direction = pymongo.DESCENDING if sort_order == "desc" else pymongo.ASCENDING

        if page_size and page:
            index = (page - 1) * page_size
            documents = provider_col.find(filter,{"_id":0}).sort("onboarding_date", sort_direction).skip(index).limit(page_size)
            pages = (total_providers + page_size - 1) // page_size
        else:
            documents = provider_col.find(filter,{"_id":0}).sort("onboarding_date", sort_direction)
            pages = 1

        list_providers = list(documents)
        long = len(list_providers)
        
        return jsonify(message="Providers returned successfully", 
                        providers=list_providers,
                        total = total_providers,
                        long = long,
                        totalPages = pages,
                        sortOrder = sort_order), 200
    
    def get_services(self, service_id, apf_id, api_name, page, page_size, sort_order):
        current_app.logger.debug(f"Getting the services")
        service_col = self.db.get_col_by_name(self.db.services_col)

        total_services = service_col.count_documents({})

        filter = {}
        if service_id:
            filter["api_id"]=service_id
        if apf_id:
            filter["apf_id"]=apf_id
        if api_name:
            filter["api_name"]=api_name
        
        sort_direction = pymongo.DESCENDING if sort_order == "desc" else pymongo.ASCENDING

        if page_size and page:
            index = (page - 1) * page_size
            documents = service_col.find(filter,{"_id":0}).sort("onboarding_date", sort_direction).skip(index).limit(page_size)
            pages = (total_services + page_size - 1) // page_size
        else:
            documents = service_col.find(filter,{"_id":0}).sort("onboarding_date", sort_direction)
            pages = 1

        list_services= list(documents)
        long = len(list_services)
        
        return jsonify(message="Services returned successfully", 
                        services=list_services,
                        total = total_services,
                        long = long,
                        totalPages = pages,
                        sortOrder = sort_order), 200
    
    def get_security(self, invoker_id,  page, page_size):
        current_app.logger.debug(f"Getting the security context")
        security_col = self.db.get_col_by_name(self.db.security_context_col)

        total_security = security_col.count_documents({})

        filter = {}

        if invoker_id:
            filter["api_invoker_id"]=invoker_id

        if page_size and page:
            index = (page - 1) * page_size
            documents = security_col.find(filter,{"_id":0}).skip(index).limit(page_size)
            pages = (total_security + page_size - 1) // page_size
        else:
            documents = security_col.find(filter,{"_id":0})
            pages = 1

        list_security= list(documents)
        long = len(list_security)
        
        return jsonify(message="Security context returned successfully", 
                        security=list_security,
                        total = total_security,
                        long = long,
                        totalPages = pages), 200
    
    def get_events(self, subscriber_id, subscription_id,  page, page_size):
        current_app.logger.debug(f"Getting the events")
        events_col = self.db.get_col_by_name(self.db.events)

        total_events = events_col.count_documents({})

        filter = {}

        if subscriber_id:
            filter["subscriber_id"]=subscriber_id
        if subscription_id:
            filter["subscription_id"]=subscription_id

        if page_size and page:
            index = (page - 1) * page_size
            documents = events_col.find(filter,{"_id":0}).skip(index).limit(page_size)
            pages = (total_events + page_size - 1) // page_size
        else:
            documents = events_col.find(filter,{"_id":0})
            pages = 1

        list_events= list(documents)
        long = len(list_events)
        
        return jsonify(message="Events returned successfully", 
                        events=list_events,
                        total = total_events,
                        long = long,
                        totalPages = pages), 200
    
    def remove_entities(self, uuid):

        current_app.logger.debug(f"Removing entities for uuid: {uuid}")
        invoker_col = self.db.get_col_by_name(self.db.invoker_col)
        provider_col = self.db.get_col_by_name(self.db.provider_col)

        try:
            if invoker_col.count_documents({'uuid':uuid}) == 0 and provider_col.count_documents({'uuid':uuid}) == 0:
                current_app.logger.debug(f"No entities found for uuid: {uuid}")
                return jsonify(message=f"No entities found for uuid: {uuid}"), 204
            
            for invoker in invoker_col.find({'uuid':uuid}, {"_id":0}):
                current_app.logger.debug(f"Removing Invoker: {invoker["api_invoker_id"]}")
                url = 'https://{}/api-invoker-management/v1/onboardedInvokers/{}'.format(os.getenv('CAPIF_HOSTNAME'), invoker["api_invoker_id"])
                requests.request("DELETE", url, cert=(
                            '/usr/src/app/helper_service/certs/superadmin.crt', '/usr/src/app/helper_service/certs/superadmin.key'), verify='/usr/src/app/helper_service/certs/ca_root.crt')

            for provider in provider_col.find({'uuid':uuid}, {"_id":0}):
                current_app.logger.debug(f"Removing Provider: {provider["api_prov_dom_id"]}")
                url = 'https://{}/api-provider-management/v1/registrations/{}'.format(os.getenv('CAPIF_HOSTNAME'), provider["api_prov_dom_id"])

                requests.request("DELETE", url, cert=(
                                '/usr/src/app/helper_service/certs/superadmin.crt', '/usr/src/app/helper_service/certs/superadmin.key'), verify='/usr/src/app/helper_service/certs/ca_root.crt')
        except Exception as e:
            current_app.logger.debug(f"Error deleting user entities: {e}")
            jsonify(message=f"Error deleting user entities: {e}"), 500
        
        current_app.logger.debug(f"User entities removed successfully")
        return jsonify(message="User entities removed successfully"), 200

    def get_configuration(self):
        """Get all current settings."""
        current_app.logger.debug("Retrieving current CAPIF configuration")
        config_col = self.db.get_col_by_name(self.db.capif_configuration)
        config = config_col.find_one({}, {"_id": 0})

        if not config:
            return jsonify(message="No CAPIF configuration found"), 404

        return jsonify(config), 200
    

    def update_config_param(self, param_path, new_value):
        """
        Updates a single parameter in the configuration.
        param_path: Path of the parameter (e.g., settings.acl_policy_settings.allowed_total_invocations)
        """
        current_app.logger.debug(f"Updating configuration parameter: {param_path} with value: {new_value}")

        config_col = self.db.get_col_by_name(self.db.capif_configuration)

        existing_config = config_col.find_one({}, {"_id": 0})
        current_value = get_nested_value(existing_config, param_path)

        if current_value is None:
            return jsonify(message=f"The parameter '{param_path}' does not exist in the configuration"), 404

        converted_value = convert_value_to_original_type(new_value, current_value)

        if isinstance(converted_value, tuple):
            return converted_value

        update_query = {"$set": {param_path: converted_value}}
        result = config_col.update_one({}, update_query)

        if result.modified_count == 0:
            return jsonify(message=f"No configuration found or parameter '{param_path}' not updated"), 404

        return jsonify(message=f"Parameter '{param_path}' updated successfully"), 200
    
    
    def replace_configuration(self, new_config):
        """
        Replace all current settings with a new one.
        """
        current_app.logger.debug("Replacing entire CAPIF configuration")

        error_response = validate_snake_case_keys(new_config)
        if error_response:
            return error_response

        config_col = self.db.get_col_by_name(self.db.capif_configuration)
        existing_config = config_col.find_one({}, {"_id": 0})

        if existing_config:
            new_config = convert_nested_values(new_config, existing_config)

        result = config_col.replace_one({}, new_config, upsert=True)

        if result.matched_count == 0:
            return jsonify(message="No existing configuration found; a new one was created"), 201

        return jsonify(message="Configuration replaced successfully"), 200



    def add_new_configuration(self, category_name, category_values):
        """
        Add a new category of parameters in 'settings'.
        """
        current_app.logger.debug(f"Adding new category: {category_name} with values: {category_values}")

        config_col = self.db.get_col_by_name(self.db.capif_configuration)

        category_name_snake = to_snake_case(category_name)
        category_values_snake = convert_dict_keys_to_snake_case(category_values)

        update_query = {"$set": {f"settings.{category_name_snake}": category_values_snake}}

        result = config_col.update_one({}, update_query)

        if result.modified_count == 0:
            return jsonify(message=f"No configuration found or category '{category_name_snake}' not added"), 404

        return jsonify(message=f"Category '{category_name_snake}' added successfully"), 200


    def add_new_config_setting(self, param_path, new_value):
        """Add a new parameter in 'settings'."""
        current_app.logger.debug(f"Adding new configuration setting: {param_path} with value: {new_value}")
        config_col = self.db.get_col_by_name(self.db.capif_configuration)

        param_path_snake = ".".join(to_snake_case(part) for part in param_path.split("."))

        update_query = {"$set": {f"settings.{param_path_snake}": new_value}}
        result = config_col.update_one({}, update_query)

        if result.modified_count == 0:
            return jsonify(message=f"No configuration found or parameter '{param_path_snake}' not updated"), 404

        return jsonify(message=f"Parameter '{param_path_snake}' added successfully"), 200


    def remove_config_param(self, param_path):
        """Removes a specific parameter inside 'settings'."""
        current_app.logger.debug(f"Removing configuration parameter: {param_path}")

        config_col = self.db.get_col_by_name(self.db.capif_configuration)

        param_path_snake = ".".join(to_snake_case(part) for part in param_path.split("."))

        update_query = {"$unset": {f"settings.{param_path_snake}": ""}}

        result = config_col.update_one({}, update_query)

        if result.modified_count == 0:
            return jsonify(message=f"No configuration found or parameter '{param_path_snake}' not removed"), 404

        return jsonify(message=f"Parameter '{param_path_snake}' removed successfully"), 200


    def remove_config_category(self, category_name):
        """Removes an entire category inside 'settings'."""
        current_app.logger.debug(f"Removing configuration category: {category_name}")

        config_col = self.db.get_col_by_name(self.db.capif_configuration)

        category_name_snake = to_snake_case(category_name)

        update_query = {"$unset": {f"settings.{category_name_snake}": ""}}

        result = config_col.update_one({}, update_query)

        if result.modified_count == 0:
            return jsonify(message=f"No configuration found or category '{category_name_snake}' not removed"), 404

        return jsonify(message=f"Category '{category_name_snake}' removed successfully"), 200
