# 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 .qommon import _
from .qommon.misc import simplify
from .qommon.storage import StorableObject
from .qommon import get_cfg
import wcs.qommon.storage as st

from .qommon.substitution import Substitutions, invalidate_substitution_cache

class User(StorableObject):
    _names = 'users'

    name = None
    email = None
    roles = None
    is_admin = False
    anonymous = False
    form_data = None  # dumping ground for custom fields

    verified_fields = None
    name_identifiers = None
    lasso_dump = None

    last_seen = None

    def __init__(self, name = None):
        StorableObject.__init__(self)
        self.name = name
        self.name_identifiers = []
        self.verified_fields = []
        self.roles = []

    def migrate(self):
        changed = False

        if self.roles and 'site-admin' in self.roles:
            self.is_admin = True
            self.roles = [x for x in self.roles if x != 'site-admin']
            changed = True

        if self.roles:
            for role in self.roles:
                if type(role) is int:
                    self.roles = [str(x) for x in self.roles]
                    changed = True
                    break

        if not self.verified_fields:
            self.verified_fields = []

        if changed:
            self.store()

    @invalidate_substitution_cache
    def store(self, *args, **kwargs):
        return super(User, self).store(*args, **kwargs)

    @classmethod
    def get_formdef(cls):
        from .admin.settings import UserFieldsFormDef
        return UserFieldsFormDef()

    @property
    def ascii_name(self):
        return simplify(self.get_display_name(), space=' ')

    def get_display_name(self):
        if self.name:
            return self.name
        if self.email:
            return self.email
        return _('Unknown User')
    display_name = property(get_display_name)

    def get_roles(self):
        return (self.roles or [])

    def set_attributes_from_formdata(self, formdata):
        users_cfg = get_cfg('users', {})

        if formdata.get('email'):
            self.email = formdata.get('email')

        field_email = users_cfg.get('field_email')
        if field_email:
            self.email = formdata.get(field_email)

        field_name_values = users_cfg.get('field_name')
        if type(field_name_values) is str: # it was a string in previous versions
            field_name_values = [field_name_values]

        if field_name_values:
            self.name = ' '.join([formdata.get(x) for x in field_name_values if formdata.get(x)])

    def can_go_in_admin(self):
        return self.is_admin

    def can_go_in_backoffice(self):
        if self.is_admin:
            return True
        if self.anonymous:
            return False
        from .roles import Role
        for role_id in self.roles or []:
            try:
                role = Role.get(role_id)
                if role.allows_backoffice_access:
                    return True
            except KeyError: # role has been deleted
                pass
        return False

    def can_go_in_backoffice_section(self, section):
        authorised_roles = set(get_cfg('admin-permissions', {}).get(section) or [])
        if authorised_roles:
            return bool(set(self.roles or []).intersection(authorised_roles))
        return self.can_go_in_admin()

    def can_go_in_backoffice_forms(self):
        return self.can_go_in_backoffice_section('forms')

    def can_go_in_backoffice_cards(self):
        return self.can_go_in_backoffice_section('cards')

    def can_go_in_backoffice_workflows(self):
        return self.can_go_in_backoffice_section('workflows')

    @classmethod
    def get_available_roles(cls):
        from .roles import get_user_roles
        return get_user_roles()

    def add_roles(self, roles):
        if not self.roles:
            self.roles = []
        self.roles.extend(roles)

    @classmethod
    def get_users_with_role(cls, role_id):
        # this will be slow with the pickle backend as there is no index
        # suitable for Intersects()
        return cls.select([st.Intersects('roles', [str(role_id)])])

    @classmethod
    def get_users_with_name_identifier(cls, name_identifier):
        return cls.select([st.Intersects('name_identifiers', [name_identifier])])

    @classmethod
    def get_users_with_email(cls, email):
        return cls.select([st.Equal('email', email)])

    def get_substitution_variables(self, prefix='session_'):
        d = {
            prefix+'user': self,
            prefix+'user_display_name': self.display_name,
            prefix+'user_email': self.email,
        }
        formdef = self.get_formdef()
        if formdef:
            from .formdata import get_dict_with_varnames
            data = get_dict_with_varnames(formdef.fields, self.form_data)
            for k, v in data.items():
                d[prefix+'user_'+k] = v

        d[prefix + 'user_admin_access'] = self.can_go_in_admin()
        d[prefix + 'user_backoffice_access'] = self.can_go_in_backoffice()
        for i, name_identifier in enumerate(self.name_identifiers):
            d[prefix + 'user_name_identifier_%d' % i] = name_identifier
        return d

    @classmethod
    def get_substitution_variables_list(cls, prefix='session_'):
        formdef = cls.get_formdef()
        if not formdef:
            return []
        variables = []
        for field in formdef.fields:
            # we only advertise fields with a varname, as they can be
            # considered stable
            if field.varname:
                variables.append((
                        _('User'), prefix+'user_var_'+field.varname,
                        _('Session User Field: %s') % field.label))
        return variables

    def __getattr__(self, attr):
        if attr in self.__dict__:
            return self.__dict__[attr]

        formdef = self.get_formdef()
        if formdef:
            # lookup based on field varname
            field = [x for x in formdef.fields if x.varname == attr]
            if not field:  # lookup on field id
                field = [x for x in formdef.fields if str(x.id) == '_' + attr]
            if field:
                if 'form_data' not in self.__dict__:
                    return None
                return self.__dict__['form_data'].get(field[0].id)

        if attr[0] == 'f' and (self.__dict__['form_data'] and attr[0] == 'f' and
                self.__dict__['form_data']):
            return self.__dict__['form_data'].get(attr[1:])

        raise AttributeError()

    def get_json_export_dict(self):
        data = {
            'id': self.id,
            'name': self.display_name,
        }
        if self.email:
            data['email'] = self.email
        if self.name_identifiers:
            data['NameID'] = self.name_identifiers
        return data

    # django-compatibility methods, useful in templates
    def is_anonymous(self):
        return self.anonymous

    def is_authenticated(self):
        return not(self.anonymous)

    def get_full_name(self):
        return self.display_name


Substitutions.register('session_user_display_name', category=N_('User'), comment=N_('Session User Display Name'))
Substitutions.register('session_user_email', category=N_('User'), comment=N_('Session User Email'))
Substitutions.register_dynamic_source(User)
