#
# SchoolTool - common information systems platform for school administration
# Copyright (c) 2012 Shuttleworth Foundation
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
import ldap

import z3c.form
from zope.annotation.interfaces import IAnnotations
from zope.cachedescriptors.property import Lazy
from zope.traversing.browser.absoluteurl import absoluteURL

from schooltool.app.interfaces import ISchoolToolApplication
from schooltool.common import format_message
from schooltool.ldap.interfaces import ILDAPPersonsConfig, ILDAPPersonsClient
from schooltool.ldap.config import ST_APP_LDAP_KEY
from schooltool.ldap.config import decode_ldap_query
from schooltool.ldap.security import LDAPServerDown, BadLDAPCredentials
from schooltool.skin import flourish

from schooltool.ldap import SchoolToolLDAPMessage as _
from schooltool.common import SchoolToolMessage as s_


class LDAPSettingsEdit(flourish.form.Form, z3c.form.form.EditForm):

    fields = z3c.form.field.Fields(ILDAPPersonsConfig)

    label = None
    legend = _('LDAP settings')

    temp_content = None

    def getContent(self):
        if self.temp_content is None:
            app = ISchoolToolApplication(None)
            self.temp_content = ILDAPPersonsConfig(app)
        return self.temp_content

    def update(self):
        z3c.form.form.EditForm.update(self)

    @z3c.form.button.buttonAndHandler(s_('Apply'))
    def handle_edit_action(self, action):
        super(LDAPSettingsEdit, self).handleApply.func(self, action)
        # XXX: hacky sucessful submit check
        if (self.status == self.successMessage or
            self.status == self.noChangesMessage):
            app = ISchoolToolApplication(None)
            annotations = IAnnotations(app)
            annotations[ST_APP_LDAP_KEY] = self.temp_content
            self.request.response.redirect(self.nextURL())

    @z3c.form.button.buttonAndHandler(_('Reset to defaults'), name="reset")
    def handle_reset_to_default(self, action):
        app = ISchoolToolApplication(None)
        annotations = IAnnotations(app)
        annotations[ST_APP_LDAP_KEY] = None
        self.request.response.redirect(
            absoluteURL(self, self.request))

    @z3c.form.button.buttonAndHandler(s_('Cancel'))
    def handle_cancel_action(self, action):
        self.request.response.redirect(self.nextURL())

    def nextURL(self):
        url = absoluteURL(self.context, self.request) + '/settings'
        return url

    def updateActions(self):
        super(LDAPSettingsEdit, self).updateActions()
        self.actions['apply'].addClass('button-ok')
        self.actions['reset'].addClass('button-cancel')
        self.actions['cancel'].addClass('button-cancel')


class LDAPOverview(object):

    @Lazy
    def config(self):
        app = ISchoolToolApplication(None)
        return ILDAPPersonsConfig(app, None)

    @Lazy
    def server_status(self):
        if self.config is None:
            return _('Not configured')
        client = ILDAPPersonsClient(self.config, None)
        if client is None:
            return _('Not configured')
        try:
            client.connect()
        except ldap.LDAPError:
            return _('Bad configuration')
        except BadLDAPCredentials:
            return _('Bad bind DN or password')
        except LDAPServerDown:
            return _('Down')
        finally:
            client.close()
        return _('Active')

    @Lazy
    def users_status(self):
        if self.config is None:
            return None
        client = ILDAPPersonsClient(self.config, None)
        if client is None:
            return None
        n_users = 0
        if not client.queries:
            return _('No client DN queries configured, LDAP SSO disabled.')

        try:
            for attr, query in client.queries:
                base, scope, filterstr = decode_ldap_query(query)
                filterstr = filterstr or '(objectClass=*)'
                users = client.search(base, scope, filter=filterstr)
                n_users += len(list(users))
        except ldap.LDAPError:
            return _('Bad DN query(-ies)')
        except (BadLDAPCredentials, LDAPServerDown):
            return None
        finally:
            client.close()

        return format_message(
            _('${amount} users in LDAP.'),
            mapping={'amount': str(n_users)})


class LDAPSettingsOverview(flourish.page.Content,
                           LDAPOverview):
    pass


class LDAPSettingsView(LDAPSettingsEdit, LDAPOverview):

    mode = z3c.form.interfaces.DISPLAY_MODE
