from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync

from games.models import GameModel
from profiles.models import FriendModel, AskFriendModel

import time
import json

class ChatNoticeConsumer(WebsocketConsumer):

    def connect(self):

        user = self.scope["user"]
        #if (user.is_anonymous or not user.is_authenticated):
            #return

        if (self.channel_layer == None):
            return

        self.room_group_name = f'chatNotice{user.pk}'
        if (not hasattr(self.channel_layer, "users_channels")):
            self.channel_layer.users_channels = {}
        self.channel_layer.users_channels[user.pk] = self.channel_name

        if (not hasattr(self.channel_layer, "invite")):
            self.channel_layer.invite = {}
        self.channel_layer.invite[user.pk] = [];

        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name,
            self.channel_name
        )

        self.accept()

        self.sync()

    def disconnect(self, code):

        user = self.scope["user"]
        if (user.is_anonymous or not user.is_authenticated):
            return    

        del self.channel_layer.users_channels[user.pk]
        del self.channel_layer.invite[user.pk]

        for inviter_id, inviteds_id in self.channel_layer.invite.items():
            if (user.pk in inviteds_id): 
                self.channel_layer.invite[inviter_id].remove(user.pk)

        self.sync()


    def receive(self, text_data=None, bytes_data=None):

        if text_data == None:
            return

        user = self.scope["user"]
        #if (user.is_anonymous or not user.is_authenticated):
            #return

        text_data_json = json.loads(text_data)
        
        type_notice = text_data_json.get('type')
        targets : list = text_data_json.get('targets')
        content : dict = text_data_json.get('content')

        if (type_notice == None or targets == None):
            return

        if (self.channel_layer == None):
            return

        message_time: int = text_data_json.get('time')

        if (message_time == None):
            message_time: int = int(time.time() * 1000)

        result = None
        try:
            status, result = getattr(self, "pre_" + type_notice)(user, targets)
        except AttributeError as error:
            status = 200

        if (status < 300):
            if targets == "all":
                targets = list(self.channel_layer.users_channels.keys())

            for target in targets:
                channel = self.channel_layer.users_channels.get(target)
                if (channel == None or target == user.pk):
                    if (channel == None):
                        status = 444 # target not connected
                    continue
                async_to_sync(self.channel_layer.send)(channel, {
                    'type':type_notice,
                    'author_id':user.pk,
                    'content':content,
                    'result':result,
                    'targets': targets,
                    'time':message_time,
                    'status': 200,
                })

        async_to_sync(self.channel_layer.send)(self.channel_layer.users_channels.get(user.pk), {
            'type':type_notice,
            'author_id':user.pk,
            'result':result,
            'targets': targets,
            'time':message_time,
            'status':status,
        })

    def sync(self, user = None, level = None):
        
        sendToUser = True
        if (user == None):
            user = self.scope["user"]
            sendToUser = False
            

        if (level == None):
            level = 0

        message_time: int = int(time.time() * 1000)
        if (sendToUser):
            targets = [user.pk]
        else:
            targets = list(self.channel_layer.users_channels.keys())

        for target in targets:
            channel = self.channel_layer.users_channels.get(target)
            if (channel == None or (not sendToUser and target == user.pk)):
                continue
            async_to_sync(self.channel_layer.send)(channel, {
                'type':"online_users",
                'author_id':user.pk,
                'targets': targets,
                'time':message_time,
                'status': 200,
            })
            if (level >= 1): 
                async_to_sync(self.channel_layer.send)(channel, {
                    'type':"invite",
                    'author_id':user.pk,
                    'targets': targets,
                    'time':message_time,
                    'status': 200,
                })


    def pre_invite(self, user, targets):

        if (user.is_anonymous or not user.is_authenticated):
            return 400, None

        status = 200
        for target in targets:
            if (target in self.channel_layer.invite[user.pk]):
                status = 409
                continue

            channel = self.channel_layer.users_channels.get(target)
            if (channel == None):
                status = 404
                continue

            # Add the invited in "self.channel_layer.invite"
            if (user.pk != target):
                self.channel_layer.invite[user.pk].append(target)

        return status, None

    def get_invites(self, user):
        invites = []
        for inviter_id, inviteds_id in self.channel_layer.invite.items():
            if (user.pk in inviteds_id and user.pk != inviter_id): 
                invites.append(inviter_id)
        return invites;

    def invite(self, event):

        user = self.scope["user"]
        if (user.is_anonymous or not user.is_authenticated):
            return

        invites = self.get_invites(user)

        self.send(text_data=json.dumps({
            'type':event['type'],
            'author_id':event['author_id'],
            'invites': invites,
            'targets': event['targets'],
            'time': event['time'],
            'status':event['status'],
        }))

    def pre_accept_invite(self, user, targets):

        if (user.is_anonymous or not user.is_authenticated):
            return 400, None

        if (user.pk not in self.channel_layer.invite[targets[0]]):
            return 400, None

        self.channel_layer.invite[targets[0]].remove(user.pk)

        if (targets[0] in self.channel_layer.invite[user.pk]):
            self.channel_layer.invite[user.pk].remove(targets[0])

        id_game = GameModel().create([user.pk, targets[0]]);

        return 200, id_game

    def accept_invite(self, event):

        user = self.scope["user"]
        if (user.is_anonymous or not user.is_authenticated):
            return

        invites = self.get_invites(user)

        self.send(text_data=json.dumps({
            'type':event['type'],
            'author_id':event['author_id'],
            'id_game': event['result'],
            'invites': invites,
            'time': event['time'],
            'status':event['status'],
        }))

    def pre_refuse_invite(self, user, targets):

        if (user.is_anonymous or not user.is_authenticated):
            return 400, None

        if (user.pk not in self.channel_layer.invite[targets[0]]):
            return 400, None

        self.channel_layer.invite[targets[0]].remove(user.pk)

        if (targets[0] in self.channel_layer.invite[user.pk]):
            self.channel_layer.invite[user.pk].remove(targets[0])

        return 200, None
        
    def refuse_invite(self, event):

        user = self.scope["user"]
        if (user.is_anonymous or not user.is_authenticated):
            return

        invites = self.get_invites(user)

        self.send(text_data=json.dumps({
            'type':event['type'],
            'author_id':event['author_id'],
            'invites': invites,
            'time': event['time'],
            'status':event['status'],
        }))

    def pre_ask_friend(self, user, targets):

        if (user.is_anonymous or not user.is_authenticated):
            return 400, None

        if (AskFriendModel.objects.filter(asker=user.pk, asked=targets[0])):
            return 409, None

        if (FriendModel().isFriend(user.pk, targets[0])):
            return 409, None

        if (targets[0] != None):
            AskFriendModel(asker=user.pk, asked=targets[0]).save()

        return 200, None

    def send_ask_friend(self, event):

        user = self.scope["user"]
        if (user.is_anonymous or not user.is_authenticated):
            return

        asked = AskFriendModel().getAsked(user.pk)
        asker = AskFriendModel().getAsker(user.pk)

        online_friends = self.get_online_friend(user)

        self.send(text_data=json.dumps({
            'type':event['type'],
            'author_id':event['author_id'],
            'targets':event['targets'],
            'asker': asker,
            'asked': asked,
            'online': online_friends,
            'time': event['time'],
            'status':event['status'],
        }))

    def ask_friend(self, event):
        self.send_ask_friend(event)

    def delete_ask(self, asker, user_asked):
        if (user_asked.is_anonymous or not user_asked.is_authenticated):
            return 400, None

        asked = user_asked.pk

        if (not AskFriendModel.objects.filter(asker=asker, asked=asked)):
            return 404, None

        if (FriendModel().isFriend(asker, asked)):
            return 409, None

        if (not AskFriendModel().deleteAsk(asker, asked)):
            return 400, None

        return 200, None

    def pre_accept_friend(self, user, targets):
        status, result = self.delete_ask(targets[0], user)
        
        if (status == 200):
            FriendModel(user_id1=user.pk, user_id2=targets[0]).save()
        return status, result

    def accept_friend(self, event):
        self.send_ask_friend(event)

    def pre_refuse_friend(self, user, targets):
        return self.delete_ask(targets[0], user)

    def refuse_friend(self, event):
        self.send_ask_friend(event)

    def pre_remove_friend(self, user, targets):

        if (user.is_anonymous or not user.is_authenticated):
            return 400, None
        
        if (not FriendModel().isFriend(user.pk, targets[0])):
            return 409, None

        if (not FriendModel().deleteFriend(user.pk, targets[0])):
            return 400, None

        return 200, None
    
    def remove_friend(self, event):
        self.send_ask_friend(event)


    def get_online_friend(self, user):
        online_friends = {}
        for friend in FriendModel().getFriends(user.pk):
            if (friend in self.channel_layer.users_channels):
                online_friends[friend] = "green"
            else:
                online_friends[friend] = "red"
        return online_friends

    def online_users(self, event):

        user = self.scope["user"]
        if (user.is_anonymous or not user.is_authenticated):
            return

        online_friends = self.get_online_friend(user)

        self.send(text_data=json.dumps({
            'type':event['type'],
            'author_id':event['author_id'],
            'online':online_friends,
            'time': event['time'],
            'status':event['status'],
        }))
