import datetime

from django.utils import timezone
from django.db import transaction
from django.db.models import Q, Count, CharField, Prefetch
from django.db.models.functions import Cast, Coalesce
from rest_framework import serializers, status, permissions
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.response import Response
from rest_framework.views import APIView

from basic_info import models as basic_models
from printing import models as printing_models
from api_man.serializers import ProductSerializer
from stateflow import models as stateflow_models


class IsPrintingFactory(permissions.BasePermission):
    """仅允许印染工厂用户访问（与 api_v1 逻辑保持一致）"""

    message = '您没有访问印染订单的权限'

    def has_permission(self, request, view):
        if not request.user or not request.user.is_authenticated:
            return False
        if hasattr(request.user, 'employee'):
            return request.user.employee.merchant.type == basic_models.MerchantTypeEnum.FACTORY
        return False


class PrintingJobV2Serializer(serializers.ModelSerializer):
    """v2 独立的印染任务序列化器，包含开单数量"""

    billed_quantity = serializers.DecimalField(max_digits=18, decimal_places=2, read_only=True)
    business_object_id = serializers.SerializerMethodField()
    width = serializers.SerializerMethodField()
    fabric = serializers.SerializerMethodField()
    product = ProductSerializer(read_only=True)

    class Meta:
        model = printing_models.PrintingJob
        fields = [
            'id',
            'printing_order',
            'product',
            'work_state',
            'quantity',
            'width',
            'fabric',
            'unit',
            'size',
            'pieces',
            'description',
            'business_object_id',
            'created_at',
            'updated_at',
            'billed_quantity',
        ]
        read_only_fields = ['id', 'created_at', 'updated_at', 'billed_quantity']

    def get_business_object_id(self, obj):
        return obj.business_object_id

    def get_width(self, obj: printing_models.PrintingJob) -> float:
        return obj.printing_order.width

    def get_fabric(self, obj: printing_models.PrintingJob) -> str:
        return obj.printing_order.fabric


class PrintingJobByCustomerView(APIView):
    """
    按客户与日期范围查询印染任务。

    必填 query 参数:
    - customer_id: 客户 ID
    - date_from: 开始日期 (YYYY-MM-DD)
    - date_to: 结束日期 (YYYY-MM-DD)，闭区间，包含 23:59:59
    可选过滤：
    - printing_order: 按印染主订单 ID
    - product_id / product_name / product_human_id / product_width_size / product_color
    """

    serializer_class = PrintingJobV2Serializer
    permission_classes = [permissions.AllowAny]

    def get(self, request):
        qp = request.query_params
        customer_id = qp.get('customer_id')
        date_from = qp.get('date_from')
        date_to = qp.get('date_to')
        printing_order_id = qp.get('printing_order')
        product_id = qp.get('product_id')
        product_name = qp.get('product_name')
        product_human_id = qp.get('product_human_id')
        product_width_size = qp.get('product_width_size')
        product_color = qp.get('product_color')

        if not customer_id:
            return Response({'detail': 'customer_id 为必填参数'}, status=status.HTTP_400_BAD_REQUEST)
        if not date_from or not date_to:
            return Response({'detail': 'date_from 与 date_to 为必填参数'}, status=status.HTTP_400_BAD_REQUEST)

        try:
            customer_id_int = int(customer_id)
        except (TypeError, ValueError):
            return Response({'detail': 'customer_id 必须为数字'}, status=status.HTTP_400_BAD_REQUEST)

        try:
            start_date = datetime.datetime.strptime(date_from, '%Y-%m-%d').date()
            end_date = datetime.datetime.strptime(date_to, '%Y-%m-%d').date()
        except ValueError:
            return Response({'detail': '日期格式需为 YYYY-MM-DD'}, status=status.HTTP_400_BAD_REQUEST)

        # 闭区间：包含当日 00:00:00 和 23:59:59.999999
        start_dt = datetime.datetime.combine(start_date, datetime.time.min)
        end_dt = datetime.datetime.combine(end_date, datetime.time.max)

        if timezone.is_naive(start_dt):
            start_dt = timezone.make_aware(start_dt, timezone.get_default_timezone())
        if timezone.is_naive(end_dt):
            end_dt = timezone.make_aware(end_dt, timezone.get_default_timezone())

        queryset = printing_models.PrintingJob.objects.select_related('printing_order', 'product').filter(
            printing_order__customer_id=customer_id_int,
            created_at__gte=start_dt,
            created_at__lte=end_dt,
        )

        # 可选过滤：printing_order
        if printing_order_id:
            try:
                queryset = queryset.filter(printing_order_id=int(printing_order_id))
            except (TypeError, ValueError):
                return Response({'detail': 'printing_order 必须为数字'}, status=status.HTTP_400_BAD_REQUEST)

        # 可选过滤：product
        if product_id:
            try:
                queryset = queryset.filter(product_id=int(product_id))
            except (TypeError, ValueError):
                return Response({'detail': 'product_id 必须为数字'}, status=status.HTTP_400_BAD_REQUEST)
        if product_name:
            queryset = queryset.filter(product__name__icontains=product_name)
        if product_human_id:
            queryset = queryset.filter(product__human_id__icontains=product_human_id)
        if product_width_size:
            try:
                width_decimal = float(product_width_size)
            except (TypeError, ValueError):
                return Response({'detail': 'product_width_size 必须为数字'}, status=status.HTTP_400_BAD_REQUEST)
            queryset = queryset.filter(product__width_size=width_decimal)
        if product_color:
            queryset = queryset.filter(product__color__icontains=product_color)

        queryset = queryset.order_by('-created_at')

        serializer = self.serializer_class(queryset, many=True)
        return Response(serializer.data)


class PrintingJobBatchAdvancePreviewRequestSerializer(serializers.Serializer):
    """批量推进：预览/校验请求"""

    printing_job_ids = serializers.ListField(
        child=serializers.IntegerField(min_value=1),
        allow_empty=False,
        help_text='需要批量推进的 printing_job id 列表',
    )

    def validate_printing_job_ids(self, value):
        # 去重保持稳定性（前端可能重复传）
        deduped = list(dict.fromkeys(value))
        if not deduped:
            raise serializers.ValidationError('printing_job_ids 不能为空')
        return deduped


class PrintingJobBatchAdvanceSubmitRequestSerializer(PrintingJobBatchAdvancePreviewRequestSerializer):
    """批量推进：提交请求"""

    parameters = serializers.DictField(
        child=serializers.JSONField(),
        required=False,
        default=dict,
        help_text='与单条推进接口一致的工艺参数（将作为 **kwargs 传给 stateflow）',
    )


def _validate_jobs_for_batch_advance(printing_job_ids: list[int]):
    """
    批量推进的核心一致性校验（preview 与 submit 共用）

    规则：
    1) 所有 id 都存在
    2) 全部属于同一个 printing_order
    3) 全部存在 business_object（流程实例）
    4) 全部具有相同的 next_pending_state（下一待执行节点），否则不允许批量

    注意：这里不做“竞态”处理（preview 后 submit 前状态变化），submit 时会再次调用该函数重新校验。
    后续如需增强，可在 preview 返回 snapshot token，在 submit 校验 token 以提升用户体验。
    """
    # 查询并校验存在性
    qs = (
        printing_models.PrintingJob.objects
        .select_related('printing_order', 'business_object', 'business_object__process')
        .filter(id__in=printing_job_ids)
    )
    jobs = list(qs)
    found_ids = {j.id for j in jobs}
    missing_ids = [str(i) for i in printing_job_ids if i not in found_ids]
    if missing_ids:
        raise serializers.ValidationError({'detail': f'以下 printing_job 不存在: {", ".join(missing_ids)}'})

    # 同一订单
    order_ids = {j.printing_order_id for j in jobs}
    if len(order_ids) != 1:
        raise serializers.ValidationError({'detail': '所选明细不属于同一个 printing_order，无法批量推进'})
    printing_order_id = next(iter(order_ids))
    printing_order = jobs[0].printing_order

    # 必须有关联流程实例
    no_bo = [str(j.id) for j in jobs if not j.business_object_id]
    if no_bo:
        raise serializers.ValidationError({'detail': f'以下 printing_job 未关联流程实例（business_object），无法推进: {", ".join(no_bo)}'})

    # 计算并校验 next_pending_state 一致
    from stateflow import services as stateflow_services

    next_infos = []
    for j in jobs:
        info = stateflow_services.get_next_pending_state(j.business_object, include_parameters=True)
        if info is None:
            next_infos.append((j.id, None))
        else:
            next_infos.append((j.id, info))

    # 不能包含“无待执行节点”（流程已完成或无节点）
    cannot_advance = [str(job_id) for job_id, info in next_infos if info is None]
    if cannot_advance:
        raise serializers.ValidationError({'detail': f'以下 printing_job 没有待执行节点（流程已完成或无节点），无法批量推进: {", ".join(cannot_advance)}'})

    # 比对 state_id
    first_info = next_infos[0][1]
    target_state = first_info['state']
    target_order = first_info['order']
    target_state_id = target_state.id

    diff_jobs = []
    for job_id, info in next_infos:
        if info['state'].id != target_state_id:
            diff_jobs.append(str(job_id))
    if diff_jobs:
        raise serializers.ValidationError({'detail': f'所选明细当前待执行节点不一致，无法批量推进（不同节点的 jobs: {", ".join(diff_jobs)}）'})

    # 参数定义取目标节点（所有一致）
    target_parameters = first_info.get('parameters', []) or []

    return {
        'printing_order': printing_order,
        'printing_order_id': printing_order_id,
        'jobs': jobs,
        'target_state': target_state,
        'target_order': target_order,
        'target_parameters': target_parameters,
    }


class PrintingJobBatchAdvancePreviewView(APIView):
    """
    批量推进：预览

    作用：
    - 校验 printing_job_ids 是否可批量推进（同订单/同待执行节点）
    - 返回“下一步待执行节点”的信息及其工艺参数定义，供前端生成批量表单
    """

    permission_classes = [permissions.IsAuthenticated, IsPrintingFactory]

    def post(self, request):
        srz = PrintingJobBatchAdvancePreviewRequestSerializer(data=request.data)
        srz.is_valid(raise_exception=True)

        data = _validate_jobs_for_batch_advance(srz.validated_data['printing_job_ids'])

        from stateflow.serializers import StateParameterSerializer

        params_srz = StateParameterSerializer(
            data['target_parameters'],
            many=True,
            context={'request': request},
        )

        return Response({
            'printing_order_id': data['printing_order_id'],
            'printing_job_ids': [j.id for j in data['jobs']],
            'next_state': {
                'id': data['target_state'].id,
                'name': data['target_state'].name,
                'description': data['target_state'].description,
                'order': data['target_order'],
                'parameters': params_srz.data,
            }
        })


class PrintingJobBatchAdvanceSubmitView(APIView):
    """
    批量推进：提交

    规则：全成功/全失败
    - 任意一个 job 推进失败：整体回滚（不产生任何 stateflow 日志，也不产生批量推进记录）
    """

    permission_classes = [permissions.IsAuthenticated, IsPrintingFactory]

    def post(self, request):
        srz = PrintingJobBatchAdvanceSubmitRequestSerializer(data=request.data)
        srz.is_valid(raise_exception=True)

        payload = srz.validated_data
        parameters = payload.get('parameters') or {}

        data = _validate_jobs_for_batch_advance(payload['printing_job_ids'])

        from stateflow import services as stateflow_services

        # 全成功/全失败：用事务包住整个批量推进
        with transaction.atomic():
            record = printing_models.PrintingJobBatchAdvanceRecord.objects.create(
                printing_order=data['printing_order'],
                state=data['target_state'],
                created_by=request.user,
                parameters=parameters,
            )
            record.printing_jobs.set(data['jobs'])

            # 逐个复用单条推进逻辑
            last_message = None
            for job in data['jobs']:
                ok, msg, _state_log = stateflow_services.advance_to_next_state(
                    job.business_object,
                    request.user,
                    **parameters
                )
                if not ok:
                    # 抛异常触发事务回滚，保证“全部失败”
                    raise serializers.ValidationError({'detail': msg})
                last_message = msg

        # 返回最新的 job 列表（可用于前端刷新）
        refreshed_jobs = (
            printing_models.PrintingJob.objects
            .select_related('printing_order', 'product')
            .filter(id__in=[j.id for j in data['jobs']])
            .order_by('id')
        )
        job_srz = PrintingJobV2Serializer(refreshed_jobs, many=True)

        return Response({
            'detail': last_message or '批量推进成功',
            'batch_id': record.id,
            'printing_order_id': data['printing_order_id'],
            'printing_job_ids': [j.id for j in data['jobs']],
            'jobs': job_srz.data,
        })


class PlateOrderByProcessNodeSerializer(serializers.ModelSerializer):
    """按流程节点筛选 PlateOrder 的列表序列化（尽量保持轻量）"""

    design_code = serializers.SerializerMethodField()
    customer_name = serializers.CharField(source='customer.name', read_only=True)
    business_object_id = serializers.SerializerMethodField()
    process_parameters = serializers.SerializerMethodField()

    class Meta:
        model = printing_models.PlateOrder
        fields = [
            'id',
            'design_code',
            'customer',
            'customer_name',
            'style_name',
            'urgency_level',
            'is_invalid',
            'business_object_id',
            'process_parameters',
            'created_at',
            'updated_at',
        ]
        read_only_fields = fields

    def get_design_code(self, obj: printing_models.PlateOrder) -> str | None:
        return obj.design_code or (str(obj.id) if obj.id else None)

    def get_business_object_id(self, obj: printing_models.PlateOrder) -> int | None:
        return obj.business_object_id

    def get_process_parameters(self, obj: printing_models.PlateOrder) -> list[dict]:
        """
        返回“订单维度”的工艺参数 key/value（当前 process_node 对应 state 的参数）。

        取值来源：
        - 优先取该订单 business_object 在目标 state 的**最新一次 StateFlowRecord**（可能是已撤销记录）
          对应的 StateLogParameterRecord 汇总（后提交覆盖先提交）。
        - 若从未提交过该 state 的参数：value 为 null（由前端自行用顶层 parameters 的默认值做兜底/占位）
        """
        keys: list[str] = self.context.get('target_parameter_keys') or []
        target_state_id: int | None = self.context.get('target_state_id')
        if not keys or not target_state_id:
            return [{'key': k, 'value': None} for k in keys]

        bo = getattr(obj, 'business_object', None)
        if not bo:
            return [{'key': k, 'value': None} for k in keys]

        logs = getattr(bo, '_prefetched_target_state_logs', None)
        if logs is None:
            # fallback：极少数情况下未预取
            logs = list(
                bo.state_logs.filter(state_id=target_state_id).order_by('-completed_at', '-id')[:1]
            )

        latest_log = logs[0] if logs else None
        summary: dict = {}
        if latest_log:
            param_records = getattr(latest_log, '_prefetched_parameter_records', None)
            if param_records is None:
                param_records = list(latest_log.parameter_records.all().order_by('created_at', 'id'))
            for rec in param_records:
                summary.update(rec.parameters or {})

        return [{'key': k, 'value': summary.get(k)} for k in keys]


class PlateOrderByProcessNodeView(APIView):
    """
    按 process_node_id 查询“当前处于该节点（NEXT 模式：下一个待执行节点）”的 PlateOrder 列表。

    GET /api/v2/plate-orders/by-process-node/

    Query 参数:
    - process_node_id: 必填，ProcessNode.id
    - search: 可选。支持:
        - 纯数字：同时匹配 id 精确 + design_code icontains
        - 非纯数字：design_code icontains
    - ordering: 可选，默认 -created_at，支持: id / created_at / updated_at / design_code
    - limit/offset: 分页（limit 默认 20）
    """

    permission_classes = [permissions.IsAuthenticated, IsPrintingFactory]

    _ORDERING_FIELDS = {'id', 'created_at', 'updated_at', 'design_code'}

    def get(self, request):
        qp = request.query_params
        process_node_id = qp.get('process_node_id')
        if not process_node_id:
            return Response({'detail': 'process_node_id 为必填参数'}, status=status.HTTP_400_BAD_REQUEST)

        try:
            process_node_id_int = int(process_node_id)
        except (TypeError, ValueError):
            return Response({'detail': 'process_node_id 必须为数字'}, status=status.HTTP_400_BAD_REQUEST)

        try:
            process_node = (
                stateflow_models.ProcessNode.objects
                .select_related('process', 'state')
                .get(id=process_node_id_int)
            )
        except stateflow_models.ProcessNode.DoesNotExist:
            return Response({'detail': 'process_node 不存在'}, status=status.HTTP_404_NOT_FOUND)

        # 目标节点信息
        target_state_id = process_node.state_id
        target_order = process_node.order
        process_id = process_node.process_id

        # 目标节点之前的所有 state_id（distinct，便于计数）
        prev_state_ids = list(
            stateflow_models.ProcessNode.objects
            .filter(process_id=process_id, order__lt=target_order)
            .order_by('order', 'id')
            .values_list('state_id', flat=True)
            .distinct()
        )

        queryset = (
            printing_models.PlateOrder.objects
            .select_related('customer', 'business_object')
            .filter(business_object__isnull=False, business_object__process_id=process_id)
        )

        # NEXT 模式判定：前置节点都已完成（未撤销） + 目标节点尚未完成（未撤销）
        if prev_state_ids:
            queryset = queryset.annotate(
                completed_prev_states=Count(
                    'business_object__state_logs__state_id',
                    filter=Q(
                        business_object__state_logs__is_cancelled=False,
                        business_object__state_logs__state_id__in=prev_state_ids,
                    ),
                    distinct=True,
                )
            ).filter(completed_prev_states=len(prev_state_ids))

        queryset = queryset.annotate(
            completed_target=Count(
                'business_object__state_logs__id',
                filter=Q(
                    business_object__state_logs__is_cancelled=False,
                    business_object__state_logs__state_id=target_state_id,
                ),
                distinct=True,
            )
        ).filter(completed_target=0)

        # search：同时支持主键与 design_code icontains（不新增额外参数）
        search = (qp.get('search') or '').strip()
        if search:
            if search.isdigit():
                try:
                    search_id = int(search)
                except (TypeError, ValueError):
                    search_id = None
                cond = Q(design_code__icontains=search)
                if search_id is not None:
                    cond = cond | Q(id=search_id)
                queryset = queryset.filter(cond)
            else:
                queryset = queryset.filter(design_code__icontains=search)

        # ordering：默认 -created_at
        ordering = (qp.get('ordering') or '-created_at').strip() or '-created_at'
        direction = '-' if ordering.startswith('-') else ''
        field = ordering[1:] if ordering.startswith('-') else ordering
        if field not in self._ORDERING_FIELDS:
            return Response(
                {'detail': f'ordering 不支持: {ordering}（可选: {", ".join(sorted(self._ORDERING_FIELDS))}）'},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # 为 design_code 排序提供兜底：为空时按主键字符串
        if field == 'design_code':
            queryset = queryset.annotate(
                design_code_normalized=Coalesce('design_code', Cast('id', output_field=CharField()))
            ).order_by(f'{direction}design_code_normalized', 'id')
        else:
            queryset = queryset.order_by(f'{direction}{field}', 'id')

        # 该节点参数模板（仅 key/value），同时用于 results[*].process_parameters 的 key 集合
        params = list(process_node.state.parameters.order_by('id').values('key', 'value'))
        target_keys = [p['key'] for p in params]

        # 预取：目标 state 的最新日志及其参数记录（用于拼装“订单维度”的参数值，避免 N+1）
        param_records_prefetch = Prefetch(
            'parameter_records',
            queryset=stateflow_models.StateLogParameterRecord.objects.order_by('created_at', 'id'),
            to_attr='_prefetched_parameter_records',
        )
        target_state_logs_qs = (
            stateflow_models.StateFlowRecord.objects
            .filter(state_id=target_state_id)
            .order_by('-completed_at', '-id')
            .prefetch_related(param_records_prefetch)
        )
        queryset = queryset.prefetch_related(
            Prefetch(
                'business_object__state_logs',
                queryset=target_state_logs_qs,
                to_attr='_prefetched_target_state_logs',
            )
        )

        # 分页（limit 默认 20）
        paginator = LimitOffsetPagination()
        paginator.default_limit = 20
        page = paginator.paginate_queryset(queryset, request, view=self)
        results = page if page is not None else list(queryset)

        srz = PlateOrderByProcessNodeSerializer(
            results,
            many=True,
            context={
                'request': request,
                'target_state_id': target_state_id,
                'target_parameter_keys': target_keys,
            },
        )

        return Response({
            'process_node': {
                'id': process_node.id,
                'process_id': process_id,
                'state_id': target_state_id,
                'state_name': process_node.state.name,
                'order': target_order,
            },
            'parameters': params,
            'count': getattr(paginator, 'count', len(results)),
            'next': paginator.get_next_link() if page is not None else None,
            'previous': paginator.get_previous_link() if page is not None else None,
            'results': srz.data,
        })
