"""
Workflow Views.

Django REST Framework ViewSets for workflow API.
"""
from django.utils import timezone
from django.db.models import Q, Count, Max, Prefetch
from rest_framework import viewsets, status, filters
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from django_filters.rest_framework import DjangoFilterBackend

from .models import (
    ProcessDefinition,
    ProcessInstance,
    Task,
    TaskAssignment,
    ProcessVariable,
    ProcessHistory,
    SLADefinition,
    SLAViolation,
    InstanceStatus,
    TaskStatus,
)
from .serializers import (
    ProcessDefinitionListSerializer,
    ProcessDefinitionDetailSerializer,
    ProcessDefinitionCreateSerializer,
    ProcessDefinitionUpdateSerializer,
    ProcessDefinitionDeploySerializer,
    ProcessDefinitionVersionSerializer,
    ProcessInstanceListSerializer,
    ProcessInstanceDetailSerializer,
    ProcessInstanceStartSerializer,
    ProcessInstanceCancelSerializer,
    ProcessInstanceSuspendSerializer,
    ProcessInstanceResumeSerializer,
    TaskListSerializer,
    TaskDetailSerializer,
    TaskClaimSerializer,
    TaskUnclaimSerializer,
    TaskCompleteSerializer,
    TaskDelegateSerializer,
    TaskReassignSerializer,
    TaskAssignmentSerializer,
    TaskAssignmentCreateSerializer,
    ProcessVariableSerializer,
    ProcessVariableUpdateSerializer,
    ProcessHistorySerializer,
    SLADefinitionSerializer,
    SLADefinitionCreateSerializer,
    SLAViolationSerializer,
)
from .permissions import (
    ProcessDefinitionPermission,
    ProcessInstancePermission,
    TaskPermission,
    CanDeployProcessDefinition,
    TenantIsolationPermission,
)
from .filters import (
    ProcessDefinitionFilter,
    ProcessInstanceFilter,
    TaskFilter,
    ProcessHistoryFilter,
)


class ProcessDefinitionViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing process definitions.
    
    Provides CRUD operations plus:
    - deploy: Deploy a process definition
    - new_version: Create a new version from an existing definition
    """
    permission_classes = [IsAuthenticated, ProcessDefinitionPermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_class = ProcessDefinitionFilter
    search_fields = ['name', 'slug', 'description']
    ordering_fields = ['name', 'version', 'created_at', 'deployed_at']
    ordering = ['-created_at']

    def get_queryset(self):
        user = self.request.user
        queryset = ProcessDefinition.objects.filter(tenant=user.tenant)
        
        # Annotate with instances count for list view
        if self.action == 'list':
            queryset = queryset.annotate(
                instances_count=Count('instances')
            )
        
        return queryset.select_related('created_by')

    def get_serializer_class(self):
        if self.action == 'list':
            return ProcessDefinitionListSerializer
        elif self.action == 'retrieve':
            return ProcessDefinitionDetailSerializer
        elif self.action == 'create':
            return ProcessDefinitionCreateSerializer
        elif self.action in ['update', 'partial_update']:
            return ProcessDefinitionUpdateSerializer
        elif self.action == 'deploy':
            return ProcessDefinitionDeploySerializer
        elif self.action == 'new_version':
            return ProcessDefinitionVersionSerializer
        return ProcessDefinitionDetailSerializer

    @action(detail=True, methods=['post'], permission_classes=[IsAuthenticated, CanDeployProcessDefinition])
    def deploy(self, request, pk=None):
        """Deploy a process definition."""
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data)
        serializer.is_valid(raise_exception=True)
        
        instance.is_deployed = True
        instance.deployed_at = timezone.now()
        instance.save(update_fields=['is_deployed', 'deployed_at'])
        
        return Response(
            ProcessDefinitionDetailSerializer(instance, context={'request': request}).data,
            status=status.HTTP_200_OK
        )

    @action(detail=True, methods=['post'])
    def new_version(self, request, pk=None):
        """Create a new version of a process definition."""
        instance = self.get_object()
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        
        # Get the next version number
        latest_version = ProcessDefinition.objects.filter(
            tenant=request.user.tenant,
            slug=instance.slug
        ).aggregate(max_version=Max('version'))['max_version'] or 0
        
        # Create new version
        new_definition = ProcessDefinition.objects.create(
            tenant=request.user.tenant,
            slug=instance.slug,
            name=instance.name,
            description=instance.description,
            bpmn_xml=instance.bpmn_xml,
            metadata=instance.metadata,
            version=latest_version + 1,
            is_deployed=False,
            is_active=True,
            created_by=request.user,
        )
        
        # Copy SLA definitions if requested
        if serializer.validated_data.get('copy_sla', True):
            for sla in instance.sla_definitions.filter(is_active=True):
                SLADefinition.objects.create(
                    definition=new_definition,
                    task_definition_key=sla.task_definition_key,
                    duration_value=sla.duration_value,
                    duration_unit=sla.duration_unit,
                    action_on_violation=sla.action_on_violation,
                    escalation_config=sla.escalation_config,
                    is_active=True,
                )
        
        return Response(
            ProcessDefinitionDetailSerializer(new_definition, context={'request': request}).data,
            status=status.HTTP_201_CREATED
        )

    @action(detail=True, methods=['post'])
    def deactivate(self, request, pk=None):
        """Deactivate a process definition."""
        instance = self.get_object()
        instance.is_active = False
        instance.save(update_fields=['is_active'])
        
        return Response(
            ProcessDefinitionDetailSerializer(instance, context={'request': request}).data,
            status=status.HTTP_200_OK
        )

    @action(detail=True, methods=['post'])
    def activate(self, request, pk=None):
        """Activate a process definition."""
        instance = self.get_object()
        instance.is_active = True
        instance.save(update_fields=['is_active'])
        
        return Response(
            ProcessDefinitionDetailSerializer(instance, context={'request': request}).data,
            status=status.HTTP_200_OK
        )


class ProcessInstanceViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing process instances.
    
    Provides CRUD operations plus:
    - start: Start a new process instance
    - cancel: Cancel a running instance
    - suspend: Suspend a running instance
    - resume: Resume a suspended instance
    - history: Get process history
    - variables: Get/update process variables
    """
    permission_classes = [IsAuthenticated, ProcessInstancePermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_class = ProcessInstanceFilter
    search_fields = ['business_key', 'definition__name']
    ordering_fields = ['started_at', 'completed_at', 'status']
    ordering = ['-started_at']
    http_method_names = ['get', 'post', 'head', 'options']  # No PUT/PATCH/DELETE

    def get_queryset(self):
        user = self.request.user
        queryset = ProcessInstance.objects.filter(tenant=user.tenant)
        
        # Apply visibility filter based on permissions
        if not user.is_superuser and not user.has_perm('workflow.view_all_instances'):
            # Users can see instances they started or have tasks in
            queryset = queryset.filter(
                Q(started_by=user) |
                Q(tasks__assignments__user=user, tasks__assignments__is_active=True)
            ).distinct()
        
        # Prefetch for detail view
        if self.action == 'retrieve':
            queryset = queryset.prefetch_related(
                'variables',
                Prefetch(
                    'tasks',
                    queryset=Task.objects.filter(
                        status__in=[TaskStatus.CREATED, TaskStatus.ASSIGNED, TaskStatus.CLAIMED]
                    ).select_related('assignee')
                )
            )
        
        return queryset.select_related('definition', 'started_by')

    def get_serializer_class(self):
        if self.action == 'list':
            return ProcessInstanceListSerializer
        elif self.action == 'retrieve':
            return ProcessInstanceDetailSerializer
        elif self.action == 'start':
            return ProcessInstanceStartSerializer
        elif self.action == 'cancel':
            return ProcessInstanceCancelSerializer
        elif self.action == 'suspend':
            return ProcessInstanceSuspendSerializer
        elif self.action == 'resume':
            return ProcessInstanceResumeSerializer
        return ProcessInstanceDetailSerializer

    @action(detail=False, methods=['post'])
    def start(self, request):
        """Start a new process instance."""
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        instance = serializer.save()
        
        # TODO: Call workflow engine to start execution
        
        return Response(
            ProcessInstanceDetailSerializer(instance, context={'request': request}).data,
            status=status.HTTP_201_CREATED
        )

    @action(detail=True, methods=['post'])
    def cancel(self, request, pk=None):
        """Cancel a process instance."""
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data)
        serializer.is_valid(raise_exception=True)
        
        instance.status = InstanceStatus.CANCELLED
        instance.cancelled_at = timezone.now()
        instance.error_message = serializer.validated_data.get('reason', '')
        instance.save(update_fields=['status', 'cancelled_at', 'error_message'])
        
        # Cancel all active tasks
        instance.tasks.active().update(
            status=TaskStatus.CANCELLED,
            completed_at=timezone.now()
        )
        
        return Response(
            ProcessInstanceDetailSerializer(instance, context={'request': request}).data,
            status=status.HTTP_200_OK
        )

    @action(detail=True, methods=['post'])
    def suspend(self, request, pk=None):
        """Suspend a process instance."""
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data)
        serializer.is_valid(raise_exception=True)
        
        instance.status = InstanceStatus.SUSPENDED
        instance.save(update_fields=['status'])
        
        return Response(
            ProcessInstanceDetailSerializer(instance, context={'request': request}).data,
            status=status.HTTP_200_OK
        )

    @action(detail=True, methods=['post'])
    def resume(self, request, pk=None):
        """Resume a suspended process instance."""
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data)
        serializer.is_valid(raise_exception=True)
        
        instance.status = InstanceStatus.RUNNING
        instance.save(update_fields=['status'])
        
        return Response(
            ProcessInstanceDetailSerializer(instance, context={'request': request}).data,
            status=status.HTTP_200_OK
        )

    @action(detail=True, methods=['get'])
    def history(self, request, pk=None):
        """Get process instance history."""
        instance = self.get_object()
        history = instance.history.all().select_related('user', 'task')
        
        # Pagination could be added here
        serializer = ProcessHistorySerializer(history, many=True)
        return Response(serializer.data)

    @action(detail=True, methods=['get', 'post'])
    def variables(self, request, pk=None):
        """Get or set process variables."""
        instance = self.get_object()
        
        if request.method == 'GET':
            variables = instance.variables.all()
            serializer = ProcessVariableSerializer(variables, many=True)
            return Response(serializer.data)
        
        elif request.method == 'POST':
            # Expect: { "name": "value" } or {"variables": [{"name": "", "value": "", "type": ""}]}
            data = request.data
            
            if 'variables' in data:
                for var_data in data['variables']:
                    ProcessVariable.objects.update_or_create(
                        instance=instance,
                        name=var_data['name'],
                        scope=var_data.get('scope'),
                        defaults={
                            'value': var_data['value'],
                            'type': var_data.get('type', 'string')
                        }
                    )
            else:
                for name, value in data.items():
                    ProcessVariable.objects.update_or_create(
                        instance=instance,
                        name=name,
                        scope=None,
                        defaults={'value': value, 'type': 'string'}
                    )
            
            variables = instance.variables.all()
            serializer = ProcessVariableSerializer(variables, many=True)
            return Response(serializer.data)


class TaskViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing tasks.
    
    Provides CRUD operations plus:
    - claim: Claim a task
    - unclaim: Release a claimed task
    - complete: Complete a task
    - delegate: Delegate a task to another user
    - reassign: Reassign task candidates (admin)
    - inbox: Get user's task inbox
    - my_tasks: Get user's claimed tasks
    """
    permission_classes = [IsAuthenticated, TaskPermission]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_class = TaskFilter
    search_fields = ['name', 'description', 'instance__business_key']
    ordering_fields = ['priority', 'due_date', 'created_at']
    ordering = ['-priority', '-created_at']
    http_method_names = ['get', 'post', 'head', 'options']  # No PUT/PATCH/DELETE

    def get_queryset(self):
        user = self.request.user
        queryset = Task.objects.filter(tenant=user.tenant)
        
        # Apply visibility filter based on permissions
        if not user.is_superuser and not user.has_perm('workflow.view_all_tasks'):
            # Users can see tasks assigned to them or where they are candidates
            queryset = queryset.filter(
                Q(assignee=user) |
                Q(assignments__user=user, assignments__is_active=True) |
                Q(assignments__group__in=user.groups.all(), assignments__is_active=True) |
                Q(instance__started_by=user)
            ).distinct()
        
        return queryset.select_related('instance', 'instance__definition', 'assignee')

    def get_serializer_class(self):
        if self.action == 'list':
            return TaskListSerializer
        elif self.action in ['retrieve', 'inbox', 'my_tasks']:
            return TaskDetailSerializer
        elif self.action == 'claim':
            return TaskClaimSerializer
        elif self.action == 'unclaim':
            return TaskUnclaimSerializer
        elif self.action == 'complete':
            return TaskCompleteSerializer
        elif self.action == 'delegate':
            return TaskDelegateSerializer
        elif self.action == 'reassign':
            return TaskReassignSerializer
        return TaskDetailSerializer

    @action(detail=False, methods=['get'])
    def inbox(self, request):
        """Get user's task inbox (available tasks)."""
        user = request.user
        tasks = Task.objects.filter(tenant=user.tenant).available_for(user).active()
        tasks = tasks.select_related('instance', 'instance__definition', 'assignee')
        tasks = tasks.order_by('-priority', '-created_at')
        
        page = self.paginate_queryset(tasks)
        if page is not None:
            serializer = TaskListSerializer(page, many=True, context={'request': request})
            return self.get_paginated_response(serializer.data)
        
        serializer = TaskListSerializer(tasks, many=True, context={'request': request})
        return Response(serializer.data)

    @action(detail=False, methods=['get'])
    def my_tasks(self, request):
        """Get user's claimed tasks."""
        user = request.user
        tasks = Task.objects.filter(tenant=user.tenant).assigned_to(user).active()
        tasks = tasks.select_related('instance', 'instance__definition')
        tasks = tasks.order_by('-priority', '-created_at')
        
        page = self.paginate_queryset(tasks)
        if page is not None:
            serializer = TaskListSerializer(page, many=True, context={'request': request})
            return self.get_paginated_response(serializer.data)
        
        serializer = TaskListSerializer(tasks, many=True, context={'request': request})
        return Response(serializer.data)

    @action(detail=True, methods=['post'])
    def claim(self, request, pk=None):
        """Claim a task."""
        task = self.get_object()
        serializer = self.get_serializer(task, data=request.data)
        serializer.is_valid(raise_exception=True)
        
        task.assignee = request.user
        task.status = TaskStatus.CLAIMED
        task.claimed_at = timezone.now()
        task.save(update_fields=['assignee', 'status', 'claimed_at'])
        
        return Response(
            TaskDetailSerializer(task, context={'request': request}).data,
            status=status.HTTP_200_OK
        )

    @action(detail=True, methods=['post'])
    def unclaim(self, request, pk=None):
        """Release a claimed task."""
        task = self.get_object()
        serializer = self.get_serializer(task, data=request.data)
        serializer.is_valid(raise_exception=True)
        
        task.assignee = None
        task.status = TaskStatus.ASSIGNED if task.assignments.filter(is_active=True).exists() else TaskStatus.CREATED
        task.claimed_at = None
        task.save(update_fields=['assignee', 'status', 'claimed_at'])
        
        return Response(
            TaskDetailSerializer(task, context={'request': request}).data,
            status=status.HTTP_200_OK
        )

    @action(detail=True, methods=['post'])
    def complete(self, request, pk=None):
        """Complete a task."""
        task = self.get_object()
        serializer = self.get_serializer(task, data=request.data)
        serializer.is_valid(raise_exception=True)
        
        # Update task
        task.status = TaskStatus.COMPLETED
        task.completed_at = timezone.now()
        task.output_variables = serializer.validated_data.get('variables', {})
        task.save(update_fields=['status', 'completed_at', 'output_variables'])
        
        # TODO: Call workflow engine to continue execution
        
        return Response(
            TaskDetailSerializer(task, context={'request': request}).data,
            status=status.HTTP_200_OK
        )

    @action(detail=True, methods=['post'])
    def delegate(self, request, pk=None):
        """Delegate a task to another user."""
        task = self.get_object()
        serializer = self.get_serializer(task, data=request.data)
        serializer.is_valid(raise_exception=True)
        
        new_assignee = serializer.validated_user
        task.assignee = new_assignee
        task.save(update_fields=['assignee'])
        
        return Response(
            TaskDetailSerializer(task, context={'request': request}).data,
            status=status.HTTP_200_OK
        )

    @action(detail=True, methods=['post'])
    def reassign(self, request, pk=None):
        """Reassign task candidates (admin action)."""
        task = self.get_object()
        serializer = self.get_serializer(task, data=request.data)
        serializer.is_valid(raise_exception=True)
        
        # Clear existing assignments
        task.assignments.filter(is_active=True).update(
            is_active=False,
            unassigned_at=timezone.now()
        )
        
        # Add new assignments
        if serializer.validated_data.get('user_id'):
            TaskAssignment.objects.create(
                task=task,
                user_id=serializer.validated_data['user_id'],
                is_active=True
            )
        
        if serializer.validated_data.get('group_id'):
            TaskAssignment.objects.create(
                task=task,
                group_id=serializer.validated_data['group_id'],
                is_active=True
            )
        
        # Reset task status
        if task.status == TaskStatus.CLAIMED:
            task.status = TaskStatus.ASSIGNED
            task.assignee = None
            task.claimed_at = None
            task.save(update_fields=['status', 'assignee', 'claimed_at'])
        
        return Response(
            TaskDetailSerializer(task, context={'request': request}).data,
            status=status.HTTP_200_OK
        )

    @action(detail=True, methods=['get'])
    def assignments(self, request, pk=None):
        """Get task assignments."""
        task = self.get_object()
        assignments = task.assignments.filter(is_active=True).select_related('user', 'group')
        serializer = TaskAssignmentSerializer(assignments, many=True)
        return Response(serializer.data)


class ProcessHistoryViewSet(viewsets.ReadOnlyModelViewSet):
    """
    ViewSet for viewing process history.
    
    Read-only access to process history entries.
    """
    permission_classes = [IsAuthenticated, TenantIsolationPermission]
    serializer_class = ProcessHistorySerializer
    filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
    filterset_class = ProcessHistoryFilter
    ordering_fields = ['created_at']
    ordering = ['-created_at']

    def get_queryset(self):
        user = self.request.user
        
        # Filter by instances the user can access
        if user.is_superuser or user.has_perm('workflow.view_all_instances'):
            return ProcessHistory.objects.filter(
                instance__tenant=user.tenant
            ).select_related('instance', 'task', 'user')
        
        return ProcessHistory.objects.filter(
            Q(instance__started_by=user) |
            Q(instance__tasks__assignments__user=user, instance__tasks__assignments__is_active=True),
            instance__tenant=user.tenant
        ).distinct().select_related('instance', 'task', 'user')


class SLADefinitionViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing SLA definitions.
    """
    permission_classes = [IsAuthenticated, ProcessDefinitionPermission]
    filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
    ordering_fields = ['task_definition_key', 'duration_value']
    ordering = ['task_definition_key']

    def get_queryset(self):
        user = self.request.user
        return SLADefinition.objects.filter(
            definition__tenant=user.tenant
        ).select_related('definition')

    def get_serializer_class(self):
        if self.action in ['create', 'update', 'partial_update']:
            return SLADefinitionCreateSerializer
        return SLADefinitionSerializer


class SLAViolationViewSet(viewsets.ReadOnlyModelViewSet):
    """
    ViewSet for viewing SLA violations.
    
    Read-only access to SLA violation records.
    """
    permission_classes = [IsAuthenticated, TenantIsolationPermission]
    serializer_class = SLAViolationSerializer
    filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
    ordering_fields = ['violation_time', 'is_resolved']
    ordering = ['-violation_time']

    def get_queryset(self):
        user = self.request.user
        return SLAViolation.objects.filter(
            task__tenant=user.tenant
        ).select_related('task', 'sla_definition')
