import lasso
import authentic.liberty.common as common

from quixote.directory import Directory
from qommon.storage import StorableObject
import qommon.idwsf
import qommon.misc
import authentic.misc as misc
import authentic.identities as identities
import qommon.soap as soap

class IdWsf2Association(StorableObject):
    _names = 'idwsf2_associations'
    _hashed_indexes = [ 'user', 'svcmdid' ]
    svcmdid = None
    user = None

    def __init__(self, svcmdid,user):
        self.user = user
        self.svcmdid = svcmdid
        super(IdWsf2Association, self).__init__()

    def get_by_user(cls, user):
        '''Find associations for this user, user can be a string, an integer or a user object with possessing an id field'''
        if not isinstance(user, (str, int)):
            user = getattr(user, 'id', Object())
        return cls.get_with_indexed_value('user', user, ignore_errors = True)
    get_by_user = classmethod(get_by_user)

    def get_by_service(cls, svcmdid):
        if isinstance(svcmdid, IdWsf2Metadata):
            svcmdid = svcmdid.id
        return cls.get_with_indexed_value('svcmdid', svcmdid, ignore_errors = True)
    get_by_service = classmethod(get_by_service)

    def __repr__(self):
        return ('<%s' % type(self)) + ' id:%(id)s user:%(user)s svcmdid:%(svcmdid)s>' % self.__dict__

class IdWsf2Metadata(StorableObject):
    _names = 'idwsf2_metadatas'
    _hashed_indexes = [ 'provider' ]
    provider = None
    metadata = None

    def __init__(self, metadata = None, provider = None, *args, **kwargs):
        self.metadata = metadata
        self.provider = provider
        super(IdWsf2Metadata, self).__init__(*args, **kwargs)

    @classmethod
    def get_services_for_provider(cls, provider):
        if isinstance(provider, lasso.Provider):
            provider = provider.providerId
        r=cls.get_with_indexed_value('provider', provider, ignore_errors = True)
        return r

if not lasso.WSF_ENABLED:
    class DiscoveryServiceDirectory(Directory):
        _q_exports = [ 'soapEndpoint' ]

        def soapEndpoint(self):
            raise errors.TraversalError()
    def addDiscoveryBootStrapToLogin(login, base, organization,
            security_mechanisms):
        pass
else:
    def addDiscoveryBootStrapToLogin(login, url, organization,
            security_mechanisms):
        login.idwsf2AddDiscoveryBootstrapEpr(
                url = url,
                abstract = 'Discovery Service of ' + organization,
                security_mechanisms = security_mechanisms)

    class AuthenticDiscoveryService(qommon.idwsf.DiscoveryService2):
        def __init__(self):
            super(AuthenticDiscoveryService, self).__init__(server = misc.get_lasso_server(protocol = 'saml2'))

        def handleMdRegister(self, profile, before):
            if before:
                return
            provider = \
                profile.getSoapEnvelopeRequest().sb2GetProviderId()
            to_save = []
            # Try to be atomic
            for metadata in profile.metadatas:
                to_save.append(IdWsf2Metadata(metadata = metadata,
                    provider = provider,
                    id = metadata.svcMDID))
            for save in to_save:
                save.store()

        def handleMdQuery(self, profile, before):
            if not before:
                return
            mdids = profile.svcmdids
            # Try to be atomic
            metadatas = []
            if not mdids:
                provider_id = profile.getSoapEnvelopeRequest().sb2GetProviderId()
                idwsf2_metadatas = IdWsf2Metadata.get_services_for_provider(provider_id)
                for idwsf2_metadata in idwsf2_metadatas:
                    profile.addServiceMetadata(idwsf2_metadata.metadata)
            else:
                for id in mdids:
                    md = IdWsf2Metadata.get(id, ignore_errors=True)
                    if md:
                        profile.addServiceMetadata(md)

        def handleMdDelete(self, profile, before):
            if before:
                return
            to_delete = []
            for mdid in profile.svcmdids:
                md = IdWsf2Metadata.get(mdid, ignore_errors = True)
                if not md:
                    profile.failRequest(
                            lasso.IDWSF2_DISCOVERY_STATUS_CODE_FAILED,
                            lasso.IDWSF2_DISCOVERY_STATUS_CODE_NOT_FOUND)
                    return
                # Delete associations
                md_associations = IdWsf2Association.get_by_service(mdid)
                for md_association in md_associations:
                    md_association.remove_self()
                to_delete.append(md)
            for md in to_delete:
                md.remove_self()

        def handleMdAssociationAdd(self, profile, before):
            if before:
                return
            nameid=profile.getNameIdentifier()
            svcmdids=profile.svcmdids
            if svcmdids and nameid:
                user=identities.get_store().get_identity_for_name_identifier(nameid.content)
                if user:
                    already=IdWsf2Association.get_by_user(user.id)
                    for svcmdid in svcmdids:
                        if not [ x for x in already if x.svcmdid == svcmdid ] \
                            and IdWsf2Metadata.get(svcmdid, ignore_errors=True):
                            IdWsf2Association(svcmdid = svcmdid, user = user.id).store()

        def handleMdAssociationQuery(self, profile, before):
            if not before:
                return
            nameid=profile.getNameIdentifier()
            svcmdids=profile.svcmdids
            profile.svcmdids=tuple()
            if svcmdids and nameid:
                user=identities.get_store().get_identity_for_name_identifier(nameid.content)
                if user:
                    already=IdWsf2Association.get_by_user(user.id)
                    new=[]
                    for svcmdid in svcmdids:
                        if [ x for x in already if x.svcmdid == svcmdid ]:
                            new.append(svcmdid)
                    profile.svcmdids = tuple(new)

        def handleMdAssociationDelete(self, profile, before):
            if before:
                return
            nameid=profile.getNameIdentifier()
            svcmdids=profile.svcmdids
            if svcmdids and nameid:
                user=identities.get_store().get_identity_for_name_identifier(nameid.content)
                if user:
                    existing=IdWsf2Association.get_by_user(user.id)
                    for association in existing:
                        if association.svcmdid in svcmdids:
                            association.remove_self()

        def handleQuery(self, profile, before):
            if not before:
                return
            nameid=profile.getNameIdentifier()
            if nameid:
                user=identities.get_store().get_identity_for_name_identifier(nameid.content)
                if user.lasso_dump:
                    profile.setIdentityFromDump(user.lasso_dump)
                existing=IdWsf2Association.get_by_user(user.id)
                for x in existing:
                    md = IdWsf2Metadata.get(x.svcmdid, ignore_errors=True)
                    if md:
                        profile.addServiceMetadata(md.metadata)

    class DiscoveryServiceDirectory(common.LassoDirectory):
        _q_exports = [ 'soapEndpoint' ]

        def soapEndpoint(self):
            try:
                soap_message = self.get_soap_message()
            except soap.SOAPException, e:
                return e.message
            else:
                disco = AuthenticDiscoveryService()
                return disco.processRequest(soap_message, check_security = True)
