from collections import defaultdict
from datetime import datetime

from schematics.models import Model
from schematics.types import StringType, URLType, DateTimeType
from schematics.types.compound import ListType, ModelType
from schematics.exceptions import BaseError as SchematicsError, ConversionError

from intranet.search.core.utils.domain import change_domain

__registered_snippets = defaultdict(list)


class Breadcrumb(Model):
    name = StringType()
    url = URLType()


class User(Model):
    login = StringType(required=True)
    first_name = StringType(required=True)
    last_name = StringType(required=True)


class DomainChangingURLType(URLType):
    def to_primitive(self, url, context=None):
        domain = getattr(context, 'domain', None)
        if domain is None:
            return url

        return change_domain(url, domain)


class BaseSnippet(Model):

    @property
    def _fields_to_highlight(self):
        """ Список полей для подсветки.
            - None - подсчечивать все
            - пустой список - ничего не подсвечивать
            - список с перечислением полей - подсвечивать только заданные поля
        По умолчанию подсвечивает все, кроме поля 'url'
        """
        return set(self.keys()) - {'url'}


class CommonSnippet(BaseSnippet):
    url = URLType(required=True)
    title = StringType()
    description = StringType(required=True)
    date = DateTimeType()
    breadcrumbs = ListType(ModelType(Breadcrumb))
    person = ModelType(User)


class DateTimeStampType(DateTimeType):
    def to_native(self, value, context=None):
        try:
            return super().to_native(value, context)
        except ConversionError:
            return datetime.fromtimestamp(float(value))


def register(search_name, cls):
    assert issubclass(cls, BaseSnippet)

    __registered_snippets[search_name].append(cls)


def get_snippets(search):
    if search not in __registered_snippets:
        raise ValueError('Cant find snippet classes for %s search' % search)

    return __registered_snippets[search]


def parse_snippet(search, raw_snippet):
    classes = get_snippets(search)
    for cls in classes:
        try:
            res = cls(raw_snippet)
            res.validate()
            return res
        except SchematicsError:
            continue
    else:
        raise ValueError('Cant find correct class for snippet')
