import datetime
import json
import os
import uuid
import errno

from mako.template import Template
from mako.lookup import TemplateLookup
from mako.exceptions import TopLevelLookupException

from django.utils.translation import ugettext_lazy as _
from django.db import models
from django.conf import settings
from passerelle.base.models import BaseResource
from . import app_settings

class BaseQueue(BaseResource):
    pass

class BaseOutputQueue(BaseQueue):
    directory = models.CharField(max_length=200, verbose_name=_('directory '
        'where the data will be stored'),
        help_text=('created files will have filename like '
        '2013-10-03T03:34:34-c009433e-2d02-11e3-a9d2-843a4b96a544 '
        'where first is the date and time of arrival and the second part is an '
        'UUID'))
    tmp_directory = models.CharField(max_length=200,
            default=app_settings.QUEUE_DEFAULT_TMP_DIRECTORY,
            verbose_name=_('directory where temporary files are created'),
            help_text=_('must be on the same file-system as directory to '
                'guarantee atomicity of rename'))
    output_encoding = models.CharField(max_length=32, default='utf-8')

    def get_content(self, data, **kwargs):
        raise NotImplementedError

    def check_authorized_directory(self, path):
        for authorized_directory in app_settings.QUEUE_AUTHORIZED_DIRECTORIES:
            if not authorized_directory.endswith('/'):
                authorized_directory += '/'
            if path.startswith(authorized_directory):
                return True
        return False

    def queue(self, data, **kwargs):
        if data is None:
            raise Exception('missing data')
        # template the dirpath
        if isinstance(data, dict):
            kwargs.update(data)
            path = self.directory.format(**kwargs)
        else:
            path = self.directory.format(*data, **kwargs)
        if isinstance(data, dict):
            kwargs.update(data)
            tmp_path = self.tmp_directory.format(**kwargs)
        else:
            tmp_path = self.tmp_directory.format(*data, **kwargs)
        # canonicalize
        path = os.path.realpath(path)
        tmp_path = os.path.realpath(tmp_path)
        if not path.endswith('/'):
            path += '/'
        if not tmp_path.endswith('/'):
            tmp_path += '/'
        # check if it is an existing directory
        if not os.path.isdir(path):
            raise Exception('queue path is not a directory')
        if not os.path.isdir(tmp_path):
            raise Exception('queue tmp path is not a directory')
        # check if it is an authorized directory
        if not self.check_authorized_directory(path):
            raise Exception('queue path is unauthorized')
        if not self.check_authorized_directory(tmp_path):
            raise Exception('queue tmp path is unauthorized')

        filename = '{0}-{1}'.format(datetime.datetime.now().isoformat('T'), uuid.uuid1())
        tmp_filepath = os.path.join(tmp_path, filename)
        new_filepath = os.path.join(path, filename)
        try:
            with file(tmp_filepath, 'w') as f:
                f.write(self.get_content(data, **kwargs).encode(self.output_encoding))
                f.flush()
                os.fsync(f.fileno())
            os.rename(tmp_filepath, new_filepath)
        except:
            try:
                os.unlink(tmp_filepath)
            except OSError, e:
                if e.errno != errno.ENOENT:
                    raise
            raise
        return None

    class Meta:
        abstract = True

class JSONOutputQueue(BaseOutputQueue):
    def get_content(self, data, **kwargs):
        return json.dumps(data)

class MakoPrototypeOutputQueue(BaseOutputQueue):
    template = models.TextField(blank=False,
            verbose_name=_('inline mako template for formatting the output'))

    def get_content(self, data, **kwargs):
        return Template(self.template).render_unicode(data=data, args=kwargs)

template_lookup = TemplateLookup(directories=settings.MAKO_TEMPLATES_DIRS,
        module_directory=settings.MAKO_TEMPLATES_MODULES)

class MakoOutputQueue(BaseOutputQueue):
    def get_content(self, data, **kwargs):
        return template_lookup.get_template(self.slug + '-queue.mako') \
                .render_unicode(data=data, args=kwargs)

