from rest_framework import serializers

from orchestra.api import router
from orchestra.utils.db import database_ready

from .models import Resource, ResourceData


class ResourceSerializer(serializers.ModelSerializer):
    name = serializers.SerializerMethodField()
    unit = serializers.ReadOnlyField()
    
    class Meta:
        model = ResourceData
        fields = ('name', 'used', 'allocated', 'unit')
        read_only_fields = ('used',)
    
    def to_internal_value(self, raw_data):
        data = super(ResourceSerializer, self).to_internal_value(raw_data)
        if not data.resource_id:
            data.resource = Resource.objects.get(name=raw_data['name'])
        return data
    
    def get_name(self, instance):
        return instance.resource.name
    
    def get_identity(self, data):
        return data.get('name')


# Monkey-patching section

def insert_resource_serializers():
    # clean previous state
    for related in Resource._related:
        try:
            viewset = router.get_viewset(related)
        except KeyError:
            # API viewset not registered
            pass
        else:
            fields = list(viewset.serializer_class.Meta.fields)
            try:
                fields.remove('resources')
            except ValueError:
                pass
            viewset.serializer_class.Meta.fields = fields
    # Create nested serializers on target models
    for ct, resources in Resource.objects.group_by('content_type').items():
        model = ct.model_class()
        try:
            router.insert(model, 'resources', ResourceSerializer, required=False, many=True, source='resource_set')
        except KeyError:
            continue
        # TODO this is a fucking workaround, reimplement this on the proper place
        def validate_resources(self, posted, _resources=resources):
            """ Creates missing resources """
            result = []
            resources = list(_resources)
            for data in posted:
                resource = data.resource
                if resource not in resources:
                    msg = "Unknown or duplicated resource '%s'." % resource
                    raise serializers.ValidationError(msg)
                resources.remove(resource)
                if not resource.on_demand and not data.allocated:
                    data.allocated = resource.default_allocation
                result.append(data)
            for resource in resources:
                data = ResourceData(resource=resource)
                if not resource.on_demand:
                    data.allocated = resource.default_allocation
                result.append(data)
            return result
        viewset = router.get_viewset(model)
        viewset.serializer_class.validate_resources = validate_resources
        
        old_options = viewset.options
        def options(self, request, resources=resources):
            """ Provides available resources description """
            metadata = old_options(self, request)
            metadata.data['available_resources'] = [
                {
                    'name': resource.name,
                    'on_demand': resource.on_demand,
                    'default_allocation': resource.default_allocation
                } for resource in resources
            ]
            return metadata
        viewset.options = options

if database_ready():
    insert_resource_serializers()