from __future__ import absolute_import

from hashlib import sha1
import logging
import M2Crypto

from django.contrib.auth import login, authenticate
from django.contrib.auth.models import User

from dashboard.models import Profile

from .voms_helper import VOMS

logger = logging.getLogger(__name__)


# https://github.com/IFCA/keystone-voms/blob/stable/icehouse/keystone_voms/core.py
# TODO
class VomsError(Exception):
    """Voms credential management error"""

    errors = {
        0: ('none', None),
        1: ('nosocket', 'Socket problem'),
        2: ('noident', 'Cannot identify itself (certificate problem)'),
        3: ('comm', 'Server problem'),
        4: ('param', 'Wrong parameters'),
        5: ('noext', 'VOMS extension missing'),
        6: ('noinit', 'Initialization error'),
        7: ('time', 'Error in time checking'),
        8: ('idcheck', 'User data in extension different from the real'),
        9: ('extrainfo', 'VO name and URI missing'),
        10: ('format', 'Wrong data format'),
        11: ('nodata', 'Empty extension'),
        12: ('parse', 'Parse error'),
        13: ('dir', 'Directory error'),
        14: ('sign', 'Signature error'),
        15: ('server', 'Unidentifiable VOMS server'),
        16: ('mem', 'Memory problems'),
        17: ('verify', 'Generic verification error'),
        18: ('type', 'Returned data of unknown type'),
        19: ('order', 'Ordering different than required'),
        20: ('servercode', 'Error from the server'),
        21: ('notavail', 'Method not available'),
    }

    def __init__(self, value):
        self.code, self.message = self.errors.get(
            value, ('oops', 'Unknown error %d' % value))

    def __str__(self):
        return self.message


class VomsBackend(object):
    # https://github.com/IFCA/keystone-voms/blob/stable/icehouse
    # /keystone_voms/core.py
    # TODO
    @staticmethod
    def _get_cert_chain(cert, chain):
        """Return certificate and chain from the ssl info in M2Crypto format"""
        cert_out = M2Crypto.X509.load_cert_string(cert)
        chain_out = M2Crypto.X509.X509_Stack()
        for c in chain:
            aux = M2Crypto.X509.load_cert_string(c)
            if aux.check_ca():
                continue  # Don't include CA certs
            chain_out.push(aux)
        return cert_out, chain_out

    # https://github.com/IFCA/keystone-voms/blob/stable/icehouse
    # /keystone_voms/core.py
    # TODO
    def _get_voms_info(self, cert, chain):
        """Extract voms info from ssl_info and return dict with it."""

        try:
            cert, chain = self._get_cert_chain(cert, chain)
        except M2Crypto.X509.X509Error:
            raise
        with VOMS('/etc/grid-security/vomsdir/',
                  '/etc/grid-security/certificates/',
                  'libvomsapi.so.1') as v:
            voms_data = v.retrieve(cert, chain)
            if not voms_data:
                raise VomsError(v.error.value)

            d = {}
            for attr in ('user', 'userca', 'server', 'serverca',
                         'voname',  'uri', 'version', 'serial',
                         ('not_before', 'date1'), ('not_after', 'date2')):
                if isinstance(attr, basestring):
                    d[attr] = getattr(voms_data, attr)
                else:
                    d[attr[0]] = getattr(voms_data, attr[1])

            d["fqans"] = []
            for fqan in iter(voms_data.fqan):
                if fqan is None:
                    break
                d["fqans"].append(fqan)

        return d

    def authenticate(self, request):
        cert = request.environ.get('SSL_CLIENT_CERT')
        chain = []
        for k, v in request.environ.iteritems():
            if k.startswith('SSL_CLIENT_CERT_CHAIN_'):
                chain.append(v)

        if not cert or not chain:
            logger.debug('missing cert(%s) or chain(%s)', cert, chain)
            return None

        cert = cert.replace('\t', '\n')

        try:
            voms_info = self._get_voms_info(cert, chain)
        except VomsError as e:
            logger.info('VomsError: %s', str(e))
            return None
        except:
            logger.exception('Unhandled error: ')
            return None

        dn = voms_info['user']
        username = sha1(dn).hexdigest()[:30]
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            cn = dn.split('/')[-1].lstrip('CN=')
            first_name, last_name = cn.split(' ', 2)
            user = User(username=username, password='',
                        first_name=first_name, last_name=last_name)
            user.save()
            profile, created = Profile.objects.get_or_create(user=user)
            profile.org_id = dn
            profile.save()
            logger.info(u'new voms user: %s (%s)', user, dn)

        return user

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None


# https://github.com/kimvais/django-ssl-client-auth/blob/master/django_ssl_auth/base.py
class VomsMiddleware(object):
    def process_request(self, request):
        if not hasattr(request, 'user') or request.user.is_authenticated():
            return
        user = authenticate(request=request)
        if user is None or not user.is_authenticated():
            return
        logger.info("VomsMiddleware login: %s", repr(user))
        login(request, user)