from django.contrib.contenttypes.models import ContentType
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers

from dcim.choices import *
from dcim.constants import *
from dcim.models import Cable, CablePath, CableTermination
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import BaseModelSerializer, GenericObjectSerializer, NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model

__all__ = (
    'CablePathSerializer',
    'CableSerializer',
    'CableTerminationSerializer',
    'CabledObjectSerializer',
    'TracedCableSerializer',
)


class CableSerializer(NetBoxModelSerializer):
    a_terminations = GenericObjectSerializer(many=True, required=False)
    b_terminations = GenericObjectSerializer(many=True, required=False)
    status = ChoiceField(choices=LinkStatusChoices, required=False)
    tenant = TenantSerializer(nested=True, required=False, allow_null=True)
    length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False, allow_null=True)

    class Meta:
        model = Cable
        fields = [
            'id', 'url', 'display_url', 'display', 'type', 'a_terminations', 'b_terminations', 'status', 'tenant',
            'label', 'color', 'length', 'length_unit', 'description', 'comments', 'tags', 'custom_fields', 'created',
            'last_updated',
        ]
        brief_fields = ('id', 'url', 'display', 'label', 'description')


class TracedCableSerializer(BaseModelSerializer):
    """
    Used only while tracing a cable path.
    """

    class Meta:
        model = Cable
        fields = [
            'id', 'url', 'display_url', 'type', 'status', 'label', 'color', 'length', 'length_unit', 'description',
        ]


class CableTerminationSerializer(NetBoxModelSerializer):
    termination_type = ContentTypeField(
        queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
    )
    termination = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = CableTermination
        fields = [
            'id', 'url', 'display', 'cable', 'cable_end', 'termination_type', 'termination_id',
            'termination', 'created', 'last_updated',
        ]

    @extend_schema_field(serializers.JSONField(allow_null=True))
    def get_termination(self, obj):
        serializer = get_serializer_for_model(obj.termination)
        context = {'request': self.context['request']}
        return serializer(obj.termination, nested=True, context=context).data


class CablePathSerializer(serializers.ModelSerializer):
    path = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = CablePath
        fields = ['id', 'path', 'is_active', 'is_complete', 'is_split']

    @extend_schema_field(serializers.ListField)
    def get_path(self, obj):
        ret = []
        for nodes in obj.path_objects:
            serializer = get_serializer_for_model(nodes[0])
            context = {'request': self.context['request']}
            ret.append(serializer(nodes, nested=True, many=True, context=context).data)
        return ret


class CabledObjectSerializer(serializers.ModelSerializer):
    cable = CableSerializer(nested=True, read_only=True, allow_null=True)
    cable_end = serializers.CharField(read_only=True)
    link_peers_type = serializers.SerializerMethodField(read_only=True, allow_null=True)
    link_peers = serializers.SerializerMethodField(read_only=True)
    _occupied = serializers.SerializerMethodField(read_only=True)

    @extend_schema_field(OpenApiTypes.STR)
    def get_link_peers_type(self, obj):
        """
        Return the type of the peer link terminations, or None.
        """
        if not obj.cable:
            return None

        if obj.link_peers:
            return f'{obj.link_peers[0]._meta.app_label}.{obj.link_peers[0]._meta.model_name}'

        return None

    @extend_schema_field(serializers.ListField)
    def get_link_peers(self, obj):
        """
        Return the appropriate serializer for the link termination model.
        """
        if not obj.link_peers:
            return []

        # Return serialized peer termination objects
        serializer = get_serializer_for_model(obj.link_peers[0])
        context = {'request': self.context['request']}
        return serializer(obj.link_peers, nested=True, many=True, context=context).data

    @extend_schema_field(serializers.BooleanField)
    def get__occupied(self, obj):
        return obj._occupied
