import functools
import json
from datetime import datetime
from django.forms import model_to_dict

from intranet.search.core.models import Job
from .base import Storage, DoesNotExist


def convert(obj):
    res = model_to_dict(obj)
    if obj.options is not None:
        res['options'] = json.loads(obj.options)

    return res


def job_status(name=None):
    """ Декоратор для сохранения информации о фоновой задаче
    """
    def decorator(func):
        @functools.wraps(func)
        def _wrapper(*args, **kwargs):
            job_storage = JobStorage()
            job_name = name if name and not callable(name) else ':'.join([func.__module__, func.__name__])
            job_id = job_storage.create(job_name, options=kwargs)
            try:
                result = func(*args, **kwargs)
                job_storage.finish(job_id)
                return result
            except Exception as e:
                job_storage.finish(job_id, fail=True, message=str(e))
                raise
        return _wrapper
    return decorator(name) if callable(name) else decorator


class JobStorage(Storage):
    def create(self, job_name, entity='default', options=None):
        job = Job.objects.create(
            entity=entity, job_name=job_name,
            options=json.dumps(options or {}))

        return job.id

    def _get(self, job_id):
        try:
            return Job.objects.get(id=job_id)
        except Job.DoesNotExist:
            raise DoesNotExist("Can't find records for job %s" % job_id)

    def _update(self, job_id, **kwargs):
        try:
            Job.objects.filter(id=job_id).update(**kwargs)
        except Job.DoesNotExist:
            raise DoesNotExist("Can't find records for job %s" % job_id)

    def get_last_success(self, job_name):
        last_job = Job.objects.filter(job_name=job_name, status='done').last()
        return model_to_dict(last_job) if last_job else None

    def get(self, job_id):
        return convert(self._get(job_id))

    def set_status(self, job_id, status):
        assert status in Job.STATUSES
        self._update(job_id, status=status)

    def finish(self, job_id, message='', fail=False):
        if fail:
            status = 'fail'
        else:
            status = 'done'
        self._update(job_id, status=status, message=message, end_time=datetime.now())
