# -*- encoding: utf-8 -*-
import hashlib
from datetime import datetime
import logging

from django.contrib.auth import get_user_model
from django.utils.crypto import get_random_string
from django.core.mail import send_mail

from django.core.validators import validate_email
from django.core.exceptions import ValidationError

from django.conf import settings as appSettings

from . import models

# Get an instance of a logger
logger = logging.getLogger(__name__)

# Our user model
USER_MODEL = get_user_model()

class EmailTokenAuthBackend:
    """
    Provides authorization via email workflow.

    The user is verified by a token previously sent by email.
    """

    def get_user(self, user_id):
        try:
            return USER_MODEL.objects.get(pk=user_id)
        except USER_MODEL.DoesNotExist:
            return None
        

    def authenticate(self, request, emailToken=None):
        if emailToken is None:
            return None

        # do we have a user with this valid token ?
        try:
            u = USER_MODEL.objects.get(emailToken=emailToken)

            if u.etStamp and (datetime.now() - u.etStamp).total_seconds() < int(appSettings.TOKEN_VALID_SEC):
                u.emailToken = None
                u.etStamp = None
                u.save()
                return u
            else:
                u.emailToken = None
                u.etStamp = None
                u.save()
                raise ValidationError("Přihlašovací odkaz vypršel, nechte si poslat nový.")

        except USER_MODEL.DoesNotExist:
            pass

        # is this a first-time login, so the user has to be created ?
        rslt = models.AppRegEmail.objects.filter(emailToken=emailToken)
        if len(rslt) == 0:
            return None

        elif len(rslt) == 1:
            reg = rslt[0]
            if reg.etStamp and (datetime.now() - reg.etStamp).total_seconds() < int(appSettings.TOKEN_VALID_SEC):
                rsltUsers = models.AppUser.objects.filter(username=reg.email)
                if len(rsltUsers) != 0 :
                    raise ValidationError("Uživatelské jméno je již obsazeno.")

                rsltUsers = models.AppUser.objects.filter(email=reg.email)
                if len(rsltUsers) != 0 :
                    raise ValidationError("E-mailová adresa je již obsazena.")

                # token valid, create the user
                u = USER_MODEL()
                u.username = reg.email
                u.email = reg.email
                u.postcode = reg.postcode
                u.kind = reg.kind
                u.interestedIn = reg.interestedIn
                u.userform = reg.userform
                u.dc_stamp = datetime.now()
                u.save()

                # remove the approved email from registration
                reg.userform = None
                reg.save()
                reg.delete()

                return u
            else:
                raise ValidationError("Registrační odkaz vypršel, nechte si poslat nový.")

        else:
            # multiple tokens, which should not happen
            raise Exception("Multiple records with the same token.")

        return None

def sendLoginToken(user):
    """
    Generate and send a token to the user.
    """

    emailToken = get_random_string(120)

    user.emailToken = emailToken
    user.etStamp = datetime.now()
    user.save()

    emailSubj = 'Piráti - nalodění - přihlašovací odkaz'
    emailBody = """\
Dobrý den,

níže zasíláme přihlašovací odkaz do aplikace Pirátů "Nalodění":

{baseUrl}/prihlaseni/?t={emailToken}

Přihlásíte se kliknutím na odkaz, nebo jeho překopírováním do prohlížeče internetových stránek.
Odkaz je možné použít pouze jednou, v případě potřeby si nechte zaslat nový odkaz.

S pozdravem
Piráti
"""

    send_mail(
        emailSubj,
        emailBody.format(
            emailToken=emailToken,
            baseUrl=appSettings.BASE_URL),
        "nalodeni@pirati.cz",
        [user.email],   # email to ...
        fail_silently=False,
    )
            

def sendRegisterToken(f_email):
    """
    Generate and send a token to the registered email.
    """

    #   Check for user non-existence with this email
    f_email = f_email.strip().lower()
    rslt = models.AppUser.objects.filter(email__iexact=f_email)
    if len(rslt) == 0:
        pass
    elif len(rslt) == 1:
        # send login token instead
        return sendLoginToken(rslt[0])
    else:
        logger.error("More AppUser objects with the same email.")
        return


    # check registrations
    rslt = models.AppRegEmail.objects.filter(email__iexact=f_email)
    if len(rslt) == 1:
        rt = rslt[0]
    elif len(rslt) == 0:
        rt = models.AppRegEmail()
        rt.email = f_email
    else:
        logger.error("More AppRegEmail objects with the same email.")
        return

    sendRegisterTokenReg(rt)


def sendRegisterTokenReg(rt):
    """ Send registration email to an already existing AppRegEmail instance 'rt'. """
    # create token
    emailToken = get_random_string(120)

    rt.emailToken = emailToken
    rt.etStamp = datetime.now()
    rt.save()

    emailSubj = 'Piráti - nalodění - registrační odkaz'
    emailBody = """\
Dobrý den,

níže Vám zasíláme registrační odkaz do aplikace Pirátů "Nalodění":

{baseUrl}/prihlaseni/?t={emailToken}

V registraci pokračujte kliknutím na odkaz, nebo jeho překopírováním do prohlížeče internetových stránek.
Odkaz je možné použít pouze jednou, v případě potřeby si nechte zaslat nový odkaz.

S pozdravem
Piráti
"""

    send_mail(
        emailSubj,
        emailBody.format(
            emailToken=emailToken,
            baseUrl=appSettings.BASE_URL),
        "nalodeni@pirati.cz",
        [rt.email],   # email to ...
        fail_silently=False,
    )
            

def sendEmailContactVerificationToken(user):
    """ Send validation email to user.email_contact. """
    # The wanted new value, save it here so we do not accidentally change it,
    # or someone else through the user-object.
    ecw = user.email_contact

    if ecw is None or ecw == "":
        raise ValidationError("Chybí kontaktní email.")

    # ValidationError se odchytava vyse
    validate_email(ecw)

    # create token
    rand_str = get_random_string(120)

    user.email_contact_token = "%s-%s" % ( int(datetime.now().timestamp()), rand_str )
    user.email_contact_verified = False
    user.save()

    sha256hash = hashlib.sha256()
    sha256hash.update(rand_str.encode('utf-8'))
    sha256hash.update(ecw.encode('utf-8'))

    emailToken = sha256hash.hexdigest()
    emailSubj = 'Piráti - nalodění - ověření kontaktního emailu'
    emailBody = """\
Dobrý den,

níže Vám zasíláme odkaz pro ověření platnosti kontaktní e-mailové adresy.
Změna adresy na {email_contact} byla vyžádána v aplikace Pirátů "Nalodění".

Adresu ověříte přihlášením se do aplikace Nalodění
(https://nalodeni.pirati.cz/prihlaseni/) a následným kliknutím na odkaz:

{baseUrl}/ja-pirat/profil/?t={emailToken}

Pokud jste tuto změnu nežádali, můžete tuto zprávu smazat.

S pozdravem
Piráti
"""

    send_mail(
        emailSubj,
        emailBody.format(
            email_contact = ecw,
            emailToken=emailToken,
            baseUrl=appSettings.BASE_URL),
        "nalodeni@pirati.cz",
        [ecw],   # email to ...
        fail_silently=False,
    )

    return True
            


#TODO:: osetreni transakci (pro pripad utoku typu DDoS)
