import logging

from django.contrib import admin
from django.utils.translation import ugettext as _
from django.conf.urls import url
from django.conf import settings
from django.forms import ModelForm
from django import forms
from django.contrib import messages
from django.core.exceptions import ValidationError
try:
    from django.contrib.contenttypes.admin import GenericTabularInline
except ImportError:
    from django.contrib.contenttypes.generic import GenericTabularInline

from authentic2.saml.models import (LibertyProvider, LibertyServiceProvider,
                                    SPOptionsIdPPolicy, LibertyFederation,
                                    KeyValue, LibertySession, SAMLAttribute)

from authentic2.decorators import to_iter
from authentic2.attributes_ng.engine import get_service_attributes

from . import admin_views

logger = logging.getLogger(__name__)

class LibertyServiceProviderInline(admin.StackedInline):
    model = LibertyServiceProvider

class TextAndFileWidget(forms.widgets.MultiWidget):
    def __init__(self, attrs=None):
        widgets = (forms.widgets.Textarea(),
                forms.widgets.FileInput(),)
        super(TextAndFileWidget, self).__init__(widgets, attrs)

    def decompress(self, value):
        return (value, None)

    def value_from_datadict(self, data, files, name):
        # If there is a file input use it
        file = self.widgets[1].value_from_datadict(data, files, name + '_1')
        if file:
            file = file.read(file.size)
        if file:
            value = file
        else:
            value = self.widgets[0].value_from_datadict(data, files, name + '_0')
        return value

    def render(self, name, value, attrs=None):
        if attrs is None:
            attrs = {}
        if isinstance(value, (str, unicode)):
            attrs['rows'] = value.count('\n') + 5
            attrs['cols'] = min(max((len(x) for x in value.split('\n'))), 150)
        return super(TextAndFileWidget, self).render(name, value, attrs)


class LibertyProviderForm(ModelForm):
    metadata = forms.CharField(required=True, widget=TextAndFileWidget,
            label=_('Metadata'))
    public_key = forms.CharField(required=False, widget=TextAndFileWidget,
            label=_('Public key'))
    ssl_certificate = forms.CharField(required=False, widget=TextAndFileWidget,
            label=_('SSL certificate'))
    ca_cert_chain = forms.CharField(required=False, widget=TextAndFileWidget,
            label=_('Certificate chain'))

    class Meta:
        model = LibertyProvider
        fields = [
                'name',
                'slug',
                'ou',
                'entity_id',
                'entity_id_sha1',
                'federation_source',
                'metadata_url',
                'metadata',
                'public_key',
                'ssl_certificate',
                'ca_cert_chain',
        ]


def update_metadata(modeladmin, request, queryset):
    qs = queryset.filter(metadata_url__startswith='https://')
    total = qs.count()
    count = 0
    for provider in qs:
        try:
            provider.update_metadata()
        except ValidationError, e:
            params = {
                    'name': provider,
                    'error_msg': u', '.join(e.messages)
            }
            messages.error(request, _('Updating SAML provider %(name)s failed: '
                '%(error_msg)s') % params)
        else:
            count += 1
    messages.info(request, _('%(count)d on %(total)d SAML providers updated') % {
        'count': count, 'total': total})


class SAMLAttributeInlineForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        service = kwargs.pop('service', None)
        super(SAMLAttributeInlineForm, self).__init__(*args, **kwargs)
        choices = get_service_attributes(service)
        self.fields['attribute_name'].choices = choices
        self.fields['attribute_name'].widget = forms.Select(choices=choices)

    class Meta:
        model = SAMLAttribute
        fields = [
                'name_format',
                'name',
                'friendly_name',
                'attribute_name',
                'enabled',
        ]

class SAMLAttributeInlineAdmin(GenericTabularInline):
    model = SAMLAttribute
    form = SAMLAttributeInlineForm

    def get_formset(self, request, obj=None, **kwargs):
        # add service argument to form constructor
        class NewForm(self.form):
            def __init__(self, *args, **kwargs):
                kwargs['service'] = obj
                super(NewForm, self).__init__(*args, **kwargs)
        kwargs['form'] = NewForm
        return super(SAMLAttributeInlineAdmin, self).get_formset(request, obj=obj, **kwargs)

class LibertyProviderAdmin(admin.ModelAdmin):
    form = LibertyProviderForm
    list_display = ('name', 'ou', 'slug', 'entity_id')
    search_fields = ('name', 'entity_id')
    readonly_fields = ('entity_id','protocol_conformance','entity_id_sha1','federation_source')
    fieldsets = (
            (None, {
                'fields' : ('name', 'slug', 'ou', 'entity_id', 'entity_id_sha1','federation_source')
            }),
            (_('Metadata files'), {
                'fields': ('metadata_url', 'metadata', 'public_key', 'ssl_certificate', 'ca_cert_chain')
            }),
    )
    inlines = [
            LibertyServiceProviderInline,
            SAMLAttributeInlineAdmin,
    ]
    actions = [ update_metadata ]
    prepopulated_fields = {'slug': ('name',)}
    list_filter = (
            'service_provider__sp_options_policy',
            'service_provider__enabled',
    )

    def get_urls(self):
        urls = super(LibertyProviderAdmin, self).get_urls()
        urls = [
            url(r'^add-from-url/$',
                self.admin_site.admin_view(admin_views.AddLibertyProviderFromUrlView.as_view(model_admin=self)),
                name='saml_libertyprovider_add_from_url'),
            ] + urls
        return urls

class LibertyFederationAdmin(admin.ModelAdmin):
    search_fields = ('name_id_content', 'user__username')
    list_display = ('user', 'creation', 'last_modification', 'name_id_content', 'format', 'sp')
    list_filter = ('name_id_format', 'sp')

    def format(self, obj):
        name_id_format = obj.name_id_format
        if name_id_format > 15:
            name_id_format = u'\u2026' + name_id_format[-12:]
        return name_id_format

class SPOptionsIdPPolicyAdmin(admin.ModelAdmin):
    inlines = [ SAMLAttributeInlineAdmin ]
    fields = (
            'name',
            'enabled',
            'prefered_assertion_consumer_binding',
            'encrypt_nameid',
            'encrypt_assertion',
            'authn_request_signed',
            'idp_initiated_sso',
            'default_name_id_format',
            'accepted_name_id_format',
            'ask_user_consent',
            'accept_slo',
            'forward_slo',
            'needs_iframe_logout',
            'iframe_logout_timeout',
            'http_method_for_slo_request',
    )

admin.site.register(SPOptionsIdPPolicy, SPOptionsIdPPolicyAdmin)
admin.site.register(LibertyProvider, LibertyProviderAdmin)

if settings.DEBUG:
    admin.site.register(LibertyFederation, LibertyFederationAdmin)
    admin.site.register(LibertySession)
    admin.site.register(KeyValue)
