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

import datetime

from django.conf.urls.defaults import patterns, url
from django.utils.translation import ugettext_lazy as _
from django.contrib.admin import AdminSite, ModelAdmin
from django.contrib.gis.db.models import GeometryField
from django.contrib.gis import admin
from django.contrib.auth.admin import UserAdmin, GroupAdmin
from django.contrib.auth.models import Group, User
from django.contrib.admin.util import flatten_fieldsets
from django.db.models import ManyToManyField
from django.forms import CheckboxSelectMultiple
from django.http import HttpResponseRedirect
from django.contrib.admin.util import unquote
from django.contrib import messages
from django.conf import settings
from django.contrib import admin as contrib_admin
from django.contrib.admin.widgets import FilteredSelectMultiple
import django.contrib.auth.forms
from django.contrib.admin import RelatedFieldListFilter
from django.contrib.admin import SimpleListFilter


from daterange_filter.filter import DateRangeFilter


import models
import admin_views
from forms import ProfilRechercheCreationForm, InvalidationForm, \
    ValidationForm
import utils
import actions

class AdministrateurGroupListFilter(RelatedFieldListFilter):
    def __init__(self, *args, **kwargs):
        super(AdministrateurGroupListFilter, self).__init__(*args, **kwargs)
        self.lookup_choices = [
                (group.id, group.name) for group in Group.objects \
                        .filter(user__is_staff=True).distinct()
        ]


class LastLoginFilter(SimpleListFilter):
    title = _(u'Dernière connection')

    parameter_name = 'last-login-period'

    def lookups(self, request, model_admin):
        return (
                ('2y', _(u'Il y a plus de deux ans')),
                ('1y', _(u'Il y a plus d\'un ans')),
                ('6m', _(u'Il y a plus de 6 mois')),
        )

    def queryset(self, request, queryset):
        days = {
                '2y': 365*2,
                '1y': 365,
                '6m': 183
        }
        if self.value() in days:
            not_after = datetime.date.today() - datetime.timedelta(days=days[self.value()])
            queryset = queryset.filter(last_login__lte=not_after)
        return queryset


class OverloadAddForm(object):
    def get_fieldsets(self, request, obj=None):
        '''
        Utilise un fieldsets spécifique pour la création, sinon ça bug avec le
        formulaire modifié.
        '''
        if not obj:
            return self.add_fieldsets
        return super(OverloadAddForm, self).get_fieldsets(request, obj)

    def get_form(self, request, obj=None, **kwargs):
        """
        Utilise un formulaire spécifique pour la création (idée emprunté à 
        django.contrib.auth.admin)
        """
        defaults = {}
        if obj is None:
            defaults.update({
                'form': self.add_form,
                'fields': flatten_fieldsets(self.add_fieldsets),
            })
        defaults.update(kwargs)
        return super(OverloadAddForm, self).get_form(request, obj, **defaults)

class ProfilRechercheAdmin(OverloadAddForm, ModelAdmin):
    list_display = ('email', 'cas', 'types_d_offre', 'is_active')
    list_filter = ('is_active', LastLoginFilter, 'type_d_offre','acceptation_cgu','acceptation_charte_qualite')
    readonly_fields = ('email', 'cas', 'acceptation_cgu',
            'acceptation_charte_qualite', 'last_login',)
    fieldsets = (
            (_(u'Identifiants'), { 'fields': (('email', 'cas'),) }),
            (_(u'Attributs'), { 'fields': ('is_active', 'type_d_offre') }),
            (_(u"Conditions d'utilisation"), { 'fields': (('acceptation_cgu',
                'acceptation_charte_qualite'),) }))
    search_fields = ( 'email', 'user_ptr__utilisateur_cas__identifiant' )
    ordering = ('email',)
    add_fieldsets = (
            (None, { 
                'classes': ('wide', ),
                'fields': ('identifiant_cas', ) 
                }), 
    )
    add_form = ProfilRechercheCreationForm
    formfield_overrides = {
            ManyToManyField: { 'widget': CheckboxSelectMultiple },
    }
    actions = [ actions.export_as_csv, actions.desactivate_user, actions.activate_user ]

    def types_d_offre(self, instance):
        return ''.join(map(lambda s: '%s<br>' % s, instance.type_d_offre.values_list('nom', flat=True)))
    types_d_offre.allow_tags = True
    types_d_offre.short_description = _("Types d'offre")

    def cas(self, instance):
        '''
        
        Champ virtuel pour le lien à un utilisateur CAS
        '''
        q = instance.user_ptr.utilisateur_cas.all()
        return q[0].identifiant if q else ''
    cas.short_description = _(u'Identifiant CAS')

    def get_urls(self):
        urls = super(ProfilRechercheAdmin, self).get_urls()
        my_urls = patterns('',
                url(r'^injection/$', admin_views.VueInjection.as_view(), { 'media': self.media }),)
        return my_urls + urls

    def save_model(self, request, obj, form, change):
        res = super(ProfilRechercheAdmin, self).save_model(request, obj, form, change)
        if change:
            if obj.is_active:
                request.logger.info(u"active le profil recherche %s", obj)
            else:
                request.logger.info(u"désactive le profil recherche %s", obj);
        else:
            request.record('nouveau-compte-recherche-manuel',
                    'nouveau compte CAS {cas_user}',
                    cas_user=form.cleaned_data['identifiant_cas'],
                    new_user=obj)
        return res

    def queryset(self, request):
        '''
        Précharge l'object parent ainsi que les éventuels liens avec
        des utilisateurs CAS pour optimiser le nombre de requêtes.
        '''
        qs = super(ProfilRechercheAdmin, self).queryset(request)
        qs = qs.prefetch_related('user_ptr', 'user_ptr__utilisateur_cas')
        return qs

class ProfilOffreAdmin(ModelAdmin):
    list_display = ('email', 'is_active', 'accepte_notif_alertes', 'accepte_notif_depublication', 'accepte_invitations')
    list_filter = (
            LastLoginFilter, 
            ('date_joined', DateRangeFilter),
            'is_active', 
            'sans_validation', 
            'accepte_notif_alertes',
            'accepte_notif_depublication', 
            'accepte_invitations', 
            'acceptation_cgu', 
            'acceptation_charte_qualite')
    search_fields = list_display
    ordering = list_display
    readonly_fields = ('email', 'acceptation_cgu', 'acceptation_charte_qualite',
            'accepte_notif_alertes', 'accepte_notif_depublication', 'accepte_invitations',
            'comment', 'last_login')
    fieldsets = (
            (_(u'Identifiants'), { 'fields': ('email','url_de_contact') }),
            (_(u'Attributs'), { 'fields': ('is_active', 'sans_validation') }),
            (_(u"Conditions d'utilisation"), { 'fields': (('acceptation_cgu',
                'acceptation_charte_qualite'),) }),
            (_(u'Notifications par mail'), { 'fields': 
             (('accepte_notif_alertes', 'accepte_notif_depublication', 'accepte_invitations'),) }),
            (_(u'Informations complémentaires'), { 'fields':
                ('first_name', 'last_name', 'address', 'phone', 'mobile', 'job', 'comment', 'last_login')}))
    actions = [ actions.export_as_csv, actions.desactivate_user, actions.activate_user ]

    def save_model(self, request, obj, form, change):
        if change:
            if obj.is_active:
                request.logger.info(u"active le profil offre %s", obj)
            else:
                request.logger.info(u"désactive le profil offre %s", obj);
        else:
            request.logger.info(u"crée le profil offre %s", obj)
        return super(ProfilOffreAdmin, self).save_model(request, obj, form, change)

class MyOSMGeoAdmin(admin.OSMGeoAdmin):
    default_zoom = 12
    default_lat = 6251249.4833769
    default_lon = 261223.54412843

class ZoneAdmin(admin.OSMGeoAdmin):
    default_zoom = 10
    def formfield_for_dbfield(self, db_field, **kwargs):
        """
        Overloaded from ModelAdmin so that an OpenLayersWidget is used
        for viewing/editing GeometryField.
        """
        if isinstance(db_field, GeometryField):
            request = kwargs.pop('request', None)
            # Setting the widget with the newly defined widget.
            kwargs['widget'] = self.get_map_widget(db_field, request=request)
            return db_field.formfield(**kwargs)
        else:
            return super(ZoneAdmin, self).formfield_for_dbfield(db_field, **kwargs)

    def get_map_widget(self, db_field, request=None):
        try:
            id = int(filter(None, request.path.split('/'))[-1])
        except ValueError:
            id = None
        qs = self.queryset(request).exclude(id=id)
        more_wkt = [ (zone.nom, zone.polygon.transform(900913, clone=True).wkt) for zone in qs]
        cls = super(ZoneAdmin, self).get_map_widget(db_field)
        cls.params['more_wkt'] = more_wkt
        return cls

class AnnonceAdmin(MyOSMGeoAdmin):
    fieldsets = [
            (_('Validation'), { 'fields': ['etat_validation',
                'derniere_publication', 'derniere_validation',
                'derniere_validation_par', 'raison_invalidation',
                'nos_logements' ] } ),
            (_("Champs de l'annonce"), { 'fields': [ 'type_de_logement',
                'proprietaire', 'prix_par_mois', 'duree_location', 'monnaie', 'surface_en_m2',
                'pays', 'ville', 'position_geographique', 'type_d_offre',
                'prestations', 'photo', 'description' ] })]
    readonly_fields = [ 'etat_validation', 'derniere_publication', 'derniere_validation',
            'derniere_validation_par', 'raison_invalidation', 'proprietaire' ]
    addon_readonly_fields = [ 'type_de_logement',
            'prix_par_mois', 'monnaie', 'surface_en_m2',
            'pays', 'ville', 'position_geographique2',
            'prestations', 'photo', 'description' ]
    list_display = [ 'email', 'etat_validation', 'derniere_publication', 'type_d_offre' ]
    list_filter = [ 'etat_validation', 'type_d_offre', 'type_de_logement',
            'nos_logements' ]
    list_select_related = True

    def email(self, instance):
        return instance.proprietaire.email

    def queryset(self, request):
        '''Si l'utilisateur est dans un groupe de validation, on limite les
           annonces visibles à celles qu'il faut valider.
        '''
        qs = super(AnnonceAdmin, self).queryset(request)
        #if request.user.groups.filter(name__contains='validation').exists():
        #    qs = qs.filter(etat_validation='attend_validation')
        return qs

    def get_urls(self):
        urls = super(AnnonceAdmin, self).get_urls()
        my_urls = patterns('',
                url(r'^(?P<object_id>\d+)/valider/$', self.valider),
                url(r'^(?P<object_id>\d+)/invalider/$', self.invalider))
        return my_urls + urls

    def valider(self, request, object_id=None):
        obj = self.get_object(request, unquote(object_id))
        if '_valider' in request.POST:
            obj.valide(request.user)
            request.record('validation-annonce', 'validation de l\'annonce {annonce}',
                    annonce=obj)
            host = request.build_absolute_uri('/')[:-1]
            utils.mail_avec_modele_par_defaut('Annonce valide',
                    {'annonce': obj, 'host':  host },
                    settings.VALIDATION_FROM, obj.proprietaire.email)
            self.message_user(request, _(u'''L'annonce a bien été validée.'''))
            return HttpResponseRedirect('../..')
        return HttpResponseRedirect('..')

    def invalider(self, request, object_id=None):
        obj = self.get_object(request, unquote(object_id))
        if '_invalider' in request.POST:
            if request.POST.get('raison'):
                request.logger.info(u"refuse l'annonce %s avec la raison « %s »",
                    obj.id, request.POST['raison'])
                obj.invalide(request.user, request.POST['raison'])
                request.record('invalidation-annonce', 'invalidation de l\'annonce {annonce} pour la raison {raison}',
                        annonce=obj, raison=request.POST['raison'], annonceur=obj.proprietaire)
                host = request.build_absolute_uri('/')[:-1]
                utils.mail_avec_modele_par_defaut('Annonce invalide',
                        {'annonce': obj, 'host': host,
                            'raison': request.POST['raison'] },
                        settings.VALIDATION_FROM, obj.proprietaire.email)
                self.message_user(request, _(u'''L'annonce a bien été invalidée.'''))
                return HttpResponseRedirect('../..')
            else:
                messages.error(request, _(u'''Vous devez donner une raison pour l'invalidation'''))
        return HttpResponseRedirect('..')

    def position_geographique2(self, instance):
        return instance.osm_wkt()
    position_geographique2.short_description = _(u'Géolocalisation')

    def change_view(self, request, object_id, extra_context=None):
        obj = self.get_object(request, unquote(object_id))

        if obj is not None:
            extra_context = {}
            if obj.etat_validation in ('attend_validation', 'invalide'):
                extra_context['validation'] = 1
            extra_context['validation_form'] = ValidationForm()
            extra_context['invalidation_form'] = InvalidationForm()

        return super(AnnonceAdmin, self).change_view(request, object_id, extra_context=extra_context)

class LocalAdminSite(AdminSite):
    exclude = [ 'is_staff' ]

    def get_urls(self):
        urls = super(LocalAdminSite, self).get_urls()
        new_urls = patterns('',
                url('^parametres/', admin_views.VueParametres.as_view()),
                url('^notifications/', admin_views.VueNotification.as_view()),
                )
        return new_urls + urls

class UserCreationForm(django.contrib.auth.forms.UserCreationForm):
    def __init__(self, *args, **kwargs):
        super(UserCreationForm, self).__init__(*args, **kwargs)
        self.fields['groups'].queryset = Group.objects.exclude(name__contains=u'Profil')

    class Meta:
        fields = ('username', 'groups')
        widgets = { 'groups': FilteredSelectMultiple(_(u'Groupes'), False) }

class AdminUserAdmin(UserAdmin):
    add_form = UserCreationForm
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('username', 'password1', 'password2', 'groups')}
        ),
    )
    fieldsets = (('Informations', { 'fields': ('username', 'password', 'email',) }),
            ('Authorisations', { 'fields': ('is_active', 'groups') }))
    list_display = [ 'id', 'username', 'email', 'is_active', 'group_list' ]
    list_editable = [ 'username', 'email', 'is_active' ]
    list_filter = [ ('groups', AdministrateurGroupListFilter), 'is_active' ]
    prefetch_related = [ 'groups' ]
    actions = [ actions.export_as_csv ]

    def group_list(self, instance):
        return u', '.join(instance.groups.values_list('name', flat=True))
    group_list.short_description = _(u'Groupe(s)')

    def queryset(self, request):
        qs = super(AdminUserAdmin, self).queryset(request)
        return qs.exclude(username__contains='#').filter(is_superuser=False)

    def save_model(self, request, obj, form, change):
        obj.is_staff = True
        obj.save()

class TypeDePrestationAdmin(ModelAdmin):
    list_display = [ 'nom', 'ordre' ]
    list_editable = [ 'ordre' ]

contrib_admin.site = LocalAdminSite()

contrib_admin.site.register(User, UserAdmin)
contrib_admin.site.register(Group, GroupAdmin)
contrib_admin.site.register(models.Administrateur, AdminUserAdmin)
contrib_admin.site.register(models.EmailModele)
contrib_admin.site.register(models.Zone, ZoneAdmin)
contrib_admin.site.register(models.Annonce, AnnonceAdmin)
contrib_admin.site.register(models.TypeOffre)
contrib_admin.site.register(models.TypeDePrestation, TypeDePrestationAdmin)
contrib_admin.site.register(models.ProfilRecherche, ProfilRechercheAdmin)
contrib_admin.site.register(models.ProfilOffre, ProfilOffreAdmin)

import django_journal.admin
import django_journal

class SimpleTagFilter(SimpleListFilter):
    title = _(u'étiquette')
    parameter_name = 'tag'

    def lookups(self, request, model_admin):
        for tag in django_journal.Tag.objects.filter(journal__isnull=False) \
                .distinct().order_by('name'):
            yield (tag.id, tag.name)

    def queryset(self, request, queryset):
        if self.value():
            return queryset.filter(tag__id=self.value())
        return queryset

class TypeOffreFilter(SimpleListFilter):
    title = _(u"type d'offre")
    parameter_name = 'type-offre'

    def lookups(self, request, model_admin):
        for type_d_offre in models.TypeOffre.objects.all():
            yield (type_d_offre.id, type_d_offre.nom)

    def queryset(self, request, queryset):
        if self.value():
            return queryset.for_object(
                    models.TypeOffre.objects.get(id=self.value()),
                    tag='type_offre')
        return queryset

class JournalAdmin(django_journal.admin.JournalAdmin):
    list_filter = (('time', DateRangeFilter),) + \
            tuple(x for x in django_journal.admin.JournalAdmin.list_filter if x != 'tag') + (SimpleTagFilter, TypeOffreFilter)


contrib_admin.site.register(django_journal.Journal, JournalAdmin)

# FIXME: chaîne à traduire dans django-daterange-filter
_('From date')
_('To date')
