from channels.generic.websocket import WebsocketConsumer 
from django.shortcuts import get_object_or_404
from .models import * 
import json
from django.template.loader import render_to_string
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer 
from django.shortcuts import get_object_or_404
from customercore.models import * 
import json
from django.template.loader import render_to_string
from asgiref.sync import async_to_sync


class TicketRoomConsumer(WebsocketConsumer):
    def connect(self): 
        self.user = self.scope['user']
        self.ticket_id = self.scope['url_route']['kwargs']['ticket_id']
        self.ticket = get_object_or_404(Ticket, id=self.ticket_id)
        self.ticket_number = self.ticket.ticket_number

        existing_connection = TicketConnection.objects.filter(ticket=self.ticket, user=self.user, terminated_at__isnull=True).delete()
        TicketConnection.objects.create(
            ticket=self.ticket,
            user=self.user,
            date=datetime.now()
        )
        staff_profile = StaffProfile.objects.filter(user=self.user).first()
        if staff_profile:
            if not TicketStaff.objects.filter(staff=staff_profile, ticket=self.ticket).exists():
                TicketStaff.objects.create(
                    staff=staff_profile,
                    ticket=self.ticket,
                    date_added=datetime.now()
                )

        async_to_sync(self.channel_layer.group_add)(
            self.ticket_number, self.channel_name 
        )
        self.accept()
        self.modify_online_user()


    def disconnect(self, close_code):
        TicketConnection.objects.filter(
            ticket=self.ticket,
            user=self.user,
        ).update(terminated_at=datetime.now())
        async_to_sync(self.channel_layer.group_discard)(
           self.ticket_number, self.channel_name 
        )
        self.modify_online_user()

    def receive(self, text_data): 
        text_data_json = json.loads(text_data)
        event_type = text_data_json.get('event_type')
        
        if event_type == 'typing':
            event = {
                'type': 'typing_handler',
                'user_id': text_data_json.get('user_id')
            }
            async_to_sync(self.channel_layer.group_send)(
                self.ticket_number, event
            )
        elif event_type == 'stop_typing':
            event = {
                'type': 'stop_typing_handler',
            }
            async_to_sync(self.channel_layer.group_send)(
                self.ticket_number, event
            )
        elif event_type == 'update_reaction':
            reaction = text_data_json['reaction']
            update_id = text_data_json['update_id']
            event = {
                'type': 'reaction_handler',
                'update_id': update_id,
                'reaction': reaction,
            }
            async_to_sync(self.channel_layer.group_send)(
                self.ticket_number, event
            )
        else:
            body = text_data_json['description']
            file_paths = text_data_json['filePath']
            ticketupdate = TicketUpdate.objects.create(
                added_by=self.user,
                description=body,
                ticket=self.ticket,
                date_added=datetime.now()
            )
            for file_path in file_paths:
                ticket_attachment = TicketAttachment(
                    ticket_update=ticketupdate,
                    file_path=file_path  
                )
                ticket_attachment.save()
            event = {
                'type': 'update_handler',
                'update_id': ticketupdate.id
            }
            async_to_sync(self.channel_layer.group_send)(
                self.ticket_number, event
            )

    def update_handler(self, event):
        update_id = event['update_id']
        update = TicketUpdate.objects.get(id=update_id) #No need for serialize because in the context of the method not in the event data
        context = { 
            'update': update,
            'user': self.user
        }


        html = render_to_string("details_templates/partials/new-ticket-message.html", context=context)
        self.send(text_data=json.dumps({
            'event_type': 'update',
            'html': html
        }))

    def typing_handler(self, event):
        user_id = event['user_id']
        typing_user = get_object_or_404(User, id=user_id)
        context = {
            'typing_user': typing_user
        }
        html = render_to_string("details_templates/partials/typing-message.html", context=context)
        self.send(text_data=json.dumps({
            'event_type': 'typing',
            'html': html
        }))

    def stop_typing_handler(self, event):
        self.send(text_data=json.dumps({
            'event_type': 'stop_typing'
        }))

    def reaction_handler(self, event):
        update_id = event['update_id']
        reaction = event['reaction']
        user = self.user
        
        update = TicketUpdate.objects.get(id=update_id)
        existing_reaction = TicketUpdateReaction.objects.filter(ticket_update=update, customer=user).first()
        new_reaction = None
        if existing_reaction:
            # If the existing reaction type is equal to the new reaction, delete it
            if existing_reaction.reaction == reaction:
                existing_reaction.delete()
            else:
                # If not, delete all previous reactions and add a new one
                TicketUpdateReaction.objects.filter(ticket_update=update, customer=user).delete()
                new_reaction = TicketUpdateReaction.objects.create(
                    ticket_update=update, 
                    reaction=reaction,
                    customer=user
                )
        else:
            # If there's no existing reaction, simply add the new one
            new_reaction = TicketUpdateReaction.objects.create(
                ticket_update=update, 
                reaction=reaction,
                customer=user
            )
    
        self.send(text_data=json.dumps({
            'event_type': 'reaction',
            'update_id': update_id,
            'reaction': new_reaction.reaction if new_reaction else None
        }))

    def new_status_handler(self, event):
        last_ticket_status = TicketStatus.objects.filter(ticket=self.ticket).last()
        context = {
            'last_ticket_status': last_ticket_status,
            'new': True
        }
        html = render_to_string("details_templates/partials/ticket-status.html", context=context)
        self.send(text_data=json.dumps({
            'event_type': 'ticket_status',
            'html': html
        })) 


    def new_department_handler(self, event):
        last_ticket_department = TicketDepartment.objects.filter(ticket=self.ticket).last()
        context = {
            'last_ticket_department': last_ticket_department,
            'new': True
        }
        html = render_to_string("details_templates/partials/ticket-department.html", context=context)
        self.send(text_data=json.dumps({
            'event_type': 'ticket_department',
            'html': html
        })) 


    def modify_online_user(self):
        connections = TicketConnection.objects.filter(ticket=self.ticket, terminated_at__isnull=True)
        event = {
            'type': 'user_connection_handler',
            'user': {
            'id': self.user.id,
            'username': self.user.username,
            'first_name': self.user.first_name,
            'last_name': self.user.last_name,
            },
            'connections': [
                {
                    'first_name': conn.user.first_name,
                    'last_name': conn.user.last_name,
                    'image': conn.user.staffprofile.image.url if hasattr(conn.user, 'staffprofile') and conn.user.staffprofile.image else None,
                    'date': conn.date.isoformat() if conn.date else None,
                } for conn in connections  
            ] #Everything included in a webscoket's event data needs to be serialized
        }
        async_to_sync(self.channel_layer.group_send)(
            self.ticket_number, event
        )

    def user_connection_handler(self, event):
        context = {
            'connections': event['connections'],
            'user': event['user']
        }
        html = render_to_string("details_templates/partials/ticket-online-users.html", context=context)
        self.send(text_data=json.dumps({
            'event_type': 'user_status',
            'html': html
        })) 




class NewTicketConsumer(WebsocketConsumer):
    def connect(self):
        self.user = self.scope['user']
        async_to_sync(self.channel_layer.group_add)(
            "new_ticket_group",
            self.channel_name
        )
        self.accept()

    def disconnect(self, close_code):
        async_to_sync(self.channel_layer.group_discard)(
            "new_ticket_group",
            self.channel_name
        )

    def new_ticket_event(self, event):
        ticket_id = event['ticket_id']
        ticket = Ticket.objects.get(id=ticket_id)
        is_ticketstaff_member = (
            hasattr(self.user, 'staffprofile') and 
            (self.user.staffprofile in ticket.get_all_ticket_staff() or self.user.is_superuser)
        )
        is_ticket_customer = (
            hasattr(self.user, 'customerprofile') and 
            (self.user.customerprofile == ticket.customer))
        if is_ticketstaff_member or is_ticket_customer:
            context = {'ticket': ticket, 'new': True}
            if is_ticketstaff_member:
                html = render_to_string("details_templates/partials/staff-ticket-display.html", context)
            else:
                html = render_to_string("details_templates/partials/customer-ticket-display.html", context)
            self.send(text_data=json.dumps({
                'event_type': 'new_ticket',
                'html': html,
                'ticket_id': ticket_id
            }))




class NewTicketUpdateConsumer(WebsocketConsumer):
    def connect(self):
        self.user = self.scope['user']
        async_to_sync(self.channel_layer.group_add)(
            "new_ticket_update_group",
            self.channel_name
        )
        self.accept()

    def disconnect(self, close_code):
        async_to_sync(self.channel_layer.group_discard)(
            "new_ticket_update_group",
            self.channel_name
        )

    def new_ticket_update_event(self, event):
        ticket_update_id = event['ticket_update_id']
        ticket_update = TicketUpdate.objects.get(id=ticket_update_id)
        ticket = ticket_update.ticket
        ticket_id = ticket.id
        unread_updates_count = 0
        for ticket_update in ticket.ticketupdate_set.exclude(added_by=self.user): 
            if not TicketRead.objects.filter(ticket_update=ticket_update, user=self.user, read=True).exists():
                unread_updates_count += 1
            ticket.unread_updates_count = unread_updates_count
        is_ticketstaff_member = (
            hasattr(self.user, 'staffprofile') and 
            (self.user.staffprofile in ticket.get_all_ticket_staff() or self.user.is_superuser)
        )
        is_ticket_customer = (
            hasattr(self.user, 'customerprofile') and 
            (self.user.customerprofile == ticket.customer))
        if is_ticketstaff_member or is_ticket_customer:
            context = {'ticket': ticket, 'new': True}
            if is_ticketstaff_member:
                html = render_to_string("details_templates/partials/staff-ticket-display.html", context)
            else:
                html = render_to_string("details_templates/partials/customer-ticket-display.html", context)
            self.send(text_data=json.dumps({
                'event_type': 'new_ticket_update',
                'html': html,
                'ticket_id': ticket_id
            }))



