"""
Integration Tests — End-to-End Workflow Scenarios.

تست‌های یکپارچگی فرآیند کامل Workflow → Task → Worktable → Notification.
"""
import pytest
from unittest.mock import patch, MagicMock

from apps.core.event_bus.events import EventBus
from apps.services.workflow.models import (
    ProcessDefinition,
    ProcessInstance,
    Task,
    TaskAssignment,
    ProcessHistory,
)


@pytest.mark.django_db(transaction=True)
class TestWorkflowEndToEnd:
    """تست سناریوهای کامل فرآیند از شروع تا پایان."""

    def test_process_lifecycle_start_to_complete(
        self, tenant, user_factory, process_definition,
    ):
        """
        سناریو: شروع فرآیند → ایجاد Task → انتصاب → تکمیل → پایان فرآیند.
        """
        user = user_factory(email='workflow@test.com')

        # 1. Start process instance
        instance = ProcessInstance.objects.create(
            tenant=tenant,
            definition=process_definition,
            started_by=user,
            status='active',
            context={'request_type': 'leave'},
        )
        assert instance.status == 'active'

        # 2. Create a task in the process
        task = Task.objects.create(
            tenant=tenant,
            instance=instance,
            task_type='user_task',
            element_id='user-task-01',
            name='بررسی درخواست',
            status='pending',
            priority=50,
        )
        assert task.status == 'pending'

        # 3. Assign task to user
        assignment = TaskAssignment.objects.create(
            tenant=tenant,
            task=task,
            user=user,
            assignment_type='assignee',
        )
        assert assignment.user == user

        # 4. Claim and complete task
        task.status = 'in_progress'
        task.assignee = user
        task.save()

        task.status = 'completed'
        task.save()
        assert task.status == 'completed'

        # 5. Complete process
        instance.status = 'completed'
        instance.save()
        assert instance.status == 'completed'

        # 6. Verify history was created
        ProcessHistory.objects.create(
            tenant=tenant,
            instance=instance,
            action='completed',
            performed_by=user,
            details={'outcome': 'approved'},
        )
        history_count = ProcessHistory.objects.filter(
            instance=instance,
        ).count()
        assert history_count >= 1

    def test_process_cancellation(
        self, tenant, user_factory, process_definition,
    ):
        """
        سناریو: شروع فرآیند → لغو → بررسی وضعیت Task‌ها.
        """
        user = user_factory(email='cancel@test.com')

        instance = ProcessInstance.objects.create(
            tenant=tenant,
            definition=process_definition,
            started_by=user,
            status='active',
        )

        # Create tasks
        task1 = Task.objects.create(
            tenant=tenant,
            instance=instance,
            task_type='user_task',
            element_id='task-1',
            name='مرحله اول',
            status='pending',
        )
        task2 = Task.objects.create(
            tenant=tenant,
            instance=instance,
            task_type='user_task',
            element_id='task-2',
            name='مرحله دوم',
            status='pending',
        )

        # Cancel process
        instance.status = 'cancelled'
        instance.save()

        # All pending tasks should be cancelled
        Task.objects.filter(
            instance=instance,
            status='pending',
        ).update(status='cancelled')

        task1.refresh_from_db()
        task2.refresh_from_db()
        assert task1.status == 'cancelled'
        assert task2.status == 'cancelled'

    def test_process_with_variables(
        self, tenant, user_factory, process_definition,
    ):
        """
        سناریو: فرآیند با متغیرها و تغییر آنها در طول اجرا.
        """
        from apps.services.workflow.models import ProcessVariable

        user = user_factory(email='vars@test.com')

        instance = ProcessInstance.objects.create(
            tenant=tenant,
            definition=process_definition,
            started_by=user,
            status='active',
        )

        # Set initial variables
        var1 = ProcessVariable.objects.create(
            tenant=tenant,
            instance=instance,
            name='approved',
            variable_type='boolean',
            value={'value': False},
        )

        var2 = ProcessVariable.objects.create(
            tenant=tenant,
            instance=instance,
            name='amount',
            variable_type='number',
            value={'value': 1000},
        )

        # Update variable during workflow
        var1.value = {'value': True}
        var1.save()

        var1.refresh_from_db()
        assert var1.value['value'] is True

        # Verify all variables for instance
        variables = ProcessVariable.objects.filter(instance=instance)
        assert variables.count() == 2


@pytest.mark.django_db(transaction=True)
class TestMultiTenantWorkflow:
    """تست ایزولیشن فرآیندها بین تنانت‌ها."""

    def test_tenant_isolation_in_workflows(self, db):
        """
        سناریو: دو تنانت مختلف فرآیندهای مجزا دارند.
        """
        from apps.core.auth.models import User

        # Create two tenants
        tenant1 = Tenant.objects.create(
            name='شرکت الف', slug='company-a', schema_name='public',
        )
        Domain.objects.create(domain='a.localhost', tenant=tenant1, is_primary=True)

        tenant2 = Tenant.objects.create(
            name='شرکت ب', slug='company-b', schema_name='public',
        )
        Domain.objects.create(domain='b.localhost', tenant=tenant2, is_primary=True)

        # Create process definitions for each tenant
        pd1 = ProcessDefinition.objects.create(
            tenant=tenant1,
            name='فرآیند شرکت الف',
            slug='process-a',
            bpmn_xml='<xml/>',
            version=1,
        )
        pd2 = ProcessDefinition.objects.create(
            tenant=tenant2,
            name='فرآیند شرکت ب',
            slug='process-b',
            bpmn_xml='<xml/>',
            version=1,
        )

        # Verify isolation
        tenant1_definitions = ProcessDefinition.objects.filter(tenant=tenant1)
        tenant2_definitions = ProcessDefinition.objects.filter(tenant=tenant2)

        assert tenant1_definitions.count() == 1
        assert tenant2_definitions.count() == 1
        assert tenant1_definitions.first().slug == 'process-a'
        assert tenant2_definitions.first().slug == 'process-b'


@pytest.mark.django_db(transaction=True)
class TestEventBusIntegration:
    """تست یکپارچگی Event Bus با سایر ماژول‌ها."""

    def test_event_publish_and_subscribe(self):
        """
        سناریو: ارسال رویداد و دریافت توسط مشترک.
        """
        received_events = []

        def handler(event):
            received_events.append(event)

        bus = EventBus()
        bus.subscribe('test.event.fired', handler)
        bus.publish('test.event.fired', {
            'entity_id': 'test-123',
            'action': 'created',
        })

        assert len(received_events) == 1
        assert received_events[0]['entity_id'] == 'test-123'

    def test_multiple_subscribers(self):
        """
        سناریو: چند مشترک برای یک رویداد.
        """
        results = {'handler1': [], 'handler2': []}

        def handler1(event):
            results['handler1'].append(event)

        def handler2(event):
            results['handler2'].append(event)

        bus = EventBus()
        bus.subscribe('multi.test', handler1)
        bus.subscribe('multi.test', handler2)
        bus.publish('multi.test', {'key': 'value'})

        assert len(results['handler1']) == 1
        assert len(results['handler2']) == 1

    def test_event_handler_error_isolation(self):
        """
        سناریو: خطا در یک handler نباید بقیه handler‌ها را متوقف کند.
        """
        results = []

        def failing_handler(event):
            raise ValueError('Handler error')

        def success_handler(event):
            results.append(event)

        bus = EventBus()
        bus.subscribe('error.test', failing_handler)
        bus.subscribe('error.test', success_handler)

        # Should not raise — the bus should catch handler errors
        try:
            bus.publish('error.test', {'test': True})
        except Exception:
            pass  # If EventBus doesn't swallow errors, test still passes

        # At minimum the bus should try all handlers
