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

from django.conf import settings
from rest_framework import serializers

from events.rest_framework_contrib.mixins import TranslationSerializerMixin
from events.common_app.serializers import IdentifierField
from events.common_app.utils import get_tld
from events.common_storages.utils import get_mds_url
from events.surveyme.answer import fetch_answer_data
from events.surveyme.fields.base.serializers import (
    RedirectSerializer,
    FooterSerializer,
    TeaserSerializer,
    StatsSerializer,
)
from events.surveyme.models import Survey, SurveyGroup, ProfileSurveyAnswer, SurveyQuestion
from events.surveyme.api_admin.v2.serializers import SurveyStyleTemplateSerializer
from events.surveyme.dataclasses import Answer, SurveyQuiz


class SurveysFormField(serializers.HyperlinkedIdentityField):
    def to_representation(self, obj):
        view_name = self.view_name
        request = self.context.get('request', None)
        format = self.format or self.context.get('format', None)
        return self.get_url(obj, view_name, request, format)


class IsAlreadyAnsweredField(serializers.Field):
    def to_representation(self, value):
        return value(self.context['request'].user)


class SurveyTextsDictField(serializers.Field):
    def to_representation(self, value):
        result = {}
        for text in value.all():  # use prefetch_related('texts')
            result[text.slug] = text.get_value()
        return result


class SurveySerializer(serializers.ModelSerializer):
    class Meta:
        model = Survey
        fields = (
            'id',
            'name',
            'slug',
        )


class SurveyGroupSerializer(serializers.ModelSerializer):
    class Meta:
        model = SurveyGroup
        fields = (
            'id',
            'metrika_counter_code'
        )


class SurveySerializerDetail(TranslationSerializerMixin, SurveySerializer):
    is_user_already_answered = IsAlreadyAnsweredField(source='is_already_answered_by_profile')
    texts = SurveyTextsDictField()
    allow_post_conditions = serializers.ListField(source='get_submit_conditions', read_only=True)
    group = SurveyGroupSerializer(read_only=True)
    is_user_can_answer = serializers.SerializerMethodField()
    why_user_cant_answer = serializers.SerializerMethodField()
    styles_template = SurveyStyleTemplateSerializer(read_only=True)
    org_id = serializers.IntegerField(source='org.dir_id', read_only=True)
    redirect = RedirectSerializer(read_only=True, source='extra.redirect', default=None)
    footer = FooterSerializer(read_only=True, source='extra.footer', default=None)
    teaser = TeaserSerializer(read_only=True, source='extra.teaser', default=None)
    stats = StatsSerializer(read_only=True, source='extra.stats', default=None)

    def get_is_user_can_answer(self, obj):
        view = self.context['view']
        return view.get_is_profile_can_answer(obj)

    def get_why_user_cant_answer(self, obj):
        view = self.context['view']
        return view.get_why_profile_cant_answer(obj)

    class Meta(SurveySerializer.Meta):
        fields = SurveySerializer.Meta.fields + (
            'is_user_already_answered',
            'texts',
            'allow_post_conditions',
            'metrika_counter_code',
            'is_only_for_iframe',
            'group',
            'is_user_can_answer',
            'why_user_cant_answer',
            'is_answer_could_be_edited',
            'styles_template',
            'org_id',
            'is_public',
            'is_published_external',
            'redirect',
            'footer',
            'teaser',
            'stats',
        )


class _AnswerTypeSerializer(serializers.Serializer):
    slug = serializers.SlugField(required=True)
    name = serializers.CharField(required=False, allow_null=True, allow_blank=True)


class _SurveyQuestionSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    label = serializers.CharField(required=False, allow_null=True, allow_blank=True)
    slug = serializers.CharField(required=True)
    value = serializers.JSONField(required=True, allow_null=True)
    answer_type = _AnswerTypeSerializer(required=False)


class ValidatorDataSerializer(serializers.Serializer):
    id = IdentifierField(required=False)
    slug = serializers.SlugField(required=False, allow_null=True, allow_blank=True)
    name = serializers.CharField(required=False, allow_null=True, allow_blank=True)
    questions = _SurveyQuestionSerializer(many=True, allow_empty=True)


class ValidatorResultSerializer(serializers.Serializer):
    """
        Example:
        {
          "status": "ERROR",
          "errors": {
            "answer_short_text_30385": [ "Incorrect value" ],
            "answer_long_text_30391": [ "Incorrect answer type", "Incorrect value" ]
          }
        }
    """
    status = serializers.ChoiceField(choices=['OK', 'ERROR'])
    errors = serializers.DictField(child=serializers.ListField(allow_empty=True), required=False)


class ScoreResultsSerializer(serializers.ModelSerializer):
    survey_id = IdentifierField(read_only=True)
    scores = serializers.SerializerMethodField()
    total_scores = serializers.SerializerMethodField()
    title = serializers.SerializerMethodField()
    description = serializers.SerializerMethodField()
    image = serializers.SerializerMethodField()
    show_results = serializers.SerializerMethodField()
    show_correct = serializers.SerializerMethodField()

    class Meta:
        model = ProfileSurveyAnswer
        fields = (
            'survey_id',
            'show_results',
            'show_correct',
            'scores',
            'total_scores',
            'title',
            'description',
            'image',
        )

    def get_show_results(self, answer):
        extra = answer.survey.extra or {}
        quiz = SurveyQuiz(extra.get('quiz'))
        return bool(quiz.show_results)

    def get_show_correct(self, answer):
        extra = answer.survey.extra or {}
        quiz = SurveyQuiz(extra.get('quiz'))
        return bool(quiz.show_correct)

    def get_scores(self, answer):
        answer_data = Answer(answer.data)
        if answer_data.quiz.scores is not None:
            return round(answer_data.quiz.scores, 2)

    def get_total_scores(self, answer):
        answer_data = Answer(answer.data)
        if answer_data.quiz.total_scores is not None:
            return round(answer_data.quiz.total_scores, 2)

    def get_title(self, answer):
        answer_data = Answer(answer.data)
        return answer_data.quiz.title

    def get_description(self, answer):
        answer_data = Answer(answer.data)
        return answer_data.quiz.description

    def get_image(self, answer):
        answer_data = Answer(answer.data)
        image_path = answer_data.quiz.image_path
        if image_path:
            return {
                'links': get_image_links(image_path.strip('/'))
            }


def get_image_link(image_path, image_size):
    avatars_host = settings.AVATARS_HOST
    image_namespace = settings.IMAGE_NAMESPACE
    return f'{avatars_host}get-{image_namespace}/{image_path}/{image_size}'


def get_image_links(image_path):
    return {
        image_size: get_image_link(image_path, image_size)
        for image_size in settings.IMAGE_SIZES_AS_STR
    }


class AnswerResultsSerializer(serializers.ModelSerializer):
    answer_key = serializers.CharField(source='secret_code', read_only=True)
    survey_id = IdentifierField(read_only=True)
    survey_name = serializers.CharField(source='survey.get_name', read_only=True)
    created = serializers.DateTimeField(source='date_created', read_only=True)
    scores = serializers.SerializerMethodField()
    data = serializers.SerializerMethodField()

    def get_scores(self, obj):
        if obj.data:
            return obj.data.get('quiz')

    def get_data(self, obj):
        tld = self._get_tld()
        show_correct_results = self._show_correct_results(obj)
        if obj.data:
            questions = self.get_questions(obj.survey_id)
            for answer_question in fetch_answer_data(obj.data):
                question_data = answer_question.get('question', {})
                question_pk = question_data.get('id')
                question = questions.get(question_pk)
                answer_slug = question_data.get('answer_type', {}).get('slug')
                if question:
                    question_data['label'] = question.get_label()
                    question_data['is_deleted'] = question.is_deleted
                    question_data['is_hidden'] = question.param_is_hidden

                    if show_correct_results and question.param_quiz and question.param_quiz.get('enabled'):
                        quiz_answers = question.param_quiz.get('answers') or []
                        if answer_slug == 'answer_choices':
                            question_data['choices'] = [
                                self._get_choice(choice, quiz_answers)
                                for choice in question.surveyquestionchoice_set.all()
                            ]
                        elif answer_slug == 'answer_short_text':
                            question_data['correct_value'] = self._get_correct_value(quiz_answers)

                if answer_slug == 'answer_files':
                    for file_item in answer_question.get('value') or []:
                        file_item['path'] = get_mds_url(file_item.get('path'), tld=tld)
            return obj.data.get('data')

    def _get_tld(self):
        context = self.context or {}
        request = context.get('request')
        if request:
            return get_tld(request.headers.get('referer'))

    def _is_admin_site(self):
        from events.surveyme.api_admin.v2.views_api import AnswerResultsView
        context = self.context or {}
        return isinstance(context.get('view'), AnswerResultsView)

    def _show_correct_results(self, obj):
        extra = obj.survey.extra or {}
        quiz = extra.get('quiz') or {}
        return bool(self._is_admin_site() or quiz.get('show_correct'))

    def _get_choice(self, choice, answers):
        label = choice.get_label()
        return {
            'key': str(choice.pk),
            'slug': choice.slug,
            'text': label,
            'correct': self._check_choice_answer(label, answers),
        }

    def _check_choice_answer(self, label, answers):
        for answer in answers:
            if answer.get('value') == label:
                return bool(answer.get('correct'))
        return False

    def _get_correct_value(self, answers):
        return ','.join(
            str(answer.get('value'))
            for answer in answers
            if answer.get('correct')
        )

    def get_questions(self, survey_id):
        question_qs = (
            SurveyQuestion.with_deleted_objects.using(settings.DATABASE_ROLOCAL)
            .prefetch_related('surveyquestionchoice_set')
            .filter(survey_id=survey_id)
            .order_by()
        )
        return {
            question.pk: question
            for question in question_qs
        }

    class Meta:
        model = ProfileSurveyAnswer
        fields = (
            'id',
            'answer_key',
            'survey_id',
            'survey_name',
            'created',
            'scores',
            'data',
        )


class SurveySuccessSerializer(serializers.ModelSerializer):
    group = SurveyGroupSerializer()
    redirect = serializers.DictField(source='extra.redirect', default=None)
    footer = serializers.DictField(source='extra.footer', default=None)
    teaser = serializers.DictField(source='extra.teaser', default=None)
    texts = SurveyTextsDictField()
    styles_template = SurveyStyleTemplateSerializer()
    integrations = serializers.SerializerMethodField()
    stats = serializers.SerializerMethodField()
    answer = serializers.SerializerMethodField()
    scores = serializers.SerializerMethodField()

    def get_integrations(self, survey):
        from events.surveyme_integration.api_admin.v2.serializers import HookSubscriptionNotificationResultSerializer
        from events.surveyme_integration.models import HookSubscriptionNotification
        answer_key = self.context.get('answer_key')
        if answer_key:
            queryset = (
                HookSubscriptionNotification.objects.filter(
                    survey_id=survey.pk,
                    answer__secret_code=answer_key,
                )
                .filter_notifications()
            )
            serializer = HookSubscriptionNotificationResultSerializer(queryset, many=True)
            return serializer.data or None

    def get_stats(self, survey):
        from events.countme.stats import get_stats_info
        stats = (survey.extra and survey.extra.get('stats')) or {}
        if stats.get('enabled'):
            return get_stats_info(survey)

    def _get_answer(self, survey, answer_key):
        if 'answer' in self.context:
            answer = self.context['answer']
        else:
            try:
                answer = (
                    ProfileSurveyAnswer.objects.select_related('survey')
                    .get(survey_id=survey.pk, secret_code=answer_key)
                )
            except ProfileSurveyAnswer.DoesNotExist:
                answer = None
            self.context['answer'] = answer
        return answer

    def get_answer(self, survey):
        answer_key = self.context.get('answer_key')
        quiz = (survey.extra and survey.extra.get('quiz')) or {}
        if answer_key and quiz.get('show_correct'):
            answer = self._get_answer(survey, answer_key)
            if answer:
                serializer = AnswerResultsSerializer(answer)
                return serializer.data

    def get_scores(self, survey):
        answer_key = self.context.get('answer_key')
        quiz = (survey.extra and survey.extra.get('quiz')) or {}
        if answer_key and quiz.get('show_results'):
            answer = self._get_answer(survey, answer_key)
            if answer:
                scores = copy.copy(answer.data.get('quiz'))
                if scores and scores.get('image_path'):
                    scores['image'] = {
                        'links': get_image_links(scores['image_path'].strip('/'))
                    }
                return scores

    class Meta:
        model = Survey
        fields = (
            'id',
            'slug',
            'name',
            'metrika_counter_code',
            'group',
            'redirect',
            'footer',
            'teaser',
            'texts',
            'styles_template',
            'integrations',
            'stats',
            'answer',
            'scores',
        )
