import json
import uuid

from guardian.shortcuts import assign_perm
from renkanmanager.models import Renkan, Workspace, Revision
from rest_framework import serializers


class RenkanSerializer(serializers.Serializer):
    id = serializers.ReadOnlyField(source="renkan_guid")
    current_revision_id = serializers.ReadOnlyField(source="current_revision_guid")
    workspace_id = serializers.CharField(source="workspace_guid", required=False)
    source_revision_id = serializers.CharField(source="source_revision_guid", required=False)
    revision_count = serializers.ReadOnlyField()
    created_by = serializers.StringRelatedField(source="creator")
    last_updated_by = serializers.SerializerMethodField("get_current_revision_last_updator")
    title = serializers.CharField(required=False)
    content = serializers.JSONField(required=False)
    creation_date = serializers.ReadOnlyField()
    modification_date = serializers.SerializerMethodField("get_current_revision_modification_date")
    create_new_revision = serializers.BooleanField(write_only=True, required=False) # only used for updating
    
    # ADD ERROR HANDLING
    def get_current_revision_last_updator(self, renkan):
        current_revision = Revision.objects.get(revision_guid = renkan.current_revision_guid)
        return current_revision.last_updated_by.username
    
    
    def get_current_revision_modification_date(self, renkan):
        current_revision = Revision.objects.get(revision_guid = renkan.current_revision_guid)
        return current_revision.modification_date
    
    def create(self, validated_data):
        """
            Method to create a new Renkan (and its first revision)
        """
        creator = validated_data.get('creator')
        initial_revision = Revision.objects.create(
            title = validated_data.get('title', 'Untitled renkan'),
            creator = creator,
            last_updated_by = creator
        )
        renkan = Renkan.objects.create(
            current_revision_guid = initial_revision.revision_guid,
            workspace_guid = validated_data.get('workspace_guid', ''),
            source_revision_guid = validated_data.get('source_revision_guid', ''),
            creator = creator
        )
        initial_revision.parent_renkan_guid = renkan.renkan_guid
        initial_revision.content = validated_data.get('content', json.dumps(
            {
                "id": str(renkan.renkan_guid),
                "title": initial_revision.title,
                "description": "",
                "created": str(initial_revision.creation_date),
                "updated": str(initial_revision.modification_date),
                "edges": [],
                "nodes": [],
                "users": [],
                "space_id": str(renkan.workspace_guid),
                "views": []
            }
        ))
        initial_revision.save()
        renkan.save()
        assign_perm('view_renkan', creator, renkan)
        assign_perm('change_renkan', creator, renkan)
        assign_perm('delete_renkan', creator, renkan)
        assign_perm('view_revision', creator, initial_revision)
        assign_perm('delete_revision', creator, initial_revision)
        return renkan
    
    def update(self, renkan, validated_data):
        """
            Method to update a Renkan object. Creates a new revision if needed
        """
        updator = validated_data.get('updator')
        current_revision = Revision.objects.get(revision_guid=renkan.current_revision_guid)  
        if validated_data.get("create_new_revision", False):
            revision_to_update = Revision.objects.create()
            revision_to_update.parent_renkan_guid = renkan.renkan_guid
            revision_to_update.creator = updator
            renkan.current_revision_guid = revision_to_update.revision_guid
        else:
            revision_to_update = current_revision
        revision_to_update.title = validated_data.get('title', current_revision.title)
        revision_to_update.content = validated_data.get('content', current_revision.content)
        revision_to_update.last_updated_by = updator
        revision_to_update.save()
        if not updator.has_perm('view_revision', revision_to_update):
            assign_perm('view_revision', updator, revision_to_update)
            assign_perm('delete_revision', updator, revision_to_update)
        renkan.save()
        return renkan
    
    def validate_workspace_id(self, value):
        if self.instance is not None:
            raise serializers.ValidationError("You cannot update workspace_guid")
        return value
    
    def validate_source_revision_id(self, value):
        if self.instance is not None:
            raise serializers.ValidationError("You cannot update source_revision_guid")
        return value
    
    def validate_content(self, value):
        
        try:
            json.loads(value)
        except ValueError:
            raise serializers.ValidationError("Content format is not a JSON-serializable")
        loaded_json = json.loads(value)
            
        if (not "nodes" in loaded_json):
            raise serializers.ValidationError("Content requires a 'nodes' entry")
        if (not "edges" in loaded_json):
            raise serializers.ValidationError("Content requires a 'edges' entry")
        if (not "views" in loaded_json):
            raise serializers.ValidationError("Content requires a 'views' entry")
            
        return value


class RevisionSerializer(serializers.Serializer):
    id = serializers.ReadOnlyField(source="revision_guid")
    parent_renkan_id = serializers.ReadOnlyField(source="parent_renkan_guid")
    workspace_id = serializers.SerializerMethodField("get_related_workspace_guid")
    title = serializers.ReadOnlyField()
    content = serializers.JSONField(read_only=True)
    renkan_created_by = serializers.SerializerMethodField("get_related_project_creator")
    renkan_creation_date = serializers.SerializerMethodField("get_related_project_creation_date")
    revision_created_by = serializers.StringRelatedField(source="creator")
    revision_last_updated_by = serializers.StringRelatedField(source="last_updated_by")
    revision_modification_date = serializers.ReadOnlyField(source="modification_date")
    
    def get_related_workspace_guid(self, revision):
        parent_renkan = Renkan.objects.get(renkan_guid = revision.parent_renkan_guid)
        return parent_renkan.workspace_guid
    
    def get_related_project_creator(self, revision):
        parent_project = Renkan.objects.get(renkan_guid = revision.parent_renkan_guid)
        return parent_project.creator.username
    
    def get_related_project_creation_date(self, revision):
        parent_project = Renkan.objects.get(renkan_guid = revision.parent_renkan_guid)
        return parent_project.creation_date
    

class WorkspaceSerializer(serializers.Serializer):
    id = serializers.ReadOnlyField(source="workspace_guid")
    workspace_created_by = serializers.StringRelatedField(source="creator")
    creation_date = serializers.ReadOnlyField()
    renkan_count = serializers.ReadOnlyField()
    title = serializers.CharField()
    
    def create(self, validated_data):
        creator = validated_data.get('creator')
        workspace = Workspace.objects.create()
        workspace.title = validated_data.get('title', '')
        workspace.creator = creator
        workspace.save()
        assign_perm('view_workspace', creator, workspace)
        assign_perm('change_workspace', creator, workspace)
        assign_perm('delete_workspace', creator, workspace)
        return workspace
    
    def update(self, workspace, validated_data):
        workspace.title = validated_data.get('title', '')
        workspace.save()
        return workspace