from channels.db import database_sync_to_async
from django.core.exceptions import ObjectDoesNotExist
from django.core.signing import TimestampSigner, BadSignature
from django.db import IntegrityError
from django.contrib.auth import get_user_model
from xmppserver.hooks.base import BaseAuthHook
from ..conf import settings

class DefaultAuthHook(BaseAuthHook):

    async def bind(self, stream):
        stream.bound_user = self.user

    async def unbind(self, stream):
        stream.bound_user = None
        self.user = None

    @staticmethod
    def get_webuser_username(user):
        return user.get_username()

    @staticmethod
    def get_webuser_by_username(username):
        return get_user_model().objects.get_by_natural_key(username)

    @database_sync_to_async
    def check_webuser(self, stream, user, username):
        if user.is_authenticated and \
           username == self.get_webuser_username(user):
            self.user = user
            return True
        return False

    @database_sync_to_async
    def check_token(self, stream, username, token):
        # validate token generated by credentials_view
        try:
            signer = TimestampSigner(salt='xmppserver.credentials')
            token = signer.unsign(token, settings.CREDENTIALS_MAX_AGE)
        except BadSignature:
            return False
        jid = '%s@%s' % (username, stream.host)
        if token != jid:
            return False
        try:
            user = self.get_webuser_by_username(username)
        except ObjectDoesNotExist:
            pass
        else:
            if user and user.is_active:
                self.user = user
                return True
        return False

    @database_sync_to_async
    def check_password(self, stream, username, password):
        try:
            user = self.get_webuser_by_username(username)
        except ObjectDoesNotExist:
            pass
        else:
            if user and user.is_active and \
               user.check_password(password):
                self.user = user
                return True
        return False

    @database_sync_to_async
    def valid_contact(self, username):
        try:
            user = self.get_webuser_by_username(username)
        except ObjectDoesNotExist:
            pass
        else:
            if user and user.is_active:
                return True
        return False

    @database_sync_to_async
    def create_user(self, username, password):
        user = get_user_model()()
        setattr(user, user.USERNAME_FIELD, username)
        user.set_password(password)
        try:
            user.save()
        except IntegrityError:
            return False
        return True

    @database_sync_to_async
    def change_password(self, password):
        self.user.set_password(password)
        self.user.save()

    @database_sync_to_async
    def delete_user(self):
        self.user.delete()
