import os
import secrets

import rfc3987
from capif_events.models.event_subscription import EventSubscription  # noqa: E501
from flask import current_app, Response

from .auth_manager import AuthManager
from .resources import Resource
from .responses import internal_server_error, not_found_error, make_response, bad_request_error
from ..util import serialize_clean_camel_case, clean_empty, dict_to_camel_case


class EventSubscriptionsOperations(Resource):

    def __check_subscriber_id(self, subscriber_id):
        mycol_invoker= self.db.get_col_by_name(self.db.invoker_collection)
        mycol_provider= self.db.get_col_by_name(self.db.provider_collection)

        current_app.logger.debug("Cheking subscriber id")

        invoker_query = {"api_invoker_id":subscriber_id}

        invoker = mycol_invoker.find_one(invoker_query)

        provider_query = {"api_prov_funcs.api_prov_func_id":subscriber_id}

        provider = mycol_provider.find_one(provider_query)

        if invoker is None and provider is None:
            current_app.logger.error("Not found invoker or provider with this subscriber id")
            return not_found_error(detail="Invoker or APF or AEF or AMF Not found", cause="Subscriber Not Found")

        return None
    
    def __check_event_filters(self, events, filters):
        current_app.logger.debug("Checking event filters.")
        valid_filters = {
            "SERVICE_API_UPDATE": ["api_ids"],
            "SERVICE_API_AVAILABLE" : ["api_ids"],
            "SERVICE_API_UNAVAILABLE" : ["api_ids"],
            "API_INVOKER_ONBOARDED": ["api_invoker_ids"],
            "API_INVOKER_OFFBOARDED": ["api_invoker_ids"],
            "API_INVOKER_UPDATED": ["api_invoker_ids"],
            "ACCESS_CONTROL_POLICY_UPDATE":["api_invoker_ids", "api_ids"],
            "SERVICE_API_INVOCATION_SUCCESS": ["api_invoker_ids", "api_ids", "aef_ids"],
            "SERVICE_API_INVOCATION_FAILURE": ["api_invoker_ids", "api_ids", "aef_ids"],
            "API_TOPOLOGY_HIDING_CREATED": [],
            "API_TOPOLOGY_HIDING_REVOKED": []
        }

        for event, filter in zip(events, filters):
            invalid_filters = set(filter.keys()) - set(valid_filters.get(event, []))

            if invalid_filters:
                 current_app.logger.debug(f"The eventFilter {invalid_filters} for event {event} are not applicable.")
                 return bad_request_error(detail="Bad Param", cause = f"Invalid eventFilter for event {event}", invalid_params=[{"param": "eventFilter", "reason": f"The eventFilter {invalid_filters} for event {event} are not applicable."}])
        return None

    def __init__(self):
        Resource.__init__(self)
        self.auth_manager = AuthManager()

    def create_event(self, subscriber_id, event_subscription):

        try:
            mycol = self.db.get_col_by_name(self.db.event_collection)

            current_app.logger.debug("Creating event")

            if rfc3987.match(event_subscription.notification_destination, rule="URI") is None:
                current_app.logger.error("Bad url format")
                return bad_request_error(detail="Bad Param", cause = "Detected Bad formar of param", invalid_params=[{"param": "notificationDestination", "reason": "Not valid URL format"}])

            if event_subscription.supported_features is None:
                return bad_request_error(
                    detail="supportedFeatures not present in request",
                    cause="supportedFeatures not present",
                    invalid_params=[{"param": "supportedFeatures", "reason": "not defined"}]
                )

            ## Verify that this subscriberID exist in publishers or invokers

            result = self.__check_subscriber_id(subscriber_id)

            if  isinstance(result, Response):

                return result
            
            # Check if EnhancedEventReport is enabled and validate event filters
            
            if EventSubscription.return_supp_feat_dict(event_subscription.supported_features)["EnhancedEventReport"]:
                if event_subscription.event_filters:
                    current_app.logger.debug(event_subscription.event_filters)
                    result = self.__check_event_filters(event_subscription.events, clean_empty(event_subscription.to_dict()["event_filters"]))
                    if isinstance(result, Response):
                        return result
            else:
                if event_subscription.event_filters:
                    current_app.logger.error("Event filters provided but EnhancedEventReport is not enabled")
                    return bad_request_error(
                        detail="Bad Param",
                        cause="Event filters provided but EnhancedEventReport is not enabled",
                        invalid_params=[{"param": "eventFilters", "reason": "EnhancedEventReport is not enabled"}]
                    )

            # Generate subscriptionID
            subscription_id = secrets.token_hex(15)
            evnt = dict()
            evnt["subscriber_id"] = subscriber_id
            evnt["subscription_id"] = subscription_id
            evnt.update(event_subscription.to_dict())
            mycol.insert_one(evnt)

            current_app.logger.debug("Event Subscription inserted in database")

            self.auth_manager.add_auth_event(subscription_id, subscriber_id)

            res = make_response(object=serialize_clean_camel_case(event_subscription), status=201)
            res.headers['Location'] = f"https://{os.getenv("CAPIF_HOSTNAME")}/capif-events/v1/{str(subscriber_id)}/subscriptions/{str(subscription_id)}"

            return res

        except Exception as e:
            exception = "An exception occurred in create event"
            current_app.logger.error(exception + "::" + str(e))
            return internal_server_error(detail=exception, cause=str(e))

    def delete_event(self, subscriber_id, subscription_id):

        try:
            mycol = self.db.get_col_by_name(self.db.event_collection)

            current_app.logger.debug("Removing event subscription")

            result = self.__check_subscriber_id(subscriber_id)


            if  isinstance(result, Response):
                return result

            my_query = {'subscriber_id': subscriber_id,
                    'subscription_id': subscription_id}
            eventdescription = mycol.find_one(my_query)

            if eventdescription is None:
                current_app.logger.error("Event subscription not found")
                return not_found_error(detail="Event subscription not exist", cause="Event API subscription id not found")

            mycol.delete_one(my_query)
            current_app.logger.debug("Event subscription removed from database")

            self.auth_manager.remove_auth_event(subscription_id, subscriber_id)

            out =  "The event matching subscriptionId  " + subscription_id + " was deleted."
            return make_response(out, status=204)

        except Exception as e:
            exception= "An exception occurred in delete event"
            current_app.logger.error(exception + "::" + str(e))
            return internal_server_error(detail=exception, cause=str(e))
        
    def put_event(self, event_subscription, subscriber_id, subscription_id):
        try:
            mycol = self.db.get_col_by_name(self.db.event_collection)

            current_app.logger.debug("Updating event subscription")
            
            if event_subscription.supported_features is None:
                return bad_request_error(
                    detail="supportedFeatures not present in request",
                    cause="supportedFeatures not present",
                    invalid_params=[{"param": "supportedFeatures", "reason": "not defined"}]
                )

            result = self.__check_subscriber_id(subscriber_id)

            if  isinstance(result, Response):
                return result
            
            if EventSubscription.return_supp_feat_dict(event_subscription.supported_features)["EnhancedEventReport"] and event_subscription.event_filters:
                result = self.__check_event_filters(event_subscription.events, clean_empty(event_subscription.to_dict()["event_filters"]))
                if  isinstance(result, Response):
                    return result

            my_query = {'subscriber_id': subscriber_id,
                    'subscription_id': subscription_id}
            eventdescription = mycol.find_one(my_query)

            if eventdescription is None:
                current_app.logger.error("Event subscription not found")
                return not_found_error(detail="Event subscription not exist", cause="Event API subscription id not found")
            body = event_subscription.to_dict()

            body["subscriber_id"] = subscriber_id
            body["subscription_id"] = subscription_id

            mycol.replace_one(my_query, body)
            current_app.logger.debug("Event subscription updated from database")


            res = make_response(object=serialize_clean_camel_case(event_subscription), status=200)

            return res
            
        except Exception as e:
            exception= "An exception occurred in updating event"
            current_app.logger.error(exception + "::" + str(e))
            return internal_server_error(detail=exception, cause=str(e)) 


    def patch_event(self, event_subscription, subscriber_id, subscription_id):
        try:
            mycol = self.db.get_col_by_name(self.db.event_collection)

            current_app.logger.debug("Patching event subscription")

            result = self.__check_subscriber_id(subscriber_id)

            if  isinstance(result, Response):
                return result

            my_query = {'subscriber_id': subscriber_id,
                    'subscription_id': subscription_id}
            eventdescription = mycol.find_one(my_query)

            if eventdescription is None:
                current_app.logger.error("Event subscription not found")
                return not_found_error(detail="Event subscription not exist", cause="Event API subscription id not found")

            if EventSubscription.return_supp_feat_dict(eventdescription.get("supported_features"))["EnhancedEventReport"]:
                if event_subscription.events and event_subscription.event_filters:
                    result = self.__check_event_filters(event_subscription.events, clean_empty(event_subscription.to_dict()["event_filters"]))
                elif event_subscription.events and  event_subscription.event_filters is None and eventdescription.get("event_filters", None):
                    result = self.__check_event_filters(event_subscription.events, eventdescription.get("event_filters"))
                elif event_subscription.events is None and event_subscription.event_filters:
                    result = self.__check_event_filters(eventdescription.get("events"), clean_empty(event_subscription.to_dict()["event_filters"]))

                if  isinstance(result, Response):
                    return result

            body = clean_empty(event_subscription.to_dict())
            document = mycol.update_one(my_query, {"$set":body})
            document = mycol.find_one(my_query)
            current_app.logger.debug("Event subscription patched from database")

            res = make_response(object=EventSubscription.from_dict(dict_to_camel_case(document)), status=200)

            return res
            
        except Exception as e:
            exception= "An exception occurred in patching event"
            current_app.logger.error(exception + "::" + str(e))
            return internal_server_error(detail=exception, cause=str(e)) 
