"""
Printing module business logic services
"""
from typing import Dict, Any, Tuple
from django.conf import settings
from django.db import transaction
from django.contrib.contenttypes.models import ContentType
from printing import models as printing_models
from printing.signals import printing_job_production_completed, printing_order_created
from stateflow import models as stateflow_models
from stateflow import services as stateflow_services


class PrintingOrderService:
    """印染订单业务逻辑服务"""
    
    @staticmethod
    def get_default_process():
        """获取默认流程"""
        process_id = getattr(settings, 'PRINTING_DEFAULT_PROCESS_ID', None)
        if not process_id:
            return None
        
        try:
            return stateflow_models.Process.objects.get(id=process_id)
        except stateflow_models.Process.DoesNotExist:
            return None
    
    @staticmethod
    def create_printing_order(data: Dict[str, Any], user) -> printing_models.PrintingOrder:
        """
        创建印染订单
        
        Args:
            data: 订单数据
            user: 当前用户
            
        Returns:
            创建的订单实例
        """
        # 如果未指定 process，使用默认值
        if 'process' not in data or data['process'] is None:
            default_process = PrintingOrderService.get_default_process()
            if default_process:
                data['process'] = default_process
        
        # 绑定创建人
        data['created_by'] = user
        
        # 绑定商户（从当前用户的 employee 获取）
        if hasattr(user, 'employee') and user.employee and user.employee.merchant:
            data['merchant'] = user.employee.merchant
        
        order = printing_models.PrintingOrder.objects.create(**data)

        # domain event: printing order created
        # handler will ensure transaction.on_commit for external side effects (e.g. WeCom notify)
        try:
            printing_order_created.send(
                sender=printing_models.PrintingOrder,
                instance=order,
                created_by=user,
            )
        except Exception:
            # signal should never break core create flow
            import logging
            logging.getLogger(__name__).exception(
                "[api_v1.views.printing.services] 触发 printing_order_created signal 失败（已忽略）"
            )
        return order
    
    @staticmethod
    def update_printing_order(
        printing_order: printing_models.PrintingOrder,
        data: Dict[str, Any],
        user
    ) -> Tuple[bool, str, printing_models.PrintingOrder]:
        """
        更新印染订单
        
        Args:
            printing_order: 订单实例
            data: 更新数据
            user: 当前用户
            
        Returns:
            (success, message, updated_order)
        """
        # 如果要修改 process，需要验证并同步 relink 所有 jobs 的 BusinessObject
        if 'process' in data and data['process'] != printing_order.process:
            new_process = data.get('process')
            if new_process is None:
                return False, '流程不能为空，无法修改流程', printing_order

            # 只要存在任意 job 已开始（有未撤销进度）则拒绝
            if not printing_order.can_change_process():
                return False, '存在已开始的印染任务，无法修改流程', printing_order

            from stateflow.services import relink_business_object_for_instance

            try:
                with transaction.atomic():
                    # relink 所有 jobs（jobs 未开始，允许重建并重新绑定 BO）
                    for job in printing_order.printing_jobs.select_related('business_object').all():
                        relinked = relink_business_object_for_instance(
                            instance=job,
                            new_process=new_process,
                            default_name=f"PrintingJob-{job.pk}",
                            default_description=f"印染任务 {job.pk} 的流程实例",
                        )
                        if relinked is None:
                            raise RuntimeError(f'印染任务 {job.pk} 已存在有效进度，无法修改流程')

                    # 最后更新订单流程
                    printing_order.process = new_process
                    printing_order.save(update_fields=['process'])

            except RuntimeError as e:
                return False, str(e), printing_order

            # process 已处理完毕，避免后续通用字段更新再次覆盖
            data = {k: v for k, v in data.items() if k != 'process'}
        
        # 更新字段
        for field, value in data.items():
            setattr(printing_order, field, value)
        
        printing_order.save()
        return True, '更新成功', printing_order
    
    @staticmethod
    def can_change_process(printing_order: printing_models.PrintingOrder) -> bool:
        """
        检查订单是否可以修改流程
        
        Args:
            printing_order: 订单实例
            
        Returns:
            是否可以修改
        """
        return printing_order.can_change_process()
    
    @staticmethod
    def get_order_progress(printing_order: printing_models.PrintingOrder) -> int:
        """
        计算订单完成进度
        
        Args:
            printing_order: 订单实例
            
        Returns:
            完成百分比 (0-100)
        """
        return printing_order.progress


class PrintingJobService:
    """印染任务业务逻辑服务"""
    
    @staticmethod
    @transaction.atomic
    def create_printing_job(data: Dict[str, Any], user) -> printing_models.PrintingJob:
        """
        创建印染任务
        
        自动创建关联的 BusinessObject 实例
        
        Args:
            data: 任务数据
            user: 当前用户
            
        Returns:
            创建的任务实例
        """
        data = dict(data)
        data.pop('is_production_completed', None)
        printing_order = data.get('printing_order')
        
        # 绑定创建人
        data['created_by'] = user
        
        # 绑定商户（从当前用户的 employee 获取）
        if hasattr(user, 'employee') and user.employee and user.employee.merchant:
            data['merchant'] = user.employee.merchant
        
        # 创建 PrintingJob
        job = printing_models.PrintingJob.objects.create(**data)
        
        # 如果 PrintingOrder 有关联的流程，创建 BusinessObject
        if printing_order and printing_order.process:
            # 将流程实例绑定到该 PrintingJob（便于跨模块定位与审计）
            ct = ContentType.objects.get_for_model(printing_models.PrintingJob)
            business_object = stateflow_models.BusinessObject.objects.create(
                name=f"PrintingJob-{job.id}",
                process=printing_order.process,
                description=f"印染任务 {job.id} 的流程实例",
                content_type=ct,
                object_id=job.id,
            )
            job.business_object = business_object
            job.save()
        
        return job
    
    @staticmethod
    def update_printing_job(
        printing_job: printing_models.PrintingJob,
        data: Dict[str, Any],
        user
    ) -> Tuple[bool, str, printing_models.PrintingJob]:
        """
        更新印染任务
        
        Args:
            printing_job: 任务实例
            data: 更新数据
            user: 当前用户
            
        Returns:
            (success, message, updated_job)
        """
        data = dict(data)
        data.pop('is_production_completed', None)

        # 更新字段
        for field, value in data.items():
            # 不允许直接修改 business_object
            if field == 'business_object':
                continue
            setattr(printing_job, field, value)
        
        printing_job.save()
        return True, '更新成功', printing_job

    @staticmethod
    @transaction.atomic
    def make_printing_job_production_completed(
        printing_job: printing_models.PrintingJob,
        triggered_by=None,
    ) -> printing_models.PrintingJob:
        """显式标记 PrintingJob 已完成生产，并触发领域信号。"""
        if not printing_job.is_production_completed:
            printing_job.is_production_completed = True
            printing_job.save(update_fields=['is_production_completed', 'updated_at'])

        try:
            printing_job_production_completed.send(
                sender=printing_models.PrintingJob,
                instance=printing_job,
                triggered_by=triggered_by,
            )
        except Exception:
            import logging
            logging.getLogger(__name__).exception(
                "[api_v1.views.printing.services] 触发 printing_job_production_completed signal 失败（已忽略）"
            )

        return printing_job
    
    @staticmethod
    def get_job_status(printing_job: printing_models.PrintingJob) -> Dict[str, Any]:
        """
        获取任务状态信息
        
        Args:
            printing_job: 任务实例
            
        Returns:
            状态信息字典
        """
        return {
            'status': printing_job.status,
            'status_id': printing_job.status_id,
            'is_completed': printing_job.is_completed,
            'is_production_completed': printing_job.is_production_completed,
            'has_started': printing_job.has_started,
        }
