# coding: utf-8

import re
from typing import Mapping, Optional

from django.conf import settings
from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist
from django.db import transaction
from django.db.models.functions import Now
from rest_framework import status
from rest_framework.parsers import MultiPartParser
from rest_framework.response import Response

from procu.api import models
from procu.api.common.comment import create_quote_comment
from procu.api.enquiry.comment.utils import create_enquiry_comment
from procu.rest import exceptions, generics, serializers
from procu.wf.api import wiki_format
from .serializers import ReplySerializer

ID_REGEX = re.compile(r'YP-?(\d+)', flags=re.I)


class NoAuth(Exception):
    pass


def is_conflict(key):
    return cache.get(f'reply_{key}') == 1


def identify_quote(
    enquiry: Optional[models.Enquiry], data: Mapping
) -> (int, bool):

    # Token authentication
    try:
        reply = models.QuoteReply.objects.only('quote').get(token=data['token'])
        return (reply.quote_id, False)

    except ObjectDoesNotExist:
        pass

    if enquiry is None:
        raise NoAuth

    try:
        quote = models.Quote.objects.only('id').get(
            supplier__agents__email__iexact=data['from_email'],
            request__enquiry=enquiry,
        )
        return (quote.id, True)

    except ObjectDoesNotExist:
        pass

    raise NoAuth


class ReplyView(generics.GenericAPIView):
    serializer_class = ReplySerializer
    parser_classes = (MultiPartParser,)
    authentication_classes = ()

    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        data = serializer.validated_data

        # Initial state
        enquiry = quote = user = None
        is_suspicious = False

        # ----------------------------------------------------------------------

        def register_comment():
            key = 'reply_{}'.format(data['unique'])
            cache.set(key, 1, timeout=3600 * 24)

        transaction.on_commit(register_comment)

        # ----------------------------------------------------------------------
        # Identifying enquiry

        enquiries = [int(x) for x in data['enquiries'].split()]

        try:
            enquiry = models.Enquiry.objects.get(id=enquiries[0])

        except (IndexError, ObjectDoesNotExist):
            pass

        # ----------------------------------------------------------------------
        # Identifying user

        try:
            user = models.User.objects.get(
                email__iexact=data['from_email'], is_deleted=False
            )

        except ObjectDoesNotExist:
            pass

        # ----------------------------------------------------------------------
        # Identifying quote

        if user is not None:
            try:
                quote_id, is_suspicious = identify_quote(enquiry, data)
                quote = models.Quote.objects.permitted(user).get(id=quote_id)

                if 'api.create_quotecomment' not in quote.permissions(user):
                    return Response(
                        {'result': 'denied'}, status=status.HTTP_403_FORBIDDEN
                    )

            except (NoAuth, ObjectDoesNotExist):
                pass

        # ----------------------------------------------------------------------
        # Check if the message has already been processed

        if is_conflict(data['unique']):
            return Response(
                {'result': 'conflict'}, status=status.HTTP_409_CONFLICT
            )

        # ----------------------------------------------------------------------
        # Extract content

        files_data = [
            content
            for field, content in request.data.items()
            if field.startswith('file_')
        ]
        serializer = serializers.ListSerializer(
            child=serializers.FileField(), data=files_data
        )
        serializer.is_valid(raise_exception=True)
        files = serializer.validated_data

        attachment_ids = []

        for file in files:
            obj = models.Attachment.objects.create(
                author=user, filename=file.name, file=file
            )
            attachment_ids.append(obj.id)

        text = data['text'].strip()
        text_cut = data.get('text_cut', None)

        # ----------------------------------------------------------------------

        if quote and user:

            if text_cut:
                message_wiki = '\n'.join(
                    ['""', text, '""', '<{ ...', '""', text_cut, '""', '}>']
                )
                text_html = wiki_format(message_wiki)

            else:
                text_html = None

            create_quote_comment(
                quote_id=quote.id,
                user=user,
                data={
                    'message': text or text_cut,
                    'message_html': text_html,
                    'is_from_email': True,
                    'is_suspicious': is_suspicious,
                },
                attachment_ids=attachment_ids,
            )

            models.Quote.objects.filter(id=quote.id).update(updated_at=Now())
            models.Enquiry.objects.filter(request__quotes=quote.id).update(
                updated_at=Now()
            )

        elif enquiry:

            messages = '\n'.join(
                [
                    'Получил ответ на уведомление по этому запросу, '
                    'но не смог понять, к какому предложению он относится:',
                    '',
                    'От: {}'.format(data['from_email']),
                    'Тема: {}'.format(data['subject']),
                    '\n',
                    '<{ Текст сообщения',
                    '""',
                    text,
                    text_cut,
                    '""',
                    '}>',
                ]
            )
            message_html = wiki_format(messages)

            user = models.User.objects.get_or_create_by_login(
                settings.ROBOT_LOGIN
            )

            create_enquiry_comment(
                enquiry_id=enquiry.id,
                user=user,
                data={
                    'message': messages,
                    'message_html': message_html,
                    'is_from_email': True,
                },
                invitees=[],
                attachment_ids=attachment_ids,
                notify=True,
            )
            enquiry.bump()

        else:
            raise exceptions.ValidationError('Reply is not identified')

        return Response({'result': 'success'})
