from datetime import datetime
import re
import uuid

from django.core.cache import cache
from django.shortcuts import render
from django.utils.timezone import utc

from ..base.models import Request, Status

import app_settings

def utcdatetime_to_datetime(s):
    '''Parse an UTCDatetime string, which is define as ISO8601 with the
       timezone forced to be UTC encoded as 'Z'.
    '''
    if s is None:
        return s
    return datetime.strptime(s.strip(), '%Y-%m-%dT%H:%M:%SZ') \
            .replace(tzinfo=utc)

def get_poly_request_from_identifier(identifier, context):
    try:
        regexp = r'oai:%s:poly-request-(\d+)' % re.escape(context['hostname'])
        m = re.match(regexp, identifier)
        if m is None:
            raise ValueError('wrong identifier')
        return queryset().get(id=int(m.group(1)))
    except (Request.DoesNotExist, ValueError):
        raise ValueError

def get_record(request, context):
    params = request.GET

    if 'metadataPrefix' not in params:
        raise ValueError('missing metadataPrefix')
    context['metadataPrefix'] = metadata = params['metadataPrefix']
    if metadata not in record_templates_by_prefix:
        return error(request, context, 'cannotDisseminateFormat',
        'format %s is not supported' % metadata)
    context['record_template'] = record_templates_by_prefix[metadata]
    if 'identifier' not in params:
        raise ValueError('missing identifier')
    identifier = context['identifier'] = params['identifier']
    try:
        context['poly_request'] = get_poly_request_from_identifier(identifier, context)
    except ValueError:
        return error(request, context,
                'idDoesNotExist', 'wrong identifier %s' % identifier)
    return render(request, 'oai/get_record.xml', context)

record_templates_by_prefix = {
        'oai_dc': 'oai/dc_record.xml',
        'oai_qdc': 'oai/qdc_record.xml',
}

def queryset():
    return Request.objects.filter(
            status__in=Status.objects.get_after_start_statuses(),
            licence__diffusion_tags__contains='oai-pmh') \
                  .exclude(uploadfile='')

def listing(request, context, paginate=None, template="oai/list_records.xml"):
    """ ListRecords/Identifiers implementation """
    params = request.GET
    if paginate is None:
        paginate = app_settings.OAI_PAGINATE

    in_resumption_token = params.get('resumptionToken')
    if in_resumption_token:
        if len(params) > 2:
            raise ValueError('you cannot provide arguments with resumptionToken')
        try:
            resumption_token = cache.get(in_resumption_token)
            if resumption_token is None:
                raise ValueError
        except:
            return error(request, context, 'badResumptionToken')
        date_from = resumption_token['date_from']
        date_until = resumption_token['date_from']
        context['record_template'] = resumption_token['record_template']
        last_id = resumption_token['last_id']
    else:
        if 'metadataPrefix' not in params:
            raise ValueError('missing metadataPrefix')

        date_from = utcdatetime_to_datetime(params.get('from'))
        date_until = utcdatetime_to_datetime(params.get('until'))
        context['metadataPrefix'] = metadata = params['metadataPrefix']
        if metadata not in record_templates_by_prefix:
            return error(request, context, 'cannotDisseminateFormat',
            'format %s is not supported' % metadata)
        context['record_template'] = record_templates_by_prefix[metadata]
        last_id = None
        if 'set' in params: # set is not supported
            return error(request, context, 'noSetHierarchy')
    qs = queryset()
    if last_id:
        qs = qs.filter(id__gt=last_id)
    if date_from:
        qs = qs.filter(modification_date__gte=date_from)
    if date_until:
        qs = qs.filter(modification_date__lte=date_until)
    count = context['complete_list_size'] = qs.count()
    if paginate is not None:
        if paginate and count > paginate:
            qs = list(qs[:paginate])
            resumption_token = {
                'date_from': date_from,
                'date_until': date_until,
                'record_template': context['record_template'],
                'last_id': qs[-1].id,
                'id': uuid.uuid4(),
            }
            context['resumption_token'] = resumption_token['id']
            cache.set(resumption_token['id'], resumption_token, 3600*48)
    if count == 0:
        return error(request, context, 'noRecordsMatch')
    context['poly_requests'] = qs
    return render(request, template, context, content_type='text/xml')

def error(request, context, code, msg=''):
    context['error_code'] = code
    context['msg'] = msg
    return render(request, 'oai/error.xml', context, content_type='text/xml')

def request(request):
    context = {
        'timestamp': datetime.utcnow(),
        'url': request.build_absolute_uri(request.path),
        'base_url': request.build_absolute_uri('/')[:-1],
        'hostname': request.get_host(),
        'settings': app_settings,
    }
    params = request.GET
    if 'verb' not in params:
        return error(request, context, 'badVerb')
    verb = params['verb']
    attrs = [attr for attr in params if attr != 'verb']
    try:
        context['verb'] = verb

        if verb == 'Identify':
            if attrs:
                del context['verb']
                return error(request, context, 'badArgument')
            return render(request, 'oai/identify.xml', context,
                    content_type='text/xml')

        elif verb == 'ListMetadataFormats':
            if not set(attrs).issubset(['identifier']):
                del context['verb']
                return error(request, context, 'badArgument')
            if 'identifier' in params:
                identifier = context['identifier'] = params['identifier']
                try:
                    get_poly_request_from_identifier(identifier, context)
                except ValueError:
                    return error(request, context,
                            'idDoesNotExist', 'wrong identifier %s' % identifier)
            return render(request, 'oai/list_metadata_formats.xml', context,
                    content_type='text/xml')

        elif verb == 'ListRecords':
            return listing(request, context)

        elif verb == 'ListIdentifiers':
            return listing(request, context,
                    template='oai/list_identifiers.xml')

        elif verb == 'GetRecord':
            return get_record(request, context)

        elif verb == 'ListSets':
            return error(request, context, 'noSetHierarchy')

        else:
            del context['verb']
            return error(request, context, 'badVerb')
    except ValueError, e:
        del context['verb']
        return error(request, context, 'badArgument',
                e.args[0] if e.args else '')


