import logging
from django.contrib.auth import get_user_model
from django.dispatch import receiver
from django.db.models import signals
from djangopasswordlessknox.models import CallbackToken
from djangopasswordlessknox.models import generate_numeric_token
from djangopasswordlessknox.settings import api_settings
from djangopasswordlessknox.services import TokenService

logger = logging.getLogger(__name__)


@receiver(signals.pre_save, sender=CallbackToken)
def invalidate_previous_tokens(sender, instance, **kwargs):
    """
    Invalidates all previously issued tokens as a post_save signal.
    """
    active_tokens = None
    if isinstance(instance, CallbackToken):
        active_tokens = (
            CallbackToken.objects.active()
            .filter(user=instance.user)
            .exclude(id=instance.id)
        )

    # Invalidate tokens
    if active_tokens:
        for token in active_tokens:
            token.is_active = False
            token.save()


@receiver(signals.pre_save, sender=CallbackToken)
def check_unique_tokens(sender, instance, **kwargs):
    """
    Ensures that mobile and email tokens are unique or tries once more to generate.
    """
    if isinstance(instance, CallbackToken):
        if CallbackToken.objects.filter(key=instance.key, is_active=True).exists():
            instance.key = generate_numeric_token()


User = get_user_model()


@receiver(signals.pre_save, sender=User)
def update_alias_verification(sender, instance, **kwargs):
    """
    Flags a user's email as unverified if they change it.
    Optionally sends a verification token to the new endpoint.
    """
    if isinstance(instance, User):
        if instance.id:
            if api_settings.PASSWORDLESS_USER_MARK_EMAIL_VERIFIED is True:
                """
                For marking email aliases as not verified when a user changes it.
                """
                email_field = api_settings.PASSWORDLESS_USER_EMAIL_FIELD_NAME
                email_verified_field = (
                    api_settings.PASSWORDLESS_USER_EMAIL_VERIFIED_FIELD_NAME
                )

                # Verify that this is an existing instance and not a new one.
                try:
                    user_old = User.objects.get(id=instance.id)  # Pre-save object
                    instance_email = getattr(instance, email_field)  # Incoming Email
                    old_email = getattr(user_old, email_field)  # Pre-save object email

                    if (
                        instance_email != old_email
                        and instance_email != ""
                        and instance_email is not None
                    ):
                        # Email changed, verification should be flagged
                        setattr(instance, email_verified_field, False)
                        if (
                            api_settings.PASSWORDLESS_AUTO_SEND_VERIFICATION_TOKEN
                            is True
                        ):
                            email_subject = (
                                api_settings.PASSWORDLESS_EMAIL_VERIFICATION_SUBJECT
                            )
                            email_plaintext = api_settings.PASSWORDLESS_EMAIL_VERIFICATION_PLAINTEXT_MESSAGE
                            email_html = api_settings.PASSWORDLESS_EMAIL_VERIFICATION_TOKEN_HTML_TEMPLATE_NAME
                            message_payload = {
                                "email_subject": email_subject,
                                "email_plaintext": email_plaintext,
                                "email_html": email_html,
                            }
                            success = TokenService.send_token(
                                instance, "email", **message_payload
                            )

                            if success:
                                logger.info(
                                    "djangopasswordlessknox: Successfully sent email on updated address: %s"
                                    % instance_email
                                )
                            else:
                                logger.info(
                                    "djangopasswordlessknox: Failed to send email to updated address: %s"
                                    % instance_email
                                )

                except User.DoesNotExist:
                    # User probably is just initially being created
                    setattr(instance, email_verified_field, True)

            if api_settings.PASSWORDLESS_USER_MARK_MOBILE_VERIFIED is True:
                """
                For marking mobile aliases as not verified when a user changes it.
                """
                mobile_field = api_settings.PASSWORDLESS_USER_MOBILE_FIELD_NAME
                mobile_verified_field = (
                    api_settings.PASSWORDLESS_USER_MOBILE_VERIFIED_FIELD_NAME
                )

                # Verify that this is an existing instance and not a new one.
                try:
                    user_old = User.objects.get(id=instance.id)  # Pre-save object
                    instance_mobile = getattr(instance, mobile_field)  # Incoming mobile
                    old_mobile = getattr(
                        user_old, mobile_field
                    )  # Pre-save object mobile

                    if (
                        instance_mobile != old_mobile
                        and instance_mobile != ""
                        and instance_mobile is not None
                    ):
                        # Mobile changed, verification should be flagged
                        setattr(instance, mobile_verified_field, False)
                        if (
                            api_settings.PASSWORDLESS_AUTO_SEND_VERIFICATION_TOKEN
                            is True
                        ):
                            mobile_message = api_settings.PASSWORDLESS_MOBILE_MESSAGE
                            message_payload = {"mobile_message": mobile_message}
                            success = TokenService.send_token(
                                instance, "mobile", **message_payload
                            )

                            if success:
                                logger.info(
                                    "djangopasswordlessknox: Successfully sent SMS on updated mobile: %s"
                                    % instance_mobile
                                )
                            else:
                                logger.info(
                                    "djangopasswordlessknox: Failed to send SMS to updated mobile: %s"
                                    % instance_mobile
                                )

                except User.DoesNotExist:
                    # User probably is just initially being created
                    setattr(instance, mobile_verified_field, True)