from __future__ import absolute_import

import sys, warnings
from repoze.who.plugins.auth_tkt import AuthTktCookiePlugin
from repoze.who.plugins.friendlyform import FriendlyFormPlugin
from repoze.what.middleware import setup_auth as repoze_setup_auth

from bazbase import custom, conversion, structure
from bazbase.benchmark import benchmarking
import bazki

# Identity to log in as, for tests
test_identity = None

class BazAuthenticatorPlugin(object):

    def authenticate(self, environ, identity):
        with benchmarking('authenticating user'):
            try:
                login = identity['login']
                password = identity['password']
            except KeyError:
                return None

            try:
                e = structure.get_element_raw({u'username': login})
            except LookupError:
                warnings.warn('multiple elements with username %s' % (login,))
                return None
            if e is None:
                warnings.warn('no element with username %s' % (login,))
                return None

            if not e.has_propval(u'password'):
                warnings.warn('password not in element %s' % (e.ename,))
                return None
            elif conversion.render(e, u'password') == password:
                return login
            else:
                warnings.warn("got password '%s', expected '%s'" % (
                    password, conversion.render(e, u'password')))
                return None

class BazMetadataProvider(object):

    def add_metadata(self, environ, identity):
        with benchmarking('adding identity metadata'):
            userid = identity.get('repoze.who.userid')
            e = structure.get_element_raw({u'username': userid})
            identity.update({'element':e})
            # This is delayed to allow modules to monkey-patch is_omniscient
            environ['bazjunk.catch_errors'] = lambda e: custom.is_omniscient(e)

from repoze.what.adapters import BaseSourceAdapter

class BazGroupSourceAdapter(BaseSourceAdapter):

    def __init__(self):
        super(BazGroupSourceAdapter, self).__init__(writable=False)
        
    def _get_all_sections(self):
        return bazki.custom.GROUPS

    def _get_section_items(self, section):
        return [conversion.render_raw(e.ename, u'username')
                for e in structure.get_element(section).get_descendants()
                if e.has_propval(u'username')]
    
    def _find_sections(self, credentials):
        username = credentials['repoze.what.userid']
        e = structure.get_element_raw({u'username': username})
        retset = set(a.ename for a in e.get_ancestors()
                     if a.ename in bazki.custom.GROUPS)
        return retset
            
    def _item_is_included(self, section, item):
        a = structure.get_element(section)
        e = structure.get_element_raw({u'username':item})
        if a is None or e is None:
            return False
        return a.is_ancestor_of(e)
        
    def _section_exists(self, section):
        return section in bazki.custom.GROUPS

class DefaultAdminPlugin(object):
    def identify(self, environ):
        admin_parent = structure.get_element(unicode(test_identity))
        user = admin_parent.get_children()[0]
        return {'login': conversion.render_raw(user.ename, u'username'),
                'password': conversion.render(user, u'password')}

    def remember(self, environ, identity):
        pass
    def forget(self, environ, identity):
        pass
    
def setup_auth(app, app_conf):
    global test_identity
    
    form = FriendlyFormPlugin(
        app_conf.baz_auth.get('login_url','/login'),
        app_conf.baz_auth.get('login_handler','/login_handler'),
        app_conf.baz_auth.get('post_login_url',None),
        app_conf.baz_auth.get('logout_handler','/logout_handler'),
        app_conf.baz_auth.get('post_logout_url',None),
        login_counter_name=app_conf.baz_auth.get('baz_auth.login_counter_name',
                                                 None),
        rememberer_name='cookie')
        
    cookie_secret = app_conf.baz_auth.get('cookie_secret')
    assert cookie_secret is not None,app_conf
    cookie = AuthTktCookiePlugin(cookie_secret)

    authenticators = [('bazauth', BazAuthenticatorPlugin())]
    identifiers = [('main_identifiers', form),
                   ('cookie', cookie)]

    for a in sys.argv:
        pref = 'test_identity='
        if a.startswith(pref):
            test_identity = a[len(pref):]
    if test_identity:
        identifiers.append(('test_disable_auth', DefaultAdminPlugin()))
        
    middleware = repoze_setup_auth(app, {'baz_auth':
                                         BazGroupSourceAdapter()}, {}, 
                                   authenticators=authenticators,
                                   identifiers=identifiers,
                                   challengers=[('form',form)],
                                   mdproviders=[('baz_md',
                                                 BazMetadataProvider())],
                                   log_stream=sys.stderr)
    
    
    return middleware
