import threading
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from functools import wraps
from app.entities import enums
from app.entities.models import EntityLog, Notification

from django.conf import settings
from websocket import create_connection
import json
from rest_framework import status

from rest_framework import serializers
import copy
import json

REALTIMESERVER_IP = settings.REALTIMESERVER_IP


def model_to_dict(instance):
    try:
        mySerializer = copy.deepcopy(DynamicSerializer)
        
        mySerializer.Meta.model = instance.__class__

        return mySerializer(instance).data
        
    except Exception as e:
        return 'Serialization Error'


class DynamicSerializer(serializers.ModelSerializer):
    class Meta:
        model = None
        fields = '__all__'


class CustomPagination(PageNumberPagination):
    page_size = 10
    max_page_size = 50
    page_query_param = 'page'
    page_size_query_param = 'page_size'

    def get_paginated_response(self, data):
        return Response({
            'page_number': self.page.number,
            'size_per_page': self.page.paginator.per_page,
            'total_pages': self.page.paginator.num_pages,
            'total': self.page.paginator.count,
            'code': data['code'],
            'status': data['status'],
            'message': data['message'],
            'results': data['results']
        })


class PaginationForETL(PageNumberPagination):
    page_size = 100
    max_page_size = 3000
    page_query_param = 'page'
    page_size_query_param = 'page_size'

    def get_paginated_response(self, data):
        return Response({
            'page_number': self.page.number,
            'size_per_page': self.page.paginator.per_page,
            'total_pages': self.page.paginator.num_pages,
            'total': self.page.paginator.count,
            'code': data['code'],
            'status': data['status'],
            'message': data['message'],
            'results': data['results']
        })


# def entitylogs_decorator(function):
#     @wraps(function)
#     def wrapper(self, request, *args, **kwargs):
#         req_method = request.method
#         print(req_method)
#         if req_method.upper() == 'DELETE':
#             req_method = enums.LogEnum.DELETED.value
#         elif req_method.upper == 'PUT':
#             req_method = enums.LogEnum.UPDATE.value
#         from_instance = from app.applicationlayer.utils import model_to_dict(self.get_object())
#         print("self.serializer.data['id']")
#         print(self.serializer.data['id'])
#         EntityLog.objects.create(
#             action=req_method,
#             entity=enums.LogEntitiesEnum.APPLICATION.value,
#             row_id=from_instance['id'],
#             fromValue=from_instance
#         )
#         return function(self, request, *args, **kwargs)

#     return wrapper

def log_save(action, entity, row_id, fromValue, toValue):
    EntityLog.objects.create(
            action=action,
            entity=entity,
            row_id=int(row_id),
            fromValue=fromValue,
            toValue=toValue
        )
    return True


def status_message_response(code, status, message, results):
    message = {
        'code': code,
        'status': status,
        'message': message,
        'results': results
    }
    return message


class QuerySetHelper:

    @staticmethod
    def Sort(context):
        sort_field = context.request.query_params.get('sort_field')
        sort_order = context.request.query_params.get('sort_order')
        if sort_field and sort_order:
            if sort_order.lower() == 'asc':
                context.queryset = context.queryset.order_by(sort_field)
            else:
                context.queryset = context.queryset.order_by(f"-{sort_field}")
        return context.queryset

    @staticmethod
    def Search(context):
        search_field = context.request.query_params.get('search-field')
        search_key = context.request.query_params.get('search-key')
        if search_field and search_key:
            context.queryset = context.queryset(
                __raw__={f"{search_field}" : {"$regex" : f".*{search_key.lower()}.*"}}
            )
        return context.queryset

    @staticmethod
    def Filter(context):
        if int(len(context.request.query_params)) > 0:
            filtering_kwargs = {}
            with_params = []
            common_params = (
                'page', 'page-size', 'page_size', 'sort_order', 'sort_field'
            )
            for field, value in context.request.GET.items():
                filtering_kwargs = {}
                if value and field.lower() not in common_params:
                    filtering_kwargs[field] = {"$regex" : f".*{value.lower()}.*"}
                    filtering_kwargs[field] = {"$regex" : f".*{value}.*"}
                    # filtering_kwargs[field] = {"$regex" : f".*{value.lower()}.*"}
                    with_params.append(filtering_kwargs)
                    raw_query = {"$or": with_params}
                    context.queryset = context.queryset(__raw__=raw_query)
        return context.queryset


def main_threading(args, func_name):
    t1 = threading.Thread(
        target=func_name, args=(args,),
        daemon=False
    )
    t1.start()
    return True


# notification

def send_broadcast_message(room_name, sender, message):
    ws = create_connection(f"ws://{REALTIMESERVER_IP}/ws/chat/{room_name}/")
    data = {
        'sender': sender,
        'message': message
    }
    ws.send(json.dumps(data))
    ws.close()


def notification_create(form_code, message, account_no,
                        sender_account_no, app):
    
    try:
    
        Notification.objects.create(
                form_code=form_code,
                notif_type='TASK',
                message=message,
                is_read=False,
                app=app,
                account_no=account_no,
                sender_account_no=sender_account_no
            )
        
        ROOM = account_no
        SENDER = sender_account_no
        
        send_broadcast_message(
            ROOM,
            SENDER,
            'NEW NOTIFICATIONS'
        )

        return True

    except Exception as e:
        message = {
            'code': 500,
            'status': 'failed',
            'message': 'Request was not able to process' + str(e.__class__)
        }
        return Response(message,
                        status=status.HTTP_500_INTERNAL_SERVER_ERROR)