import datetime

import pytils
import pytz
from django.utils.encoding import smart_str, smart_bytes
from django.conf import settings
from logging import getLogger

from intranet.search.core.sources.utils import swap_layout, date_to_timestamp, get_suggest_parts
from schematics.models import Model

from intranet.search.core.utils import remove_control_characters, shave_diacritic_marks

log = getLogger(__name__)


class Document:
    # ограничение сааса на длину поискового атрибута (длина названия + длина значения)
    MAX_ATTR_LENGTH = 252

    def __init__(self, url, updated=None, raw_data=None):
        self.url = url

        self.body = None
        self.raw_data = raw_data  # нераспаршенные данные документа, нужны для кеша в yt
        self.snippets = {'ru': None, 'en': None}
        self.attributes = {
            'property': [],
            'search_int': [],
            'search_literal': [],
            'group': [],
            'facet': [],
        }
        self.factors = {'self': {}, 'meta': {}}

        self.updated = updated or datetime.datetime.now(tz=pytz.timezone(settings.TIME_ZONE))
        self.updated_ts = int(date_to_timestamp(self.updated))
        self.emit_sort_attr('updated', self.updated_ts)

    def __str__(self):
        return '<doc: %s>' % self.url

    def is_valid_attr(self, name, value, attr_type='facet'):
        # Поисковые атрибуты в саасе не должны быть длиннее 252 байт (вместе с названием)
        attr_is_too_long = (
            attr_type in ['search_literal', 'facet']
            and isinstance(value, str)
            and len(smart_bytes(value)) + len(name) >= self.MAX_ATTR_LENGTH
        )
        if attr_is_too_long:
            log.warning(
                'Search attribute exceeded max length of %s. Attribute: %s Doc: %s',
                self.MAX_ATTR_LENGTH, name, self.url
            )
            return False
        return True

    def emit_body(self, body):
        self.body = body

    def emit_snippet(self, snippet, lang='ru'):
        if isinstance(snippet, Model):
            snippet.validate()
            snippet = snippet.to_primitive(context={'view': 'saas'})

        self.snippets[lang] = snippet

    def emit_factor(self, name, value, meta=False):
        if meta:
            self.factors['meta'][name] = value
        else:
            self.factors['self'][name] = value

    def _emit_attr(self, name, value, attr_type='property'):
        if not self.is_valid_attr(name, value, attr_type):
            return
        self.attributes[attr_type].append({'name': name, 'value': value})

    def emit_property_attr(self, name, value):
        self._emit_attr(name, value, 'property')

    def emit_search_attr(self, name, value, **kwargs):
        # kwargs нужно для совместимости со старым документом
        if isinstance(value, int):
            self._emit_attr(name, value, 'search_int')
        elif value:
            self._emit_attr(name, smart_str(value), 'search_literal')

    def emit_suggest_attr_by_parts(self, attr):
        self.emit_suggest_attr(attr)
        for part in get_suggest_parts(attr):
            self.emit_suggest_attr(part)

    def emit_suggest_attr(self, value):
        if not value:
            return

        self.emit_search_attr('s_suggest', value)

        cleaners = [
            lambda text: swap_layout(text, 'ru'),
            lambda text: swap_layout(text, 'en'),
            pytils.translit.detranslify,
            shave_diacritic_marks
        ]
        for cleaner in cleaners:
            cleaned_value = cleaner(value)
            if cleaned_value and cleaned_value != value:
                self.emit_search_attr('s_suggest', cleaned_value)

    def emit_group_attr(self, name, value, label='', label_en='', url=''):
        if isinstance(value, str) and value == '':
            raise ValueError("You can't send group attr %s with empty value" % name)

        data = {
            'name': name,
            'value': value,
            'label': remove_control_characters(label),
            'label_en': remove_control_characters(label_en),
            'url': url,
        }
        self.attributes['group'].append(data)

    def emit_facet_attr(self, name, value, label, label_en=''):
        if not self.is_valid_attr(name, value):
            return

        if isinstance(value, str):
            assert ':' not in value, "Facet value can't contain `:`"
            assert ';' not in value, "Facet value can't contain `;`"

        data = {
            'name': name,
            'value': value,
            'label_ru': remove_control_characters(label),
            'label_en': remove_control_characters(label_en)
        }
        self.attributes['facet'].append(data)

    def emit_meta_factor(self, name, value):
        # метод для совместимости со старым документам
        self.emit_factor(name, value, meta=True)

    def emit_sort_attr(self, name, value):
        return self.emit_group_attr(name, value)
