# vim:spell:spelllang=fr
# -*- encoding: utf-8 -*-

import random

from django.utils.translation import ugettext_lazy as _
import django.forms as forms
from django.core.exceptions import ValidationError
from django.utils.safestring import mark_safe
from django.core.urlresolvers import reverse
from django.core.cache import cache
from django.template import Template, Context
from django.core.mail import send_mail
from django.conf import settings
from django.contrib import messages
from django.contrib.localflavor.fr.forms import FRZipCodeField
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit, Fieldset, ButtonHolder, HTML, Field

from appli_project.appli_socle import models
from appli_project.appli_socle.settings import Parametres
from appli_project.appli_socle.forms import FormulaireAvecRelaie
from appli_project.appli_socle.utils import next_url, envoyer_jeton
from appli_project.appli_socle import views as socle_views


class InscriptionForm(FormulaireAvecRelaie):
    email = forms.EmailField(label=_(u'Votre email'))
    first_name = forms.CharField(label=_(u'Prénom'))
    last_name = forms.CharField(label=_(u'Nom'))
    address = forms.CharField(label=_(u'Adresse personnelle'),
                              widget=forms.Textarea)
    phone = forms.CharField(max_length=20,
                            label=_(u'Téléphone fixe'))
    mobile = forms.CharField(max_length=20,
                             label=_(u'Mobile'))
    job = forms.CharField(max_length=128,
                          label=_(u'Profession'))
    comment = forms.ChoiceField(label=_(u'Comment avez-vous connu le site ?'),
            choices=models.ProfilOffre.COMMENT)

    def __init__(self, *args, **kwargs):
        self.static_next_url = reverse('mes-alertes-offre')
        super(InscriptionForm, self).__init__(*args, **kwargs)

    def clean_email(self):
        '''Vérifie que l'email n'est pas déjà utilisé par un profil offre'''
        email = self.cleaned_data['email']

        if models.ProfilOffre.objects.filter(email=email).exists():
            url = reverse('mot-de-passe-perdu', kwargs=dict(email=email))
            raise ValidationError(mark_safe(_(u'''Cette adresse email est déjà '''
                u'''utilisée. <a href="%s">Peut-être avez vous oublié'''
                u''' votre mot de passe ?</a>''') % url))
        return email

    def save(self):
        email = self.cleaned_data['email']
        url = self.next_url()
        self.jeton = jeton = ''.join([random.SystemRandom().choice('123456789ABCDEFGHJKMNPQRSTUVWXYZ')
            for i in range(8)])
        d = { 'next': url, 'creation': 1 }
        d.update(self.cleaned_data)
        cache.set('inscription_%s' % jeton, d, settings.INSCRIPTION_TIMEOUT)
        jeton_url = self.request.build_absolute_uri(
                reverse('confirmation-email', kwargs=dict(jeton=jeton)))
        confirmation_url = self.request.build_absolute_uri(
                reverse('confirmation-email', kwargs=dict(jeton='')))
        modele, created = models.EmailModele.objects.get_or_create(
                nom='Inscription')
        host = self.request.get_host()
        if created:
            modele.sujet = u'''Confirmation de votre email pour inscription à {{host}}'''
            modele.texte = u'''Vous venez d'inscrire votre email {{email}} sur {{host}}.

Il est nécessaire de confirmer cet email pour que votre compte soit créé.
Pour cela veuillez entrez le code {{ jeton }} sur la page Web suivante:

    {{ confirmation_url }}

ou bien recopier le lien suivant dans la barre d'adresse de votre navigateur:

    {{ jeton_url }}'''
            modele.save()

        variables = Context({ 'email' : email, 'jeton': jeton,
            'jeton_url': jeton_url, 'host': host,
            'confirmation_url': confirmation_url})
        subject = Template(modele.sujet).render(variables)
        body = Template(modele.texte).render(variables)
        send_mail(subject, body, settings.INSCRIPTION_FROM_EMAIL, [email])

    helper = FormHelper()
    helper.form_class = 'form-horizontal'
    helper.layout = Layout(
        'email',
        'first_name',
        'last_name',
        'address',
        'phone',
        'mobile',
        'job',
        'comment',
        'next',
        ButtonHolder(
            Submit('submit', _(u"M'inscrire"), css_class="btn-primary btn"),
            css_class="bouton-group"))


from django.contrib.gis.admin.widgets import OpenLayersWidget
from django.contrib.gis.admin import options

class MapWidget(OpenLayersWidget):
    template = 'appli_offre/openlayers.html'
    geom_type = 'POINT'
    params = {
            'default_lon': 261223.54412843,
            'default_lat': 6251249.4833769,
            'default_zoom': 11,
            'display_wkt': False,
            'geom_type': 'POINT',
            'field_name': 'position_geographique',
            'is_collection': False,
            'scrollable': True,
            'layerswitcher': False,
            'collectiontype': 'None',
            'is_linestring': False,
            'is_polygon': False,
            'is_point': True,
            'num_zoom': 20,
            'max_zoom': False,
            'min_zoom': False,
            'units': 'm',
            'max_resolution': '156543.0339',
            'max_extent': '-20037508,-20037508,20037508,20037508',
            'modifiable': True,
            'mouse_position': True,
            'scale_text': True,
            'map_width': '400',
            'map_height': 400,
            'point_zoom': 11,
            'srid': options.spherical_mercator_srid,
            'display_srid': False,
            'wms_url': 'http://vmap0.tiles.osgeo.org/wms/vmap0',
            'wms_layer': 'basic',
            'wms_name': 'OpenLayers WMS'
            }

class AnnonceForm(forms.ModelForm):
    class Meta:
        model = models.Annonce
        widgets = {
                'position_geographique': MapWidget,
        }
        fields = ('prix_par_mois', 'duree_location', 'surface_en_m2', 'type_de_logement',
                'pays', 'ville', 'type_d_offre', 'position_geographique',
                'photo', 'description')

    class Media:
        js = ('/static/openlayers/OpenLayers.js',)

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request')
        self.helper = FormHelper()
        self.helper.form_class = 'form-horizontal'
        self.prestations = fields = []
        type_de_prestations = models.TypeDePrestation.objects.order_by('ordre')
        for type_de_prestation in type_de_prestations:
            name = 'prestation-%s' % type_de_prestation.id
            if type_de_prestation.type_de_valeur == 'B':
                field = forms.BooleanField(label=type_de_prestation.nom,
                        required=False)
            elif type_de_prestation.type_de_valeur == 'N':
                field = forms.IntegerField(label=type_de_prestation.nom,
                        required=False)
            fields.append((name, field))
        field_names = [x[0] for x in fields]
        super(AnnonceForm, self).__init__(*args, **kwargs)
        self.fields.update(dict(fields))
        self.fields['duree_location'].required = True
        if self.instance.id:
            # modification
            for prestation in self.instance.prestations.all():
                type_de_prestation = prestation.type_de_prestation
                name = 'prestation-%s' % type_de_prestation.id
                if type_de_prestation.type_de_valeur == 'B':
                    self.fields[name].initial = True
                elif type_de_prestation.type_de_valeur == 'N':
                    self.fields[name].initial = prestation.valeur_numerique
            submits = [Submit('modifier-et-publier', _(u'Modifier et publier'), css_class='btn btn-primary'),
                       Submit('modifier', _(u'Modifier')),
                       Submit('supprimer', _(u'Supprimer'))]
        else:
            # création
            submits = [Submit('creer', _(u'Créer'))]
        self.helper.layout = Layout(
                'type_de_logement',
                'prix_par_mois',
                'duree_location',
                'surface_en_m2',
                'pays',
                Field('ville', placeholder=_(u'en région parisienne'), help_text='coin'),
                'type_d_offre',
                Field('position_geographique', css_class='osm-input'),
                'photo',
                'description',
                Fieldset(_(u'Prestations diverses'),
                    HTML(_("""<p class="alert alert-info">Tous ces champs sont facultatifs</p>""")),
                    *field_names),
                ButtonHolder(*submits,
                    css_class='bouton-group'))

    def clean_surface_en_m2(self):
        '''Vérifie que la surface dépasse la surface minimum autorisée'''
        surface_en_m2 = self.cleaned_data['surface_en_m2']
        surface_minimum = Parametres().surface_minimum
        if surface_en_m2 < surface_minimum:
            raise ValidationError(
                    mark_safe(_(u'Pour respecter la <a href="/charte-qualite">charte'
                      u' qualité</a> la surface doit être supérieur à %s '
                      u'm²') % surface_minimum))
        return surface_en_m2

    def clean_photo(self):
        photo = self.cleaned_data['photo']
        if photo and photo.size > 1024*1024:
            raise ValidationError(_(u'La taille des images est limitée à 1Mo'))
        return photo

    def clean(self):
        '''Vérifie que l'annonce respecte le prix maximum au m²'''
        cleaned_data = super(AnnonceForm, self).clean()
        try:
            surface_en_m2 = cleaned_data['surface_en_m2']
            prix_par_mois = cleaned_data['prix_par_mois']
        except KeyError:
            return cleaned_data
        parametres = Parametres()
        prix_au_m2_maximum = parametres.prix_au_m2_maximum
        prix_au_m2 = float(prix_par_mois) / float(surface_en_m2)
        prix_maximum = surface_en_m2 * parametres.prix_au_m2_maximum
        if  prix_au_m2 > prix_au_m2_maximum:
            raise ValidationError(
                    mark_safe(_(u'Pour respecter la <a href="/charte-qualite">charte'
                      u' qualité</a> le prix au m² ne doit pas dépasser {0}'
                      u' €/m². Ici il est de {1:.2f} €/m². Le prix par mois'
                      u' maximum au vu de la surface est de {2} €').format(prix_au_m2_maximum,
                          prix_au_m2,
                          prix_maximum)))
        return cleaned_data

    def save(self):
        self.instance.monnaie = 'EUR'
        self.instance.proprietaire = self.request.user
        # Les profils offre sans validation ont leurs offres validés
        # automatiquement
        if self.request.user.sans_validation:
            if self.instance.etat_validation != 'publiee':
                self.instance.etat_validation = 'valide'
        else:
            if self.instance.etat_validation == 'valide':
                pass
            elif self.instance.etat_validation == 'invalide':
                self.instance.etat_validation = 'attend_validation'
            elif self.instance.etat_validation == 'publiee':
                self.instance.etat_validation = 'attend_validation'
            elif self.instance.etat_validation == 'attend_validation':
                pass
        instance = super(AnnonceForm, self).save()
        new_prestations = []
        for type_de_prestation in models.TypeDePrestation.objects.order_by('ordre'):
            name = 'prestation-%s' % type_de_prestation.id
            if type_de_prestation.type_de_valeur == 'B' \
                    and self.cleaned_data.get(name, False):
                prestation, created = models.Prestation.objects.get_or_create(
                        type_de_prestation=type_de_prestation)
                new_prestations.append(prestation)
            elif type_de_prestation.type_de_valeur == 'N' \
                    and self.cleaned_data.get(name) is not None:
                prestation, created = models.Prestation.objects.get_or_create(
                        type_de_prestation=type_de_prestation,
                        valeur_numerique=self.cleaned_data[name])
                new_prestations.append(prestation)
        instance.prestations = new_prestations
        return instance

class MonEmailForm(FormulaireAvecRelaie):
    email = forms.EmailField(label=_(u'Mon email'))
    url_de_contact = forms.URLField(label=_(u'URL de contact'), required=False,
            help_text=_(u'en option, remplace le mail pour le contact du propriétaire'))
    success_url = '/offre'

    def __init__(self, **kwargs):
        request = kwargs.get('request')
        user = self.user = request.user
        initial = kwargs.setdefault('initial', {})
        initial['email'] = user.email
        if hasattr(user, 'url_de_contact'):
            initial['url_de_contact'] = user.url_de_contact
        self.helper = FormHelper()
        self.helper.form_class = 'form-horizontal'
        self.helper.layout = Layout('email', 'url_de_contact', 'next',
                ButtonHolder(Submit('submit', _(u"Confirmer"))))
        super(MonEmailForm, self).__init__(**kwargs)

    def clean_email(self):
        email = self.cleaned_data.get('email')
        if email and models.ProfilOffre.objects \
                .filter(email=email) \
                .exclude(id=self.request.user.id).exists():
            raise ValidationError(_(u'Cet email est déjà utilisé par un autre compte'))
        return email

    def save(self):
        cleaned_data = self.cleaned_data
        if hasattr(self.user, 'url_de_contact'):
            if self.user.url_de_contact != cleaned_data['url_de_contact']:
                self.user.url_de_contact = cleaned_data['url_de_contact']
                self.user.save()
        email = cleaned_data['email']
        if email != self.user.email:
            jeton = envoyer_jeton(self.request, "Changement d'email",
                    _(u'''Code de confirmation pour le changement d'email sur {{host}}'''),
                    _(u'''\
Vous avez demandé à changer d'email sur {{ host }}. Pour confirmer 
ce changement veuillez entrer le code {{jeton}} sur la page:

   {{ confirmation_url }}

ou bien recopier le lien suivant dans la barre de navigation de votre navigateur

   {{ jeton_url }}'''),
                    { },
                    settings.CHANGE_EMAIL_FROM,
                    cleaned_data['email'],
                    { 'email': email, 'user_id': self.user.id,
                      'change_email': 1, 'next': next_url(self.request, '/')
                    })

            messages.info(self.request, _(u"Vous avez demandé à changer d'email."
                u" Un email a été envoyé a l'email"
                u" %s pour validation. Après validation votre email sera"
                u" changé.") % cleaned_data['email'])
            self.request.logger.info(u"demande un changement d'email "
                u"de %s à %s", self.request.user.email, cleaned_data['email'])
            self.request.logger.info(u"le jeton %s lui a été envoyé", jeton)

class MesAlertesForm(FormulaireAvecRelaie):
    refuse_notif_depublication = forms.BooleanField(
            label=_(u'Je ne souhaite pas recevoir les messages de'
                    u' désactivation automatique des annonces'),
            required=False)
    refuse_notif_alertes = forms.BooleanField(
            label=_(u'Je ne souhaite pas recevoir de notification'
                    u" des périodes d'affluence"),
            required=False)
    refuse_invitations = forms.BooleanField(
            label=_(u"Je ne souhaite pas recevoir d'invitations"),
            required=False)
    success_url = '/offre'


    def __init__(self, **kwargs):
        request = kwargs.get('request')
        user = self.user = request.user
        initial = kwargs.setdefault('initial', {})
        if not socle_views.is_webmaster(user):
            initial['refuse_notif_depublication'] = not user.accepte_notif_depublication
            initial['refuse_notif_alertes'] = not user.accepte_notif_alertes
            initial['refuse_invitations'] = not user.accepte_invitations
        self.helper = FormHelper()
        self.helper.form_class = 'form-horizontal'
        button_holder = ButtonHolder(Submit('submit', _(u"Confirmer")))
        fields = [ 'refuse_notif_depublication',
                'refuse_notif_alertes', 'refuse_invitations', 'next',
                button_holder]
        if socle_views.is_webmaster(user):
            invitations = True
        else:
            invitations = models.Annonce.objects \
                    .filter(proprietaire=user) \
                    .exclude(type_d_offre=1) \
                    .filter(etat_validation__in=['valide', 'publiee']) \
                    .exists() or user.accepte_invitations
        if not invitations:
            fields.remove('refuse_invitations')
        self.helper.layout = Layout(*fields)
        super(MesAlertesForm, self).__init__(**kwargs)
        if not invitations:
            del self.fields['refuse_invitations']

    def save(self):
        cleaned_data = self.cleaned_data
        for other in cleaned_data:
            if other.startswith('refuse_'):
                key = 'accepte_' + other[len('refuse_'):]
                if getattr(self.user, key) == cleaned_data[other]:
                    setattr(self.user, key, not cleaned_data[other])
                    self.request.logger.info(
                            u"met son paramètre %s"
                            u" à la valeur %s",
                            key, getattr(self.user, key))
        self.user.save()
