import re
from rest_framework.decorators import action
from apps.utils.viewsets import CustomModelViewSet, CustomGenericViewSet
from apps.utils.mixins import CustomListModelMixin, CustomRetrieveModelMixin, ComplexQueryMixin, BulkUpdateModelMixin

from apps.wpmw.models import Wpr, WprDefect
from apps.qm.models import FtestItem
from apps.wpmw.serializers import WprSerializer, WprNewSerializer, WprDetailSerializer, WproutListSerializer, WprChangeNumberSerializer, WprPreInfoSerializer
from rest_framework.response import Response
from apps.mtm.models import Material
from rest_framework.exceptions import ParseError
from django.db import transaction
from apps.wpmw.filters import WprFilter
from apps.utils.sql import query_one_dict
from django.db.models.expressions import RawSQL


class WprViewSet(BulkUpdateModelMixin, CustomListModelMixin, CustomRetrieveModelMixin, ComplexQueryMixin, CustomGenericViewSet):
    """动态产品

    动态产品
    """

    perms_map = {"get": "*", "put": "*", "patch": "*"}
    select_related_fields = ["wm", "mb", "material", "wm__material_ofrom"]
    prefetch_related_fields = ["defects"]
    queryset = Wpr.objects.all()
    serializer_class = WprSerializer
    retrieve_serializer_class = WprDetailSerializer
    filterset_class = WprFilter
    ordering = ["number", "create_time"]
    ordering_fields = ["number", "create_time", "update_time"]
    search_fields = ["number", "material__name", "material__model", "material__specification", "number_out","wm__batch"]
    annotate_dict = {
        "number_prefix": RawSQL("regexp_replace(wpmw_wpr.number, '(\\d+)$', '')", []),
        "number_suffix": RawSQL("COALESCE(NULLIF(regexp_replace(wpmw_wpr.number, '.*?(\\d+)$', '\\1'), ''), '0')::bigint", []),
    }

    def add_info_for_list(self, data):
        parent_ids = [item["wpr_from"] for item in data if item.get("wpr_from", False)]
        if parent_ids:
            parent_data = Wpr.objects.filter(id__in=parent_ids).values("id", "number", "data")
            parent_map = {item["id"]: item for item in parent_data}
            for item in data:
                if item["wpr_from"]:
                    item["wpr_from_"] = parent_map[item["wpr_from"]]
        return data

    def filter_queryset(self, queryset):
        qs = super().filter_queryset(queryset)
        if "mb__isnull" in self.request.query_params or "wm__isnull" in self.request.query_params:
            pass
        else:
            qs.exclude(mb=None, wm=None)
        return qs
    
    @action(methods=["post"], detail=False, perms_map={"post": "*"}, serializer_class=WprChangeNumberSerializer)
    @transaction.atomic
    def change_number(self, request, *args, **kwargs):
        """修改编号"""
        sr = WprChangeNumberSerializer(data=request.data)
        sr.is_valid(raise_exception=True)
        vdata = sr.validated_data
        new_number = vdata["new_number"]
        old_number = vdata["old_number"]
        if Wpr.objects.filter(number=new_number).exists():
            raise ParseError("新编号已存在,不可使用")
        wpr = Wpr.objects.get(number=old_number)
        from apps.wpm.models import Mlogbw, Handoverbw
        from apps.inm.models import MIOItemw
        Wpr.objects.filter(id=wpr.id).update(number=new_number)
        Mlogbw.objects.filter(wpr=wpr).update(number=new_number)
        Handoverbw.objects.filter(wpr=wpr).update(number=new_number)
        MIOItemw.objects.filter(wpr=wpr).update(number=new_number)
        return Response()

    @action(methods=["post"], detail=False, perms_map={"post": "*"}, serializer_class=WprNewSerializer)
    def new_number(self, request, *args, **kwargs):
        """获取新的编号"""
        data = request.data
        year = data.get("year")
        month = data.get("month")
        material_start = data.get("material_start")
        wps_qs = Wpr.objects.filter(material_start=material_start, create_time__year=year, create_time__month=month).order_by("create_time")
        wpr_last = wps_qs.order_by("number").last()
        count = wps_qs.count()
        last_number = wpr_last.number if count > 0 else None
        last_number_count = int(last_number.split("-")[-1].lstrip("0"))
        mat = Material.objects.get(id=material_start)
        return Response({"count": count, "last_number": last_number, "material_model": mat.model, "last_number_count": last_number_count})

    @action(methods=["get"], detail=False, perms_map={"get": "*"})
    def number_out_last(self, request, *args, **kwargs):
        """获取最新的出库对外编号

        获取最新的出库对外编号(get请求传入prefix参数和with_unsubmit参数，with_unsubmit默认为yes，表示是否包含未出库的记录，prefix为前缀，如'WPR-2023-0)"""
        from apps.inm.models import MIOItemw

        prefix = request.query_params.get("prefix", None)
        if not prefix:
            raise ParseError("请传入前缀参数")
        with_unsubmit = request.query_params.get("with_unsubmit", "yes")
        # wpr_qs_last = Wpr.objects.filter(number_out__startswith=prefix).order_by("number_out").last()
        # 使用原始sql
        # 编号格式为"前缀+补零数字"(无分隔符,如 'FS01'/'FS00025',前端也按此规则拼接)。
        # 用 ^[0-9]+$ 约束前缀后必须紧跟纯数字,避免 'FS' 误命中 'FSM00027' 这类更长前缀;
        # 并按尾部数字的数值(而非字符串)排序取最大,避免补零位数不一致时取错。
        query = """
                SELECT id, number_out FROM wpmw_wpr
                WHERE number_out LIKE %s
                AND substring(number_out FROM LENGTH(%s) + 1) ~ '^[0-9]+$'
                ORDER BY (substring(number_out FROM LENGTH(%s) + 1))::bigint DESC
                LIMIT 1
                """
        params = (
            f"{prefix}%",
            prefix,
            prefix
        )
        number_outs = []
        wpr_qs_last = query_one_dict(query, params)
        if wpr_qs_last:
            number_outs.append(wpr_qs_last["number_out"])
        # 查找未出库的记录
        if with_unsubmit == "yes":
            # mioitemw_last = MIOItemw.objects.filter(
            #     number_out__isnull=False, number_out__startswith=prefix).order_by("number_out").last()
            query2 = """
                select mioitemw.id, mioitemw.number_out from inm_mioitemw mioitemw left join inm_mioitem mioitem on mioitem.id = mioitemw.mioitem_id
                left join inm_mio mio on mio.id = mioitem.mio_id
                where mio.submit_time is null and mioitemw.number_out LIKE %s
                AND substring(mioitemw.number_out FROM LENGTH(%s) + 1) ~ '^[0-9]+$'
                ORDER BY (substring(mioitemw.number_out FROM LENGTH(%s) + 1))::bigint DESC
                LIMIT 1
                    """
            mioitemw_last = query_one_dict(query2, params)
            if mioitemw_last:
                number_outs.append(mioitemw_last["number_out"])
        if number_outs:
            # 按尾部数字的数值取最大(与前端一致:去掉非数字字符后作为流水号)
            number_outs.sort(key=lambda n: int(re.sub(r"\D", "", n)))
            number_out_last = number_outs[-1]
            number_int_last = re.sub(r"\D", "", number_out_last)
            try:
                number_int_last = int(number_int_last)
            except ValueError:
                raise ParseError(f"找到 {number_out_last}, 请检查前缀格式")
            return Response({"number_out_last": number_out_last, "number_out_int_last": number_int_last})
        else:
            return Response({"number_out_last": None})

    @action(methods=["patch"], detail=True, perms_map={"patch": "*"}, serializer_class=WprPreInfoSerializer)
    def update_pre_info(self, request, *args, **kwargs):
        """更新预处理信息"""
        wpr = self.get_object()
        sr = WprPreInfoSerializer(data=request.data)
        sr.is_valid(raise_exception=True)
        raw_info = sr.validated_data
        from apps.utils.tools import update_dict
        wpr.pre_info = update_dict(wpr.pre_info or {}, raw_info)
        wpr.save(update_fields=["pre_info"])
        return Response({"pre_info": wpr.pre_info})

    @action(methods=["post"], detail=False, perms_map={"post": "*"}, serializer_class=WproutListSerializer)
    @transaction.atomic
    def assgin_number_out(self, request, *args, **kwargs):
        """分配出库对外编号

        分配出库对外编号"""
        sr = WproutListSerializer(data=request.data)
        sr.is_valid(raise_exception=True)
        vdata = sr.validated_data
        items = vdata["items"]
        number_outs = [i["number_out"] for i in items]
        existing_numbers = Wpr.objects.filter(number_out__in=number_outs, number_out__isnull=False).values_list("number_out", flat=True)
        if existing_numbers.exists():
            used_numbers = list(existing_numbers)
            raise ParseError(f"以下对外编号已被使用: {used_numbers[0]} 共{len(used_numbers)}个")
        for i in items:
            wpr = Wpr.objects.get(id=i["id"])
            wpr.number_out = i["number_out"]
            wpr.save()
        return Response()
