import sys

import rfc3987
import pymongo
import secrets
import re
from flask import current_app, Flask, Response
import json
from ..models.problem_details import ProblemDetails
from capif_events.models.event_subscription import EventSubscription  # noqa: E501
from .resources import Resource
from bson import json_util
from .responses import internal_server_error, not_found_error, make_response, bad_request_error
from ..db.db import MongoDatabse
from ..util import serialize_clean_camel_case, clean_empty, dict_to_camel_case
from .auth_manager import AuthManager
import os


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 __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

            # 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"http://{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

            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")

            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)) 
