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

from django.conf import settings
from django.db.models import QuerySet
from rest_framework import serializers
from rest_framework.fields import empty
from requests.packages.urllib3.fields import guess_content_type

from events.common_app.utils import sanitize_xml_illegals
from events.common_storages.models import ProxyStorageModel
from events.surveyme.answer_value import find_question_data
from events.surveyme_integration.utils import render_variables


class RenderedFieldMixin(object):
    def as_string(self, value, additional_data):
        obj = self.get_parent_object()
        prepared_value = self.prepare_value_for_rendering(value)
        processed_data = self.process_data(additional_data)
        result = render_variables(
            value=prepared_value,
            object_with_variables=self.get_variables_source(),
            answer=obj.answer,
            trigger_data=obj.trigger_data,
            notification_unique_id=obj.notification_unique_id,
            force_render=obj.force_render,
            **processed_data
        )
        return result

    def process_data(self, data):
        data = data or {}
        return data

    def as_list(self, value, additional_data):
        return [
            self.as_string(subvalue, additional_data)
            for subvalue in value
        ]

    def to_representation(self, value, additional_data=None):
        if isinstance(value, str):
            return self.as_string(value, additional_data=additional_data)
        elif isinstance(value, list):
            return self.as_list(value, additional_data=additional_data)
        return value

    def get_variables_source(self):
        return self.get_parent_object().subscription

    def prepare_value_for_rendering(self, value):
        return value

    def get_parent_object(self):
        return self.parent.instance

    def is_none_value(self, native_value):
        native_value = native_value or ''
        native_value = native_value.strip()
        return not native_value or native_value == 'None'

    def is_need_to_skip(self, obj, native_value):
        if obj.add_only_with_value and self.is_none_value(native_value):
            return True


class SafeRenderedFieldMixin(RenderedFieldMixin):
    special_re = re.compile(r'(?:\r\n?|[\n\x07\x08])')

    def to_representation(self, value, additional_data=None):
        result = super(SafeRenderedFieldMixin, self).to_representation(value)
        result = self.special_re.sub(' ', result)
        return result


class SanitizeForXMLFieldMixin(object):
    def to_representation(self, value):
        # todo: test me
        return sanitize_xml_illegals(super(SanitizeForXMLFieldMixin, self).to_representation(value))


class RenderedCharField(RenderedFieldMixin, serializers.CharField):
    pass


class RenderedJSONField(RenderedFieldMixin, serializers.JSONField):
    pass


class SafeRenderedCharField(RenderedCharField, SafeRenderedFieldMixin):
    pass


class AttachmentFileObjectWrapper(object):
    def __init__(self, file_object, path, filename, frontend_url, content_type, question, namespace):
        self.file_object = file_object
        self.path = path
        self.filename = filename
        self.frontend_url = frontend_url
        self.content_type = content_type
        self.question = question
        self.namespace = namespace


class AttachmentQuestionLabel(serializers.Serializer):
    ru = serializers.CharField(source='*')

    class Meta:
        fields = (
            'ru',
        )


class AttachmentQuestionSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    label = AttachmentQuestionLabel()

    class Meta:
        fields = (
            'id',
            'label',
        )


class AttachmentBaseSerializer(serializers.Serializer):
    filename = serializers.CharField()
    path = serializers.CharField()
    frontend_url = serializers.CharField()
    content_type = serializers.CharField()
    question = AttachmentQuestionSerializer()
    namespace = serializers.CharField()

    class Meta:
        fields = (
            'filename',
            'path',
            'frontend_url',
            'content_type',
            'question',
            'namespace',
        )


class QuestionsSelectiveSourceDescriptor(object):
    def __get__(self, instance, owner):
        if not instance.parent:
            return instance._source

        service_context_instance = instance.parent.instance

        selective_source = None
        if hasattr(instance, '_selective_source'):
            selective_source = instance._selective_source
        if selective_source and not service_context_instance.subscription.is_all_questions:
            return selective_source
        else:
            return instance._source

    def __set__(self, instance, value):
        instance._source = value


class SelectiveSourceMixin(object):
    source = QuestionsSelectiveSourceDescriptor()

    def __init__(self, selective_source=None, **kwargs):
        self._selective_source = selective_source
        super(SelectiveSourceMixin, self).__init__(**kwargs)


class AttachmentFromQuestionSerializer(SelectiveSourceMixin, AttachmentBaseSerializer):
    def to_representation(self, value):
        if isinstance(value, QuerySet):
            return self.get_list(value)
        return self.get_item(value)

    def get_list(self, value):
        attachments = []
        for question in value.all():
            attachments.extend(self.get_item(question))
        return attachments

    def get_item(self, value):
        service_context_instance = self.parent.instance
        answer_data = service_context_instance.answer.data.get('data')

        paths = []
        for question_answer in find_question_data(answer_data, value.pk):
            for item in question_answer.get('value', []):
                path = item.get('path')
                if path:
                    paths.append(path)

        if paths:
            response = [
                self._attachment_wrapper_from_path(path, value, None)
                for path in paths
            ]
            return [f for f in response if f]
        return []

    def _attachment_wrapper_from_path(self, path, question, file_object):
        if path:
            meta_info = ProxyStorageModel.objects.get(path=path)
            storage = meta_info.get_original_storage()
            filename = meta_info.original_name or path.split('/')[-1]
            namespace = meta_info.namespace or settings.MDS_OLD_NAMESPACE
            return super(AttachmentFromQuestionSerializer, self).to_representation(
                AttachmentFileObjectWrapper(
                    file_object=file_object,
                    path=path,
                    filename=filename,
                    frontend_url=storage.url(path, site_type='internal'),
                    content_type=guess_content_type(filename),
                    question=question,
                    namespace=namespace,
                )
            )
        return None


class RelatedRenderedCharField(RenderedCharField):
    def get_parent_object(self):
        return self.parent.parent.object


class IntegrationFileTemplateField(serializers.Serializer):
    type = serializers.CharField()
    name = serializers.CharField()
    content = serializers.CharField(source='template')

    class Meta:
        fields = (
            'type',
            'name',
            'content',
        )

    def to_representation(self, value):
        # todo: test me forms-666
        from events.surveyme_integration.models import IntegrationFileTemplate

        data = super(IntegrationFileTemplateField, self).to_representation(value)
        data['content'] = self._render_variables(value, data['content'])
        data['name'] = IntegrationFileTemplate.get_filename(data['name'], data['type'])
        return data

    def _render_variables(self, obj, content):
        # todo: test me forms-666
        parent_obj = self.parent.parent.instance
        return render_variables(
            value=content,
            object_with_variables=obj,
            answer=parent_obj.answer,
            trigger_data=parent_obj.trigger_data,
            notification_unique_id=parent_obj.notification_unique_id,
            force_render=parent_obj.force_render,
        )


class SafeFieldMixin(object):
    def get_attribute(self, instance):
        try:
            return super(SafeFieldMixin, self).get_attribute(instance)
        except (KeyError, AttributeError):
            if self.default is not empty:
                return self.get_default()
            raise


class SafeBooleanField(SafeFieldMixin, serializers.BooleanField):
    pass


class SafeCharField(SafeFieldMixin, serializers.CharField):
    pass


class SafeIntegerField(SafeFieldMixin, serializers.IntegerField):
    pass
