# w.c.s. - web application for online forms
# Copyright (C) 2005-2010  Entr'ouvert
#
# 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, see <http://www.gnu.org/licenses/>.

from django.utils.translation import pgettext

from quixote import get_publisher, get_request, get_response, redirect
from quixote.html import TemplateIO, htmltext

from ..qommon import _
from ..qommon.backoffice import BackofficeRootDirectory
from ..qommon.backoffice.menu import html_top

from ..qommon import misc, get_cfg
from ..qommon import errors
from ..qommon.form import *

from wcs.formdef import FormDef

import wcs.admin.categories
import wcs.admin.forms
import wcs.admin.roles
import wcs.admin.settings
import wcs.admin.users
import wcs.admin.workflows

from . import studio
from . import cards
from . import submission
from . import management
from . import data_management


class RootDirectory(BackofficeRootDirectory):
    _q_exports = ['', 'pending', 'statistics', ('menu.json', 'menu_json')]

    forms = wcs.admin.forms.FormsDirectory()
    roles = wcs.admin.roles.RolesDirectory()
    settings = wcs.admin.settings.SettingsDirectory()
    users = wcs.admin.users.UsersDirectory()
    workflows = wcs.admin.workflows.WorkflowsDirectory()
    management = management.ManagementDirectory()
    studio = studio.StudioDirectory()
    cards = cards.CardsDirectory()
    data = data_management.DataManagementDirectory()
    submission = submission.SubmissionDirectory()

    menu_items = [
        ('submission/', N_('Submission')),
        ('management/', N_('Management')),
        ('data/', N_('Cards'), {'check_display_function': studio.is_visible}),
        ('studio/', N_('Studio'), {'check_display_function': studio.is_visible}),
        ('forms/', N_('Forms Workshop'), {'sub': True}),
        ('cards/', N_('Card Models'), {'sub': True, 'check_display_function': studio.is_visible}),
        ('workflows/', N_('Workflows Workshop'), {'sub': True}),
        ('users/', N_('Users'), {'check_display_function': roles.is_visible}),
        ('roles/', N_('Roles'), {'check_display_function': roles.is_visible}),
        ('settings/', N_('Settings')),
    ]

    def _q_traverse(self, path):
        if not hasattr(self, self._q_translate(path[0]) or path[0]):
            try:
                # keep compatibility with previous versions, redirect from
                # legacy URL to new ones under management/
                FormDef.get_by_urlname(path[0], ignore_migration=True)
                url = get_request().get_path_query()
                url = url.replace('/backoffice/', '/backoffice/management/', 1)
                return redirect(url)
            except KeyError:
                pass
        get_response().add_javascript(['jquery.js', 'qommon.js', 'gadjo.js'])
        if path and path[0] == 'categories':
            # legacy /backoffice/categories/<...>, redirect.
            return redirect('/backoffice/forms/' + '/'.join(path))
        return super(RootDirectory, self)._q_traverse(path)

    @classmethod
    def is_accessible(cls, subdirectory):
        # check a backoffice directory is accessible to the current user

        if getattr(get_response(), 'filter', {}) and get_response().filter.get('admin_for_all'):
            # if admin for all is set, access is granted to everything
            return True

        if not get_request().user:
            if get_publisher().user_class.count() == 0:
                # setting up the site, access is granted to settings and users
                # sections
                return subdirectory in ('settings', 'users')
            return False

        user_roles = set(get_request().user.get_roles())
        authorised_roles = set(get_cfg('admin-permissions', {}).get(subdirectory) or [])
        if authorised_roles:
            # access is governed by roles set in the settings panel
            return user_roles.intersection(authorised_roles)

        # if the directory defines a is_accessible method, use it.
        if hasattr(getattr(cls, subdirectory, None), 'is_accessible'):
            return getattr(cls, subdirectory).is_accessible(get_request().user)

        # as a last resort, for the other directories, the user needs to be
        # marked as admin
        return get_request().user.can_go_in_admin()

    def check_admin_for_all(self):
        admin_for_all_file_path = os.path.join(get_publisher().app_dir, 'ADMIN_FOR_ALL')
        if not os.path.exists(os.path.join(admin_for_all_file_path)):
            return False
        admin_for_all_contents = open(admin_for_all_file_path).read()
        if not admin_for_all_contents:
            # empty file, access is granted to everybody
            return True
        if get_request().get_environ('REMOTE_ADDR', '') in admin_for_all_contents.splitlines():
            # if the file is not empty it should contain the list of authorized
            # IP addresses.
            return True
        return False

    def _q_access(self):
        get_response().breadcrumb = [] # reinit, root the breadcrumb in the backoffice
        get_response().breadcrumb.append( ('backoffice/', _('Back Office')) )
        get_response().add_javascript(['jquery.js', 'qommon.admin.js'])
        req = get_request()

        if self.check_admin_for_all():
            get_response().filter['admin_for_all'] = True
            return

        if get_publisher().user_class.count() > 0:
            user = req.user
            if not user:
                raise errors.AccessUnauthorizedError(
                        public_msg = _('Access to backoffice is restricted to authorized persons only. '\
                                       'Please login.'))
            if not user.can_go_in_backoffice():
                raise errors.AccessForbiddenError()
        else:
            # empty site
            if get_cfg('idp'):  # but already configured for IdP
                raise errors.AccessUnauthorizedError()

        get_response().filter['in_backoffice'] = True

    def get_intro_text(self):
        return _('''Welcome.''')

    def generate_header_menu(self, selected=None):
        s = ['<ul id="sidepage-menu">\n']
        for menu_item in self.get_menu_items():
            if not 'icon' in menu_item:
                continue
            if menu_item.get('slug') == selected:
                s.append('<li class="active">')
            else:
                s.append('<li>')
            s.append('<a href="%(url)s" class="icon-%(icon)s">%(label)s</a></li>\n' % menu_item)
        s.append('</ul>\n')
        return ''.join(s)

    def _q_index(self):
        html_top('/')
        r = TemplateIO(html=True)
        r += htmltext('<div class="bo-block"><p>%s</p></div>') % self.get_intro_text()

        menu_items = self.get_menu_items()
        r += htmltext('<ul class="apps">')
        has_studio = self.studio.is_visible()
        for menu_item in menu_items:
            if not 'icon' in menu_item:
                continue
            if menu_item['icon'] == 'studio':
                continue
            r += htmltext('<li class="zone-%(icon)s"><a href="%(url)s">%(label)s</a></li>') % menu_item
        for menu_item in menu_items:
            if 'icon' in menu_item:
                continue
            r += htmltext('<li class="zone-no-icon"><a href="%(url)s">%(label)s</a></li>') % menu_item
        r += htmltext('</ul>')
        r += htmltext('<br class="clear">')

        r += htmltext('<p id="for-more-info"></p>')

        get_response().filter['sidebar'] = str(self.get_sidebar())

        return r.getvalue()

    def get_sidebar(self):
        from ..qommon.admin.menu import get_vc_version
        r = TemplateIO(html=True)

        r += htmltext('<div class="bo-block">')
        r += htmltext('<ul id="sidebar-actions">')
        r += htmltext('<li><a href="management/statistics">%s</a></li>') % _('Global statistics')
        r += htmltext('</ul>')
        r += htmltext('</div>')

        r += htmltext('<div class="bo-block">')
        r += htmltext(_("""<p>This program is free software. You can redistribute
    it and/or modify it under the terms of the
    <a href="http://www.gnu.org/licenses/gpl">GNU General Public License</a>.
    </p>"""))
        version = get_vc_version()
        if version:
            r += htmltext('<p class="version-info">%s %s</p>' % (
                _('Version:'), version))
        r += htmltext('</div>')

        return r.getvalue()

    def menu_json(self):
        return misc.json_response(self.get_menu_items())

    def pending(self):
        # kept as a redirection for compatibility with possible bookmarks
        return redirect('.')

    def statistics(self):
        return redirect('management/statistics')

    def _q_lookup(self, component):
        if component in [str(x[0]).strip('/') for x in self.menu_items]:
            if not self.is_accessible(component):
                raise errors.AccessForbiddenError()
            return getattr(self, component)
        return super(RootDirectory, self)._q_lookup(component)

    def get_menu_items(self):
        if not get_request().user:
            # this could happen if admin-for-all is set, or if it's the first
            # user connecting.
            user_roles = set()
        else:
            user_roles = set(get_request().user.get_roles())
        menu_items = []
        backoffice_url = get_publisher().get_backoffice_url()
        if not backoffice_url.endswith('/'):
            backoffice_url += '/'
        has_studio = self.studio.is_visible()
        for item in self.menu_items:
            if len(item) == 2:
                item = list(item) + [{}]
            k, v, options = item
            slug = k.strip(str('/'))
            if not slug:
                continue
            display_function = options.get('check_display_function')
            if display_function and not display_function(slug):
                continue
            if not self.is_accessible(slug):
                continue
            if callable(v):
                label = v()
            else:
                label = _(v)
            if has_studio:
                if slug == 'forms':
                    label = misc.site_encode(pgettext('studio', 'Forms'))
                elif slug == 'cards':
                    label = misc.site_encode(pgettext('studio', 'Card Models'))
                elif slug == 'workflows':
                    label = misc.site_encode(pgettext('studio', 'Workflows'))
            menu_items.append({
                'label': label,
                'slug': slug,
                'url': backoffice_url + k,
                'sub': (options.get('sub') and has_studio) or False,
            })

            if slug in ('home', 'forms', 'workflows', 'users', 'roles',
                    'categories', 'settings', 'management', 'submission',
                    'studio', 'cards', 'data'):
                menu_items[-1]['icon'] = k.strip('/')
        return menu_items
