# -*- coding: utf-8 -*-
import abc

from functools import partial

from distribution import app
from ..validator import Validator


class ValidationError(Exception):
    pass


class staticproperty(property):
    def __get__(self, cls, owner):
        return self.fget.__get__(None, owner)()


class Model(object):
    __metaclass__ = abc.ABCMeta
    manager_class = None
    _manager = None
    _data = None
    schema = None

    @staticproperty
    @classmethod
    def manager(cls):
        if getattr(cls._manager, 'model_class', None) is not cls:
            cls._manager = cls.manager_class(cls)
        return cls._manager

    def __init__(self, data, validate=True, *args, **kwargs):
        if validate:
            try:
                self.validate(data)
            except ValidationError as e:
                message_format = partial('{}, {} = {}'.format,
                                         'Error while validating {}: error = {}'
                                         .format(
                                             self.__class__.__name__,
                                             str(e)))

                app.logger.warning(message_format("data", str(data)[:2000]))
                raise
        else:
            self._data = data

    def save(self):
        return self.manager.save(self)

    @property
    def validator(self):
        if not hasattr(self, '_validator'):
            self._validator = Validator(self.schema, allow_unknown=True)
        return self._validator

    @abc.abstractmethod
    def is_deleted(self):
        pass

    def validate(self, data):
        if not self.validator.validate(data):
            raise ValidationError(self.validator.errors)
        self._data = self.validator.document

    def update_data(self, new_data):
        for field in new_data.keys():
            self._data[field] = new_data.get(field)

        self.validate(self._data)

    @abc.abstractproperty
    def key(self):
        pass

    @property
    def data(self):
        return self._data

    @property
    def clear_data(self):
        """
        Used to display the information to the public.
        """
        return self._data

    def __hash__(self):
        return hash(self.__class__.__name__ + self.key)

    def __eq__(self, other):
        return hash(self) == hash(other)

    def __ne__(self, other):
        return not self.__eq__(other)
