"""
    Copyright (C) 2017, ContraxSuite, LLC

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    You can also be released from the requirements of the license by purchasing
    a commercial license from ContraxSuite, LLC. Buying such a license is
    mandatory as soon as you develop commercial activities involving ContraxSuite
    software without disclosing the source code of your own applications.  These
    activities include: offering paid services to customers as an ASP or "cloud"
    provider, processing documents on the fly in a web application,
    or shipping ContraxSuite within a closed source product.
"""
# -*- coding: utf-8 -*-

import datetime

import dataclasses
from django.db import transaction
from django.db.models import Subquery
from rest_framework import serializers, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response

from apps.common.mixins import SimpleRelationSerializer
from apps.document.api.v1 import BaseDocumentSerializer, DocumentNoteDetailSerializer, Document, DocumentField
from apps.document.constants import DocumentSystemField
from apps.document.repository.document_field_repository import DocumentFieldRepository
from apps.document.repository.dto import FieldValueDTO
from apps.document.tasks import plan_process_document_changed

__author__ = "ContraxSuite, LLC; LexPredict, LLC"
__copyright__ = "Copyright 2015-2020, ContraxSuite, LLC"
__license__ = "https://github.com/LexPredict/lexpredict-contraxsuite/blob/1.6.0/LICENSE"
__version__ = "1.6.0"
__maintainer__ = "LexPredict, LLC"
__email__ = "support@contraxsuite.com"


class DocumentWithFieldsDTOSerializer(BaseDocumentSerializer):
    """
    Serializer for document review page with detailed document field values
    """
    field_repo = DocumentFieldRepository()

    field_values = serializers.SerializerMethodField()
    notes = DocumentNoteDetailSerializer(source='documentnote_set', many=True)
    prev_id = serializers.SerializerMethodField()
    next_id = serializers.SerializerMethodField()
    sections = serializers.SerializerMethodField()

    class Meta:
        model = Document
        fields = ['pk', 'name', 'document_type', 'file_size',
                  'status', 'status_data', 'available_statuses_data',
                  'assignee', 'assign_date', 'assignee_data', 'available_assignees_data',
                  'description', 'title', 'full_text', 'notes',
                  'field_values',
                  'prev_id', 'next_id', 'sections', 'cluster_id']

    def get_neighbours(self, document, use_saved_filter=True):
        prev_id = next_id = None
        user = self.context['request'].user
        project = document.project
        from apps.rawdb.api.v1 import DocumentsAPIView

        ids = DocumentsAPIView.simulate_get(user, project, use_saved_filter=use_saved_filter)

        if document.pk in ids:
            pos = ids.index(document.pk)
        else:
            return self.get_neighbours(document, use_saved_filter=False)

        prev_ids = ids[:pos]
        if prev_ids:
            prev_id = prev_ids[-1]
        next_ids = ids[pos + 1:]
        if next_ids:
            next_id = next_ids[0]
        return prev_id, next_id

    def get_prev_id(self, obj):
        return self.get_neighbours(obj)[0]

    def get_next_id(self, obj):
        return self.get_neighbours(obj)[1]

    def get_sections(self, obj):
        if isinstance(obj.metadata, dict) and 'sections' in obj.metadata:
            return obj.metadata['sections']

    def get_field_values(self, doc: Document):
        fvals = self.field_repo.get_document_field_val_dtos(doc_id=doc.pk)
        for code in fvals:
            fvals[code] = dataclasses.asdict(fvals[code])
        return fvals

    def update(self, instance: Document, validated_data):
        with transaction.atomic():
            system_fields_changed = list()

            new_status = validated_data.get('status')
            if new_status is not None and new_status.pk != instance.status_id:
                is_active = instance.status and instance.status.is_active
                if new_status.is_active != is_active:
                    field_ids = self.field_repo.get_doc_field_ids_with_values(instance.pk)
                    DocumentField.objects \
                        .filter(document_type_id=instance.document_type_id, pk__in=Subquery(field_ids)) \
                        .update(dirty=True)
                system_fields_changed.append(DocumentSystemField.status.value)

            user = self.context['request'].user  # type: User
            new_assignee = validated_data.get('assignee')
            prev_assignee = instance.assignee
            if new_assignee is None and prev_assignee is not None:
                validated_data['assign_date'] = None
                system_fields_changed.append(DocumentSystemField.assignee.value)
            elif new_assignee is not None and (prev_assignee is None or new_assignee.pk != prev_assignee.pk):
                validated_data['assign_date'] = datetime.datetime.now(tz=user.get_time_zone())
                system_fields_changed.append(DocumentSystemField.assignee.value)

            res = super().update(instance, validated_data)

            plan_process_document_changed(doc_id=instance.pk,
                                          system_fields_changed=system_fields_changed,
                                          generic_fields_changed=False,
                                          user_fields_changed=False,
                                          changed_by_user_id=user.pk)
            return res


class FieldValueDTOSerializer(SimpleRelationSerializer):
    class Meta:
        model = FieldValueDTO
        fields = ['field_value', 'annotations']


class FieldValueViewSet(viewsets.ModelViewSet):
    """
    list: Annotation (Document Field Value) List
    retrieve: Retrieve Annotation (Document Field Value)
    create: Create Annotation (Document Field Value)
    update: Update Annotation (Document Field Value)
    delete: Delete Annotation (Document Field Value)
    """
    field_repo = DocumentFieldRepository()
    queryset = field_repo.get_all_docfieldvalues()
    serializer_class = FieldValueDTOSerializer
    http_method_names = ['put', 'patch']

    @action(detail=True, methods=['patch'])
    def patch_fields(self, request):
        """
        http.patch
        {
          "field_code1": { "field_value": ....,
                           "annotations": [ {"location_start": ...,
                                             "location_end": ....,
                                             "annotation_value": ....}]
                         },
        }
        """
        res = self.field_repo.update_field_values(request.data)
        return Response(res)

    def update(self, request, pk=None):
        # $http.put()
        res = self.field_repo.update_field_values(request.data)
        return Response(res)