# -*- encoding: utf-8 -*-

import sys      # zjistovani jmen funkci
import urllib
import time
import re
import random
import json
import hashlib
from datetime import date, datetime       # timeSlices
from collections import OrderedDict

import django
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import redirect, resolve_url
from django.template import Template, RequestContext, loader
from django.shortcuts import render
import django.contrib.auth as auth
from django.contrib.auth.decorators import login_required
from django.urls import reverse  
from django.views.decorators.csrf import ensure_csrf_cookie
from django import forms
from django.forms import ModelForm
from django.contrib import messages
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import pgettext, pgettext_lazy
from django.db import transaction
from django.db.models import F

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

from django.core.cache import cache
import logging

from django.conf import settings as appSettings

from . import models
from . import forms
from . import auth as nalodeni_auth

# Logger instance
logger = logging.getLogger(__name__)


def about(request):

    if not request.user.is_anonymous:
        return HttpResponseRedirect('/ja-pirat/')

    template = 'about.html'
    context = {
        }

    return render(request, template, context)


def page_eurovolby_2019(request, reg_ok=False):

    if request.method == "POST" and not reg_ok:
        form = forms.Euro2019InterestForm(request.POST)

        f_email = request.POST.get('email', None)

        try:
            validate_email(f_email)
            f_email = f_email.lower().strip()   # force lowercase
        except ValidationError as e:
            f_email = None
            messages.error(request, "Zadali jste neplatnou emailovou adresu.")

        if f_email:
            try:
                ## user exists, send login
                eui = models.Euro2019Interest.objects.get(email__iexact=f_email)
                messages.warning(request, 'Zadaná e-mailová adresa již byla registrována.')
            except models.Euro2019Interest.DoesNotExist:
                ## user not found
                eui = None
            
        if f_email and not eui:
            if form.is_valid():
                form.save()
                return redirect("nalodeni:page_eurovolby_2019_ok")
            else:
                messages.error(request, "Formulář s kontakními údaji níže obsahuje chyby, prosím, opravte je.")

    elif request.method == "GET":
        form = forms.Euro2019InterestForm()

    context = {
        'form' : form,
        'reg_ok' : reg_ok,
        }
    template = 'page_eurovolby_2019.html'

    return render(request, template, context)



@ensure_csrf_cookie
@transaction.atomic
def join_pirates(request):
    template = 'join_pirates.html'

    form = forms.AppRegEmailForm()

    if request.method == "POST":
        form = forms.AppRegEmailForm(request.POST)

        f_email = request.POST.get('email', None)

        try:
            validate_email(f_email)
            f_email = f_email.lower().strip()   # force lowercase
        except ValidationError as e:
            f_email = None
            messages.error(request, "Zadali jste neplatnou emailovou adresu.")

        if f_email:
            try:
                ## user exists, send login
                user = models.AppUser.objects.get(email__iexact=f_email)
                request.session['page_prihlaseni'] = { 'email' : f_email }
                request.session.save()

                messages.error(request, 'Zadaná e-mailová adresa již existuje, nelze ji registrovat opakovaně. ')
                messages.error(request, 'Pokud je tato adresa vaše, můžete si nechat zaslat '
                        + '<a href="/prihlaseni/">přihlašovací odkaz</a>.', extra_tags='safe'
                        )

            except models.AppUser.DoesNotExist:
                ## user not found
                user = None
            
        if f_email and not user:
            cnt = models.AppRegEmail.objects.all().count()
            try:
                ## user does not exist, try registration
                appReg = models.AppRegEmail.objects.get(email__iexact=f_email)
                form = forms.AppRegEmailForm(initial={'email':f_email})

                if appReg.emailToken is None:
                    # neni odeslana zprava
                    messages.info(request, 'Registrace pro tuto e-mailovou adresu již existuje. Přihlašovací údaje '
                                            + 'zašleme e-mailem po potvrzení koordinátorem.')
                else:
                    # registracni zprava jiz odeslana
                    if cnt >= int(appSettings.APP_REG_LIMIT_SOFT) or cnt >= int(appSettings.APP_REG_LIMIT_HARD):
                        messages.info(request, 'Registrační zpráva již byla odeslána. Pokud nedorazila,'
                                                + 'koordinátor vám po vypršení její platnosti zašle novou.')
                    else:
                        nalodeni_auth.sendRegisterTokenReg(appReg)
                        messages.info(request, 'Na zadanou emailovou adresu jsme vám zaslali potvrzovací odkaz.')

            except models.AppRegEmail.DoesNotExist:
                # create new registration
                if form.is_valid():
                    f_email = form.cleaned_data['email']

                    # attack defense system
                    if cnt >= int(appSettings.APP_REG_LIMIT_HARD):
                        messages.error(request, 'Vaše registrace NEBYLA přijata z důvodu velkého počtu nezpracovaných '
                                                + 'registrací. Jedná se o ochranu před zneužitím registračního systému. '
                                                + 'Opakujte Vaši registraci později, nebo využijte registraci přes '
                                                + 'centrální pirátskou identitu (SSO) níže.'
                                                )
                        logger.critical('AppRegEmail hard limit reached - too many '
                                        + 'unconfirmed email registrations. '
                                        + 'Not accepting any more registrations now.')

                    elif cnt >= int(appSettings.APP_REG_LIMIT_SOFT):
                        messages.info(request, 'Vaše registrace byla úspěšna. Přihlašovací údaje '
                                                + 'Vám zašleme emailem po potvrzení koordinátorem. '
                                                )
                        logger.critical('AppRegEmail soft limit reached - approve registrations to send. '
                                        + 'login links to the users.'
                                        )

                        # create registration without sending email
                        rt = form.instance
                        rt.dc_stamp = datetime.now()
                        #rt.etStamp = datetime.now()
                        rt.save()

                        request.session['fillFormFor'] = rt.id
                        request.session.save()

                        return redirect("nalodeni:dotaznik2")

                    else:
                        rt = form.instance
                        rt.dc_stamp = datetime.now()
                        #rt.etStamp = datetime.now()
                        rt.save()

                        nalodeni_auth.sendRegisterTokenReg(rt)
                        messages.info(request, 'Registrace byla úspěšná. Na Vaši emailovou adresu '
                                + 'jsme Vám zaslali potvrzovací odkaz.')

                        request.session['fillFormFor'] = rt.id
                        request.session.save()

                        return redirect("nalodeni:dotaznik2")

    elif request.method == "GET":

        if 'page_join_pirates' in request.session and 'email' in request.session['page_join_pirates']:
            fs_email = request.session['page_join_pirates']['email']
            del request.session['page_join_pirates']['email']
            request.session.save()

            try:
                validate_email(fs_email)
                fs_email = fs_email.lower().strip()   # force lowercase
            except ValidationError as e:
                fs_email = ''
                messages.error(request, "Zadali jste neplatnou emailovou adresu.")

            form.fields['email'].initial = fs_email

    context = {
        'AUTH_AVAIL_IDP' : appSettings.AUTH_AVAIL_IDP,
        'form' : form,
        }

    return render(request, template, context)


@ensure_csrf_cookie
@transaction.atomic
def follow_pirates(request):
    template = 'follow_pirates.html'

    if request.method == "POST":
        form = forms.AppRegFollowEmailForm(request.POST)

        f_email = request.POST.get('email', None)

        try:
            validate_email(f_email)
            f_email = f_email.lower().strip()   # force lowercase
        except ValidationError as e:
            f_email = None
            messages.error(request, "Zadali jste neplatnou emailovou adresu.")

        if f_email:
            try:
                ## user exists, send login
                user = models.AppUser.objects.get(email__iexact=f_email)
                request.session['page_prihlaseni'] = { 'email' : f_email }
                request.session.save()

                messages.error(request, 'Zadaná e-mailová adresa již existuje, nelze ji registrovat opakovaně. ')
                messages.error(request, 'Pokud je tato adresa vaše, můžete si nechat zaslat '
                        + '<a href="/prihlaseni/">přihlašovací odkaz</a>.', extra_tags='safe'
                        )

            except models.AppUser.DoesNotExist:
                ## user not found
                user = None
            
        if f_email and not user:
            cnt = models.AppRegEmail.objects.all().count()
            print(cnt, user)
            try:
                ## user does not exist, try registration
                appReg = models.AppRegEmail.objects.get(email__iexact=f_email)
                form = forms.AppRegFollowEmailForm(initial={'email':f_email})

                if appReg.emailToken is None:
                    # neni odeslana zprava
                    messages.info(request, 'Registrace pro tuto e-mailovou adresu již existuje. Přihlašovací údaje '
                                            + 'zašleme e-mailem po potvrzení koordinátorem.')
                else:
                    # registracni zprava jiz odeslana
                    if cnt >= int(appSettings.APP_REG_LIMIT_SOFT) or cnt >= int(appSettings.APP_REG_LIMIT_HARD):
                        messages.info(request, 'Registrační zpráva již byla odeslána. Pokud nedorazila,'
                                                + 'koordinátor vám po vypršení její platnosti zašle novou.')
                    else:
                        nalodeni_auth.sendRegisterTokenReg(appReg)
                        messages.info(request, 'Na zadanou emailovou adresu jsme vám zaslali potvrzovací odkaz.')

            except models.AppRegEmail.DoesNotExist:
                # create new registration
                if form.is_valid():
                    f_email = form.cleaned_data['email']

                    # attack defense system
                    if cnt >= int(appSettings.APP_REG_LIMIT_HARD):
                        messages.error(request, 'Vaše registrace NEBYLA přijata z důvodu velkého počtu nezpracovaných '
                                                + 'registrací. Jedná se o ochranu před zneužitím registračního systému. '
                                                + 'Opakujte Vaši registraci později, nebo využijte registraci přes '
                                                + 'centrální pirátskou identitu (SSO) níže.'
                                                )
                        logger.critical('AppRegEmail hard limit reached - too many '
                                        + 'unconfirmed email registrations. '
                                        + 'Not accepting any more registrations now.')

                    elif cnt >= int(appSettings.APP_REG_LIMIT_SOFT):
                        messages.info(request, 'Vaše registrace byla úspěšna. Přihlašovací údaje '
                                                + 'Vám zašleme emailem po potvrzení koordinátorem. '
                                                )
                        logger.critical('AppRegEmail soft limit reached - approve registrations to send. '
                                        + 'login links to the users.'
                                        )

                        # create registration without sending email
                        rt = form.instance
                        rt.dc_stamp = datetime.now()
                        #rt.etStamp = datetime.now()
                        rt.save()

                        request.session['fillFormFor'] = rt.id
                        request.session.save()

                        return redirect("nalodeni:dotaznik_follow")

                    else:
                        rt = form.instance
                        rt.dc_stamp = datetime.now()
                        #rt.etStamp = datetime.now()
                        rt.save()

                        nalodeni_auth.sendRegisterTokenReg(rt)
                        messages.info(request, 'Registrace byla úspěšná. Na Vaši emailovou adresu '
                                + 'jsme Vám zaslali potvrzovací odkaz.')

                        request.session['fillFormFor'] = rt.id
                        request.session.save()

                        return redirect("nalodeni:dotaznik_follow")

    elif request.method == "GET":
        form = forms.AppRegFollowEmailForm()

    context = {
        'form' : form,
        }

    return render(request, template, context)


def paluby(request):

    template = 'paluby.html'
    context = {
        }

    return render(request, template, context)

def posadky(request):

    template = 'posadky.html'
    context = {
        }

    return render(request, template, context)

@login_required(login_url="/prihlaseni")
def ja_pirat(request):

    template = 'ja_pirat.html'
    context = {
        }

    return render(request, template, context)


@ensure_csrf_cookie
@transaction.atomic
def prihlaseni(request):
    """
    Uživatel zadá email.

    Pokud ještě není registrován, tak:
        1. je přihlášen a má možnost vyplnit profil,
        2. po vyplnění profilu mu bude zaslán ověřovací email.
        3. v DB bude poznačeno, kdy bylo věření emailem provedeno,
            případně jestli vůbec.
        4. do doby, kdy se odhlásí (tj. první login, max. 30 minut),
            má možnost ještě profil editovat, případně se věnovat
            dalším činnostem.
            
    Některé činnosti budou přístupné pouze uživatelům s ověřeným emailem.

    Pokud je uživatel již registrován (s nebo bez ověřeného emailu),
    je mu zaslán přihlašovací odkaz na email. Použitím odkazu bude uživatel přihlášen.


    """

    if not request.user.is_anonymous:
        return HttpResponseRedirect('/ja-pirat/')

    fs_email = ''

    # user wants to register or login
    if request.method == "POST":
        f_email = request.POST.get('f_adresa',None)

        try:
            validate_email(f_email)
            f_email = f_email.lower().strip()
        except ValidationError as e:
            f_email = None
            messages.error(request, "Zadali jste neplatnou emailovou adresu.")

        if f_email:
            try:
                ## user exists, send login
                user = models.AppUser.objects.get(email__iexact=f_email)
                nalodeni_auth.sendLoginToken(user)
                messages.info(request, 'Poslali jsme Vám email s přihlašovacím odkazem. '
                                        + 'Mrkněte do poštovní schránky ... ')

            except models.AppUser.DoesNotExist:
                ## user not found, register new user ?

                request.session['page_join_pirates'] = { 'email' : f_email }
                request.session.save()

                messages.error(request, 'Zadaná e-mailová adresa neexistuje. Můžete se zaregistrovat '
                        + '<a href="/jdu-do-toho/">zde</a>.', extra_tags='safe'
                        )


    elif request.method == "GET":

        emailToken = request.GET.get('t', None)
        user = None

        # process the link from email
        if emailToken:
            try:
                user = auth.authenticate(request, emailToken=emailToken)
            except ValidationError as e:
                messages.error(request, e.message)
            except Exception as e:
                messages.error(request, "Proces se nezdařil, chyba byla oznámena správci.")
                logger.error("Nastala chyba pri zpracovani registracniho / login tokenu: %s " % e)

            if user is not None:
                # A backend authenticated the credentials
                auth.login(request, user)

                if user.postcode is None or user.district is None:
                    messages.info(request, "Prosím, doplňte údaje v profilu.")
                    return HttpResponseRedirect('/ja-pirat/profil/')

                return HttpResponseRedirect('/ja-pirat/')
            else:
                # No backend authenticated the credentials
                pass

        # predvyplnit emailovou adresu, pokud se uzivatel proklikl z registrace
        if 'page_prihlaseni' in request.session and 'email' in request.session['page_prihlaseni']:
            fs_email = request.session['page_prihlaseni']['email']
            del request.session['page_prihlaseni']['email']
            request.session.save()

            try:
                validate_email(fs_email)
                fs_email = fs_email.lower().strip()   # force lowercase
            except ValidationError as e:
                fs_email = ''
                messages.error(request, "Zadali jste neplatnou emailovou adresu.")
        


    template = 'prihlaseni.html'
    context = {
        'AUTH_AVAIL_IDP' : appSettings.AUTH_AVAIL_IDP,
        'fs_email' : fs_email,
        }

    return render(request, template, context)



@login_required(login_url="/prihlaseni")
def email_vizitka(request):
    """ HTML vizitka pro emaily """
    if request.method == "POST":
        form = forms.EmailVizitkaForm(request.POST)
    else:
        form = forms.EmailVizitkaForm(
            initial={'name': '%s %s' % (request.user.firstName, request.user.lastName) }
        )


    template = 'nastenka/email_vizitka.html'
    context = {
        'form' : form,
        }

    return render(request, template, context)



@login_required(login_url="/prihlaseni")
@transaction.atomic
def profil(request):
    if request.user.ssoUid:
       _form = forms.AppUserSsoForm
    else:
       _form = forms.AppUserForm

    # TODO :: check and enforce DB transaction to prevent race-condition attacks
    if request.method == "GET":
        if request.GET.get('doConsent', None) is not None:
            request.user.dc_stamp = datetime.now()
            request.user.save()

            return HttpResponseRedirect('/ja-pirat/profil/')

        emailToken = request.GET.get('t', None)
        if emailToken:
            # user token from DB
            ut = request.user.email_contact_token
            if ut is None:
                messages.error(request, "Špatný ověřovací token.")
                return redirect('nalodeni:profil')
            ut = ut.split('-')
            if ( (datetime.now() - datetime.utcfromtimestamp(int(ut[0]))
                    ).total_seconds() ) > 24*3600:
                messages.error(request, "Potvrzovací token kontaktního emailu vypršel.")
                request.user.email_contact_token = None
                request.user.email_contact_verified = False
            else:
                # hash the verified email_contact with token
                hash_tae = hashlib.sha256()
                hash_tae.update(ut[1].encode('utf-8'))
                hash_tae.update(request.user.email_contact.encode('utf-8'))

                if emailToken.strip() == hash_tae.hexdigest():
                    messages.info(request, "Kontaktní e-mailová adresa úspěšně ověřena.")
                    request.user.email_contact_token = None
                    request.user.email_contact_verified = True
                    request.user.ts_for_ldap_sync = datetime.now()

                    request.user.save()
                    return redirect('nalodeni:ja_pirat')
                else:
                    messages.error(request, "Špatný ověřovací token.")
                    request.user.email_contact_token = None
                    request.user.email_contact_verified = False
            request.user.save()
            return redirect('nalodeni:profil')

            
        # create edit form
        form = _form(instance=request.user)

    elif request.method == "POST":
        form = _form(request.POST, instance=request.user)
        email_contact_orig = request.user.email_contact
        with request.user.audit_context(request.user) as audit:
            if form.is_valid():
                email_contact_changed = email_contact_orig != form.instance.email_contact

                if email_contact_changed:
                    form.instance.email_contact_verified = False
                form.save()


                if (form.instance.email_contact != None
                        and not form.instance.email_contact_verified):
                    try:
                        nalodeni_auth.sendEmailContactVerificationToken(form.instance)

                        messages.info(request,
                            "Potvrzovací email pro nastavení kontaktní adresy byl odeslán.")
                    except ValidationError as e:
                        messages.error(request, "Kontaktní email není správně vyplněn.")
                    except Exception as e:
                        logger.error("Chyba pri odesilani email_contact potvrzeni: %s" % e)
                        messages.error(request,
                            "Potvrzovací email pro změnu kontaktní adresy " +
                            "se nepodařilo odeslat.")

                messages.info(request, "Údaje byly uloženy.")
                return redirect('nalodeni:ja_pirat')

            else:
                messages.error(request, "Opravte prosím chyby v zadání.")
                raise audit.DataNotSavedException
    else:
        form = None

    template = 'profil.html'
    context = {
            'form' : form,
            'AUTH_SERVER' : appSettings.AUTH_SERVER,
        }

    return render(request, template, context)



@login_required(login_url="/prihlaseni")
@transaction.atomic
def dotaznik(request):
    _form = forms.UserFormForm

    if request.user.userform is None:
        f = models.UserForm()
        f.save()
        
        request.user.userform = f
        request.user.save()

    # TODO :: check and enforce DB transaction to prevent race-condition attacks
    if request.method == "GET":
        # create edit form
        form = _form(instance=request.user.userform)

    elif request.method == "POST":
        with request.user.userform.audit_context(request.user) as audit:
            form = _form(request.POST, instance=request.user.userform)
            if form.is_valid():
                form.save()

                messages.info(request, "Údaje byly uloženy.")
                return redirect('nalodeni:ja_pirat')

            else:
                messages.error(request, "Opravte prosím chyby v zadání.")
                raise audit.DataNotSavedException
    else:
        form = None

    template = 'dotaznik.html'
    context = {
            'form' : form,
        }

    return render(request, template, context)

@transaction.atomic
def dotaznik2(request):
    _form = forms.UserFormForm

    if 'fillFormFor' in request.session:
        f = models.AppRegEmail.objects.get(pk=request.session['fillFormFor'])
        uf = f.userform

        if uf is None:
            uf = models.UserForm()
            uf.save()
            f.userform = uf
            f.save()
    else:
        return redirect('nalodeni:join_pirates')

    # TODO :: check and enforce DB transaction to prevent race-condition attacks
    if request.method == "GET":
        # create edit form
        form = _form(instance=uf)

    elif request.method == "POST":
        form = _form(request.POST, instance=uf)
        if form.is_valid():
            form.save()

            messages.info(request, "Údaje byly uloženy.")
            del request.session['fillFormFor']
            return redirect('nalodeni:join_pirates')

        else:
            messages.error(request, "Opravte prosím chyby v zadání.")
    else:
        form = None

    template = 'dotaznik.html'
    context = {
            'form' : form,
        }

    return render(request, template, context)


@transaction.atomic
def dotaznik_follow(request):
    _form = forms.UserFollowFormForm

    if 'fillFormFor' in request.session:
        f = models.AppRegEmail.objects.get(pk=request.session['fillFormFor'])
        uf = f.userform

        if uf is None:
            uf = models.UserForm()
            uf.save()
            f.userform = uf
            f.save()
    else:
        return redirect('nalodeni:follow_pirates')

    # TODO :: check and enforce DB transaction to prevent race-condition attacks
    if request.method == "GET":
        # create edit form
        form = _form(instance=uf)

    elif request.method == "POST":
        form = _form(request.POST, instance=uf)
        if form.is_valid():
            form.save()

            messages.info(request, "Údaje byly uloženy.")
            del request.session['fillFormFor']
            return redirect('nalodeni:follow_pirates')

        else:
            messages.error(request, "Opravte prosím chyby v zadání.")
    else:
        form = None

    template = 'dotaznik_follow.html'
    context = {
            'form' : form,
        }

    return render(request, template, context)


