import json
import re

from django.contrib.contenttypes.models import ContentType
from django.db.models import ProtectedError
from django.shortcuts import get_object_or_404
from rest_framework import permissions, serializers, status
from rest_framework.response import Response
from rest_framework.views import APIView

from mes.models import CapacityUnitEnum, ProductionAssignment, ProductionAssignmentStatusEnum
from mes import services as mes_services
from mes.models import Device, DeviceCategory


EXTRA_JSON_PATH_SEGMENT_RE = re.compile(r'^[A-Za-z0-9_-]+$')


def _get_employee(request):
    employee = getattr(request.user, "employee", None)
    if employee is None:
        raise serializers.ValidationError("当前用户未关联员工")
    return employee


def _employee_payload(employee):
    if employee is None:
        return None
    return {
        "id": employee.id,
        "name": employee.name,
        "merchant_id": employee.merchant_id,
    }


def _user_payload(user):
    if user is None:
        return None
    return {
        "id": user.id,
        "username": user.username,
    }


def _parse_extra_filter(extra_path: str | None, extra_value: str | None):
    if not extra_path and extra_value is None:
        return None, mes_services.UNSET
    if not extra_path or extra_value is None:
        raise serializers.ValidationError({'detail': 'extra_path 和 extra_value 必须同时传入'})

    normalized_segments = [segment.strip() for segment in extra_path.split('.') if segment.strip()]
    if not normalized_segments:
        raise serializers.ValidationError({'detail': 'extra_path 不合法'})
    for segment in normalized_segments:
        if not EXTRA_JSON_PATH_SEGMENT_RE.match(segment):
            raise serializers.ValidationError({'detail': 'extra_path 不合法'})

    try:
        parsed_value = json.loads(extra_value)
    except json.JSONDecodeError:
        parsed_value = extra_value
    return normalized_segments, parsed_value


class DeviceCategoryWriteSerializer(serializers.Serializer):
    name = serializers.CharField(allow_blank=False, max_length=100)


class DeviceWriteSerializer(serializers.Serializer):
    name = serializers.CharField(required=False, allow_blank=False, max_length=100)
    category = serializers.IntegerField(required=False, min_value=1)
    peak_capacity = serializers.IntegerField(required=False, min_value=1)
    capacity_unit = serializers.ChoiceField(required=False, choices=CapacityUnitEnum.choices)
    extra = serializers.JSONField(required=False, allow_null=True)

    def __init__(self, *args, **kwargs):
        self.is_create = kwargs.pop("is_create", False)
        super().__init__(*args, **kwargs)
        if self.is_create:
            self.fields["name"].required = True
            self.fields["category"].required = True
            self.fields["peak_capacity"].required = True

    def validate_category(self, value):
        employee = self.context["employee"]
        try:
            return DeviceCategory.objects.get(id=value, merchant=employee.merchant)
        except DeviceCategory.DoesNotExist as exc:
            raise serializers.ValidationError("设备分类不存在") from exc


class DeviceCategorySerializer(serializers.ModelSerializer):
    created_by = serializers.SerializerMethodField()
    operator = serializers.SerializerMethodField()

    class Meta:
        model = DeviceCategory
        fields = [
            "id",
            "merchant",
            "name",
            "created_by",
            "operator",
            "created_at",
            "updated_at",
        ]
        read_only_fields = fields

    def get_created_by(self, obj):
        return _user_payload(obj.created_by)

    def get_operator(self, obj):
        return _employee_payload(obj.operator)


class DeviceSerializer(serializers.ModelSerializer):
    created_by = serializers.SerializerMethodField()
    operator = serializers.SerializerMethodField()
    category_name = serializers.CharField(source="category.name", read_only=True)
    category = serializers.IntegerField(source="category_id", read_only=True)
    capacity_unit_label = serializers.CharField(source='get_capacity_unit_display', read_only=True)

    class Meta:
        model = Device
        fields = [
            "id",
            "merchant",
            "category",
            "category_name",
            "name",
            "peak_capacity",
            "capacity_unit",
            "capacity_unit_label",
            "extra",
            "created_by",
            "operator",
            "created_at",
            "updated_at",
        ]
        read_only_fields = fields

    def get_created_by(self, obj):
        return _user_payload(obj.created_by)

    def get_operator(self, obj):
        return _employee_payload(obj.operator)


class ProductionAssignmentWriteSerializer(serializers.Serializer):
    device = serializers.IntegerField(required=False, allow_null=True, min_value=1)
    content_type = serializers.IntegerField(required=False, min_value=1)
    object_id = serializers.IntegerField(required=False, min_value=1)
    assigner = serializers.IntegerField(required=False, min_value=1)
    assignee = serializers.IntegerField(required=False, min_value=1, allow_null=True)
    production_quantity = serializers.IntegerField(required=False, min_value=1)
    status = serializers.ChoiceField(required=False, choices=ProductionAssignmentStatusEnum.choices)
    extra = serializers.JSONField(required=False, allow_null=True)

    def __init__(self, *args, **kwargs):
        self.is_create = kwargs.pop("is_create", False)
        super().__init__(*args, **kwargs)
        if self.is_create:
            self.fields["content_type"].required = True
            self.fields["object_id"].required = True
            self.fields["assigner"].required = True
            self.fields["production_quantity"].required = True

    def validate_device(self, value):
        if value is None:
            return None
        employee = self.context["employee"]
        try:
            return Device.objects.get(id=value, merchant=employee.merchant)
        except Device.DoesNotExist as exc:
            raise serializers.ValidationError("设备不存在") from exc

    def validate_content_type(self, value):
        try:
            return ContentType.objects.get(id=value)
        except ContentType.DoesNotExist as exc:
            raise serializers.ValidationError("关联对象类型不存在") from exc

    def validate_assigner(self, value):
        employee = self.context["employee"]
        try:
            return employee.merchant.employees.get(id=value)
        except Exception as exc:
            raise serializers.ValidationError("指派者不存在") from exc

    def validate_assignee(self, value):
        if value is None:
            return None
        employee = self.context["employee"]
        try:
            return employee.merchant.employees.get(id=value)
        except Exception as exc:
            raise serializers.ValidationError("被指派人不存在") from exc


class ProductionAssignmentSerializer(serializers.ModelSerializer):
    device_name = serializers.SerializerMethodField(read_only=True)
    assigner = serializers.SerializerMethodField()
    assignee = serializers.SerializerMethodField()
    created_by = serializers.SerializerMethodField()
    operator = serializers.SerializerMethodField()
    status_label = serializers.CharField(source='get_status_display', read_only=True)

    class Meta:
        model = ProductionAssignment
        fields = [
            'id',
            'merchant',
            'device',
            'device_name',
            'content_type',
            'object_id',
            'assigner',
            'assignee',
            'production_quantity',
            'status',
            'status_label',
            'extra',
            'created_by',
            'operator',
            'created_at',
            'updated_at',
        ]
        read_only_fields = fields

    def get_device_name(self, obj):
        if obj.device is None:
            return None
        return obj.device.name

    def get_assigner(self, obj):
        return _employee_payload(obj.assigner)

    def get_assignee(self, obj):
        return _employee_payload(obj.assignee)

    def get_created_by(self, obj):
        return _user_payload(obj.created_by)

    def get_operator(self, obj):
        return _employee_payload(obj.operator)


class DeviceCategoryListCreateView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        employee = _get_employee(request)
        queryset = mes_services.list_device_categories_for_merchant(merchant=employee.merchant)
        name = request.query_params.get("name")
        if name:
            queryset = queryset.filter(name__icontains=name.strip())
        return Response(DeviceCategorySerializer(queryset, many=True).data)

    def post(self, request):
        employee = _get_employee(request)
        serializer = DeviceCategoryWriteSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        try:
            category = mes_services.create_device_category(
                merchant=employee.merchant,
                name=serializer.validated_data["name"],
                created_by=request.user,
                operator=employee,
            )
        except ValueError as exc:
            return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(DeviceCategorySerializer(category).data, status=status.HTTP_201_CREATED)


class DeviceCategoryDetailView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self, request, category_id):
        employee = _get_employee(request)
        return get_object_or_404(
            mes_services.list_device_categories_for_merchant(merchant=employee.merchant),
            id=category_id,
        )

    def get(self, request, category_id):
        category = self.get_object(request, category_id)
        return Response(DeviceCategorySerializer(category).data)

    def patch(self, request, category_id):
        employee = _get_employee(request)
        category = self.get_object(request, category_id)
        serializer = DeviceCategoryWriteSerializer(data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        try:
            category = mes_services.update_device_category(
                category=category,
                operator=employee,
                name=serializer.validated_data.get("name", mes_services.UNSET),
            )
        except ValueError as exc:
            return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(DeviceCategorySerializer(category).data)

    def delete(self, request, category_id):
        employee = _get_employee(request)
        category = self.get_object(request, category_id)
        try:
            mes_services.delete_device_category(category=category, operator=employee)
        except ProtectedError:
            return Response({"detail": "设备分类已被设备引用，不能删除"}, status=status.HTTP_400_BAD_REQUEST)
        return Response(status=status.HTTP_204_NO_CONTENT)


class DeviceListCreateView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        employee = _get_employee(request)
        category_id = request.query_params.get("category")
        extra_json_path, extra_json_value = _parse_extra_filter(
            request.query_params.get("extra_path"),
            request.query_params.get("extra_value"),
        )
        queryset = mes_services.list_devices_for_merchant(
            merchant=employee.merchant,
            category_id=int(category_id) if category_id else None,
            extra_json_path=extra_json_path,
            extra_json_value=extra_json_value,
        )
        name = request.query_params.get("name")
        if name:
            queryset = queryset.filter(name__icontains=name.strip())
        return Response(DeviceSerializer(queryset, many=True).data)

    def post(self, request):
        employee = _get_employee(request)
        serializer = DeviceWriteSerializer(data=request.data, is_create=True, context={"employee": employee})
        serializer.is_valid(raise_exception=True)
        try:
            device = mes_services.create_device(
                merchant=employee.merchant,
                category=serializer.validated_data["category"],
                name=serializer.validated_data["name"],
                created_by=request.user,
                operator=employee,
                peak_capacity=serializer.validated_data['peak_capacity'],
                capacity_unit=serializer.validated_data.get('capacity_unit', CapacityUnitEnum.METER),
                extra=serializer.validated_data.get("extra"),
            )
        except ValueError as exc:
            return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(DeviceSerializer(device).data, status=status.HTTP_201_CREATED)


class DeviceDetailView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self, request, device_id):
        employee = _get_employee(request)
        return get_object_or_404(
            mes_services.list_devices_for_merchant(merchant=employee.merchant),
            id=device_id,
        )

    def get(self, request, device_id):
        device = self.get_object(request, device_id)
        return Response(DeviceSerializer(device).data)

    def patch(self, request, device_id):
        employee = _get_employee(request)
        device = self.get_object(request, device_id)
        serializer = DeviceWriteSerializer(data=request.data, partial=True, context={"employee": employee})
        serializer.is_valid(raise_exception=True)
        try:
            device = mes_services.update_device(
                device=device,
                operator=employee,
                name=serializer.validated_data.get("name", mes_services.UNSET),
                category=serializer.validated_data.get("category", mes_services.UNSET),
                peak_capacity=serializer.validated_data.get('peak_capacity', mes_services.UNSET),
                capacity_unit=serializer.validated_data.get('capacity_unit', mes_services.UNSET),
                extra=serializer.validated_data.get("extra", mes_services.UNSET),
            )
        except ValueError as exc:
            return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(DeviceSerializer(device).data)

    def delete(self, request, device_id):
        employee = _get_employee(request)
        device = self.get_object(request, device_id)
        mes_services.delete_device(device=device, operator=employee)
        return Response(status=status.HTTP_204_NO_CONTENT)


class ProductionAssignmentListCreateView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        employee = _get_employee(request)
        device_id = request.query_params.get('device')
        content_type_id = request.query_params.get('content_type')
        object_id = request.query_params.get('object_id')
        status_value = request.query_params.get('status')
        queryset = mes_services.list_production_assignments_for_merchant(
            merchant=employee.merchant,
            device_id=int(device_id) if device_id else None,
            content_type_id=int(content_type_id) if content_type_id else None,
            object_id=int(object_id) if object_id else None,
            status=int(status_value) if status_value else None,
        )
        return Response(ProductionAssignmentSerializer(queryset, many=True).data)

    def post(self, request):
        employee = _get_employee(request)
        serializer = ProductionAssignmentWriteSerializer(data=request.data, is_create=True, context={'employee': employee})
        serializer.is_valid(raise_exception=True)
        try:
            assignment = mes_services.create_production_assignment(
                merchant=employee.merchant,
                device=serializer.validated_data['device'],
                content_type=serializer.validated_data['content_type'],
                object_id=serializer.validated_data['object_id'],
                assigner=serializer.validated_data['assigner'],
                assignee=serializer.validated_data.get('assignee'),
                production_quantity=serializer.validated_data['production_quantity'],
                status=serializer.validated_data.get('status', ProductionAssignmentStatusEnum.DRAFT),
                created_by=request.user,
                operator=employee,
                extra=serializer.validated_data.get('extra'),
            )
        except ValueError as exc:
            return Response({'detail': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(ProductionAssignmentSerializer(assignment).data, status=status.HTTP_201_CREATED)


class ProductionAssignmentDetailView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self, request, assignment_id):
        employee = _get_employee(request)
        return get_object_or_404(
            mes_services.list_production_assignments_for_merchant(merchant=employee.merchant),
            id=assignment_id,
        )

    def get(self, request, assignment_id):
        assignment = self.get_object(request, assignment_id)
        return Response(ProductionAssignmentSerializer(assignment).data)

    def patch(self, request, assignment_id):
        employee = _get_employee(request)
        assignment = self.get_object(request, assignment_id)
        serializer = ProductionAssignmentWriteSerializer(data=request.data, partial=True, context={'employee': employee})
        serializer.is_valid(raise_exception=True)
        try:
            assignment = mes_services.update_production_assignment(
                assignment=assignment,
                operator=employee,
                device=serializer.validated_data.get('device', mes_services.UNSET),
                assignee=serializer.validated_data.get('assignee', mes_services.UNSET),
                production_quantity=serializer.validated_data.get('production_quantity', mes_services.UNSET),
                status=serializer.validated_data.get('status', mes_services.UNSET),
                extra=serializer.validated_data.get('extra', mes_services.UNSET),
            )
        except ValueError as exc:
            return Response({'detail': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(ProductionAssignmentSerializer(assignment).data)

    def delete(self, request, assignment_id):
        employee = _get_employee(request)
        assignment = self.get_object(request, assignment_id)
        mes_services.delete_production_assignment(assignment=assignment, operator=employee)
        return Response(status=status.HTTP_204_NO_CONTENT)
