# -*- coding: utf-8 -*-
# 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 3 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, see <http://www.gnu.org/licenses/>.
"""
kallithea.controllers.admin.auth_settings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

pluggable authentication controller for Kallithea

This file was forked by the Kallithea project in July 2014.
Original author and date, and relevant copyright and licensing information is below:
:created_on: Nov 26, 2010
:author: akesterson
"""

import logging
import traceback

import formencode.htmlfill
from tg import request
from tg import tmpl_context as c
from tg.i18n import ugettext as _
from webob.exc import HTTPFound

from kallithea.controllers import base
from kallithea.lib import auth_modules, webutils
from kallithea.lib.auth import HasPermissionAnyDecorator, LoginRequired
from kallithea.lib.webutils import url
from kallithea.model import db, meta
from kallithea.model.forms import AuthSettingsForm


log = logging.getLogger(__name__)


class AuthSettingsController(base.BaseController):

    @LoginRequired()
    @HasPermissionAnyDecorator('hg.admin')
    def _before(self, *args, **kwargs):
        super(AuthSettingsController, self)._before(*args, **kwargs)

    def __load_defaults(self):
        c.available_plugins = [
            'kallithea.lib.auth_modules.auth_internal',
            'kallithea.lib.auth_modules.auth_container',
            'kallithea.lib.auth_modules.auth_ldap',
            'kallithea.lib.auth_modules.auth_crowd',
            'kallithea.lib.auth_modules.auth_pam'
        ]
        self.enabled_plugins = auth_modules.get_auth_plugins()
        c.enabled_plugin_names = [plugin.__class__.__module__ for plugin in self.enabled_plugins]

    def __render(self, defaults, errors):
        c.defaults = {}
        c.plugin_settings = {}
        c.plugin_shortnames = {}

        for plugin in self.enabled_plugins:
            module = plugin.__class__.__module__
            c.plugin_shortnames[module] = plugin.name
            c.plugin_settings[module] = plugin.plugin_settings()
            for v in c.plugin_settings[module]:
                fullname = "auth_%s_%s" % (plugin.name, v["name"])
                if "default" in v:
                    c.defaults[fullname] = v["default"]
                # Current values will be the default on the form, if there are any
                setting = db.Setting.get_by_name(fullname)
                if setting is not None:
                    c.defaults[fullname] = setting.app_settings_value
        if defaults:
            c.defaults.update(defaults)

        # we want to show , separated list of enabled plugins
        c.defaults['auth_plugins'] = ','.join(c.enabled_plugin_names)

        log.debug('defaults: %s', defaults)
        return formencode.htmlfill.render(
            base.render('admin/auth/auth_settings.html'),
            defaults=c.defaults,
            errors=errors,
            prefix_error=False,
            encoding="UTF-8",
            force_defaults=False)

    def index(self):
        self.__load_defaults()
        return self.__render(defaults=None, errors=None)

    def auth_settings(self):
        """POST create and store auth settings"""
        self.__load_defaults()
        log.debug("POST Result: %s", dict(request.POST))

        # First, parse only the plugin list (not the plugin settings).
        _auth_plugins_validator = AuthSettingsForm([]).fields['auth_plugins']
        try:
            new_enabled_plugins = _auth_plugins_validator.to_python(request.POST.get('auth_plugins'))
        except formencode.Invalid:
            # User provided an invalid plugin list. Just fall back to
            # the list of currently enabled plugins. (We'll re-validate
            # and show an error message to the user, below.)
            pass
        else:
            # Hide plugins that the user has asked to be disabled, but
            # do not show plugins that the user has asked to be enabled
            # (yet), since that'll cause validation errors and/or wrong
            # settings being applied (e.g. checkboxes being cleared),
            # since the plugin settings will not be in the POST data.
            c.enabled_plugin_names = [p for p in c.enabled_plugin_names if p in new_enabled_plugins]

        # Next, parse everything including plugin settings.
        _form = AuthSettingsForm(c.enabled_plugin_names)()

        try:
            form_result = _form.to_python(dict(request.POST))
            for k, v in form_result.items():
                if k == 'auth_plugins':
                    # we want to store it comma separated inside our settings
                    v = ','.join(v)
                log.debug("%s = %s", k, str(v))
                setting = db.Setting.create_or_update(k, v)
            meta.Session().commit()
            webutils.flash(_('Auth settings updated successfully'),
                       category='success')
        except formencode.Invalid as errors:
            log.error(traceback.format_exc())
            e = errors.error_dict or {}
            return self.__render(
                defaults=errors.value,
                errors=e,
            )
        except Exception:
            log.error(traceback.format_exc())
            webutils.flash(_('error occurred during update of auth settings'),
                    category='error')

        raise HTTPFound(location=url('auth_home'))
