"""
Workflow Signal Handlers.

Connects signal handlers to workflow signals for logging, notifications, and other side effects.
"""
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.utils import timezone

from .models import (
    ProcessInstance,
    Task,
    ProcessHistory,
    SLAViolation,
    InstanceStatus,
    TaskStatus,
    HistoryEventType,
)
from .signals import (
    process_started,
    process_completed,
    process_cancelled,
    process_failed,
    process_suspended,
    process_resumed,
    task_created,
    task_assigned,
    task_claimed,
    task_unclaimed,
    task_completed,
    task_delegated,
    task_failed,
    sla_violated,
)


# =============================================================================
# Process Instance Signal Handlers
# =============================================================================

@receiver(post_save, sender=ProcessInstance)
def log_process_instance_changes(sender, instance, created, **kwargs):
    """Log process instance creation and status changes."""
    if created:
        ProcessHistory.objects.log_event(
            instance=instance,
            event_type=HistoryEventType.INSTANCE_STARTED,
            user=instance.started_by,
            new_value={'status': instance.status},
            comment=f'Process instance started with business key: {instance.business_key}'
        )
        # Emit signal
        process_started.send(
            sender=ProcessInstance,
            instance=instance,
            user=instance.started_by,
            variables=instance.context
        )


@receiver(pre_save, sender=ProcessInstance)
def track_process_status_changes(sender, instance, **kwargs):
    """Track status changes before saving."""
    if instance.pk:
        try:
            old_instance = ProcessInstance.objects.get(pk=instance.pk)
            instance._old_status = old_instance.status
        except ProcessInstance.DoesNotExist:
            instance._old_status = None
    else:
        instance._old_status = None


@receiver(post_save, sender=ProcessInstance)
def handle_process_status_change(sender, instance, created, **kwargs):
    """Handle process status changes after save."""
    if created:
        return  # Already handled in log_process_instance_changes
    
    old_status = getattr(instance, '_old_status', None)
    if old_status and old_status != instance.status:
        # Log the status change
        event_type_map = {
            InstanceStatus.COMPLETED: HistoryEventType.INSTANCE_COMPLETED,
            InstanceStatus.CANCELLED: HistoryEventType.INSTANCE_CANCELLED,
            InstanceStatus.FAILED: HistoryEventType.INSTANCE_FAILED,
            InstanceStatus.SUSPENDED: HistoryEventType.INSTANCE_SUSPENDED,
        }
        
        event_type = event_type_map.get(instance.status)
        if event_type:
            ProcessHistory.objects.log_event(
                instance=instance,
                event_type=event_type,
                old_value={'status': old_status},
                new_value={'status': instance.status},
            )
        
        # Emit appropriate signal
        if instance.status == InstanceStatus.COMPLETED:
            process_completed.send(sender=ProcessInstance, instance=instance)
        elif instance.status == InstanceStatus.CANCELLED:
            process_cancelled.send(
                sender=ProcessInstance,
                instance=instance,
                user=None,  # Could be enhanced to track who cancelled
                reason=''
            )
        elif instance.status == InstanceStatus.FAILED:
            process_failed.send(
                sender=ProcessInstance,
                instance=instance,
                error=instance.error_message
            )
        elif instance.status == InstanceStatus.SUSPENDED:
            process_suspended.send(
                sender=ProcessInstance,
                instance=instance,
                user=None
            )
        elif instance.status == InstanceStatus.RUNNING and old_status == InstanceStatus.SUSPENDED:
            process_resumed.send(
                sender=ProcessInstance,
                instance=instance,
                user=None
            )


# =============================================================================
# Task Signal Handlers
# =============================================================================

@receiver(post_save, sender=Task)
def log_task_creation(sender, instance, created, **kwargs):
    """Log task creation."""
    if created:
        ProcessHistory.objects.log_event(
            instance=instance.instance,
            event_type=HistoryEventType.TASK_CREATED,
            task=instance,
            element_id=instance.element_id,
            new_value={
                'name': instance.name,
                'task_type': instance.task_type,
                'status': instance.status
            }
        )
        # Emit signal
        task_created.send(sender=Task, task=instance, instance=instance.instance)


@receiver(pre_save, sender=Task)
def track_task_status_changes(sender, instance, **kwargs):
    """Track task status and assignee changes before saving."""
    if instance.pk:
        try:
            old_task = Task.objects.get(pk=instance.pk)
            instance._old_status = old_task.status
            instance._old_assignee = old_task.assignee
        except Task.DoesNotExist:
            instance._old_status = None
            instance._old_assignee = None
    else:
        instance._old_status = None
        instance._old_assignee = None


@receiver(post_save, sender=Task)
def handle_task_status_change(sender, instance, created, **kwargs):
    """Handle task status changes after save."""
    if created:
        return  # Already handled in log_task_creation
    
    old_status = getattr(instance, '_old_status', None)
    old_assignee = getattr(instance, '_old_assignee', None)
    
    # Handle status changes
    if old_status and old_status != instance.status:
        event_type_map = {
            TaskStatus.ASSIGNED: HistoryEventType.TASK_ASSIGNED,
            TaskStatus.CLAIMED: HistoryEventType.TASK_CLAIMED,
            TaskStatus.COMPLETED: HistoryEventType.TASK_COMPLETED,
            TaskStatus.FAILED: HistoryEventType.TASK_FAILED,
        }
        
        event_type = event_type_map.get(instance.status)
        if event_type:
            ProcessHistory.objects.log_event(
                instance=instance.instance,
                event_type=event_type,
                task=instance,
                user=instance.assignee,
                element_id=instance.element_id,
                old_value={'status': old_status},
                new_value={'status': instance.status}
            )
        
        # Emit appropriate signal
        if instance.status == TaskStatus.CLAIMED:
            task_claimed.send(sender=Task, task=instance, user=instance.assignee)
        elif instance.status == TaskStatus.COMPLETED:
            task_completed.send(
                sender=Task,
                task=instance,
                user=instance.assignee,
                variables=instance.output_variables
            )
        elif instance.status == TaskStatus.FAILED:
            task_failed.send(sender=Task, task=instance, error=instance.error_message)
    
    # Handle assignee changes (delegation)
    if old_assignee and old_assignee != instance.assignee and instance.assignee:
        ProcessHistory.objects.log_event(
            instance=instance.instance,
            event_type=HistoryEventType.TASK_DELEGATED,
            task=instance,
            user=instance.assignee,
            element_id=instance.element_id,
            old_value={'assignee': str(old_assignee.pk) if old_assignee else None},
            new_value={'assignee': str(instance.assignee.pk) if instance.assignee else None}
        )
        task_delegated.send(
            sender=Task,
            task=instance,
            from_user=old_assignee,
            to_user=instance.assignee
        )
    
    # Handle unclaim (assignee removed)
    if old_assignee and not instance.assignee and old_status == TaskStatus.CLAIMED:
        ProcessHistory.objects.log_event(
            instance=instance.instance,
            event_type=HistoryEventType.TASK_UNCLAIMED,
            task=instance,
            user=old_assignee,
            element_id=instance.element_id,
        )
        task_unclaimed.send(sender=Task, task=instance, user=old_assignee)


# =============================================================================
# SLA Violation Signal Handlers
# =============================================================================

@receiver(post_save, sender=SLAViolation)
def log_sla_violation(sender, instance, created, **kwargs):
    """Log SLA violation."""
    if created:
        ProcessHistory.objects.log_event(
            instance=instance.task.instance,
            event_type=HistoryEventType.SLA_VIOLATED,
            task=instance.task,
            element_id=instance.task.element_id,
            new_value={
                'sla_definition': str(instance.sla_definition.pk),
                'expected_completion': str(instance.expected_completion),
                'violation_time': str(instance.violation_time)
            }
        )
        # Emit signal
        sla_violated.send(
            sender=SLAViolation,
            violation=instance,
            task=instance.task,
            sla_definition=instance.sla_definition
        )
