"""
Workflow Service Models.

This module contains all models for the Workflow Engine including:
- ProcessDefinition: BPMN process definitions with versioning
- ProcessInstance: Running instances of processes
- Task: User tasks, service tasks, script tasks
- TaskAssignment: Task assignments to users/groups
- ProcessVariable: Process variables with scoping
- ProcessHistory: Audit trail of process execution
- SLADefinition: SLA rules for tasks
- SLAViolation: SLA violation records
"""
import uuid

from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _

from apps.core.tenant.models import TenantAwareModel
from .managers import (
    ProcessDefinitionManager,
    ProcessInstanceManager,
    TaskManager,
    TaskAssignmentManager,
    ProcessVariableManager,
    ProcessHistoryManager,
    SLADefinitionManager,
    SLAViolationManager,
)


# =============================================================================
# Enums
# =============================================================================

class InstanceStatus(models.TextChoices):
    """Process instance status."""
    RUNNING = 'running', _('در حال اجرا')
    COMPLETED = 'completed', _('تکمیل شده')
    CANCELLED = 'cancelled', _('لغو شده')
    FAILED = 'failed', _('با خطا')
    SUSPENDED = 'suspended', _('معلق')


class TaskType(models.TextChoices):
    """Task types supported by the engine."""
    USER_TASK = 'user_task', _('وظیفه کاربری')
    SERVICE_TASK = 'service_task', _('وظیفه سرویس')
    SCRIPT_TASK = 'script_task', _('وظیفه اسکریپت')


class TaskStatus(models.TextChoices):
    """Task status."""
    CREATED = 'created', _('ایجاد شده')
    ASSIGNED = 'assigned', _('تخصیص داده شده')
    CLAIMED = 'claimed', _('گرفته شده')
    COMPLETED = 'completed', _('تکمیل شده')
    CANCELLED = 'cancelled', _('لغو شده')
    FAILED = 'failed', _('با خطا')


class AssignmentType(models.TextChoices):
    """Task assignment types."""
    CANDIDATE = 'candidate', _('کاندید')
    ASSIGNEE = 'assignee', _('مسئول')
    DELEGATED = 'delegated', _('واگذار شده')


class HistoryEventType(models.TextChoices):
    """Process history event types."""
    INSTANCE_STARTED = 'instance_started', _('فرآیند شروع شد')
    INSTANCE_COMPLETED = 'instance_completed', _('فرآیند تکمیل شد')
    INSTANCE_CANCELLED = 'instance_cancelled', _('فرآیند لغو شد')
    INSTANCE_FAILED = 'instance_failed', _('فرآیند با خطا مواجه شد')
    INSTANCE_SUSPENDED = 'instance_suspended', _('فرآیند معلق شد')
    INSTANCE_RESUMED = 'instance_resumed', _('فرآیند ادامه یافت')
    TASK_CREATED = 'task_created', _('وظیفه ایجاد شد')
    TASK_ASSIGNED = 'task_assigned', _('وظیفه تخصیص یافت')
    TASK_CLAIMED = 'task_claimed', _('وظیفه گرفته شد')
    TASK_UNCLAIMED = 'task_unclaimed', _('وظیفه آزاد شد')
    TASK_COMPLETED = 'task_completed', _('وظیفه تکمیل شد')
    TASK_DELEGATED = 'task_delegated', _('وظیفه واگذار شد')
    TASK_FAILED = 'task_failed', _('وظیفه با خطا مواجه شد')
    VARIABLE_CHANGED = 'variable_changed', _('متغیر تغییر کرد')
    SLA_VIOLATED = 'sla_violated', _('SLA نقض شد')


class DurationUnit(models.TextChoices):
    """SLA duration units."""
    MINUTES = 'minutes', _('دقیقه')
    HOURS = 'hours', _('ساعت')
    DAYS = 'days', _('روز')
    WEEKS = 'weeks', _('هفته')


class SLAAction(models.TextChoices):
    """Actions on SLA violation."""
    NOTIFY = 'notify', _('اطلاع‌رسانی')
    ESCALATE = 'escalate', _('Escalation')
    REASSIGN = 'reassign', _('تخصیص مجدد')


# =============================================================================
# Models
# =============================================================================

class ProcessDefinition(TenantAwareModel):
    """
    BPMN Process Definition with versioning support.
    
    Each process can have multiple versions. Only deployed versions
    can be used to start new instances.
    """
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False
    )
    slug = models.SlugField(
        max_length=100,
        verbose_name=_('شناسه'),
        help_text=_('شناسه یکتا برای URL')
    )
    name = models.CharField(
        max_length=255,
        verbose_name=_('نام'),
        help_text=_('نام نمایشی فرآیند')
    )
    description = models.TextField(
        blank=True,
        null=True,
        verbose_name=_('توضیحات')
    )
    bpmn_xml = models.TextField(
        verbose_name=_('BPMN XML'),
        help_text=_('محتوای BPMN 2.0 XML')
    )
    metadata = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_('متادیتا'),
        help_text=_('اطلاعات اضافی مانند tags و category')
    )
    version = models.PositiveIntegerField(
        default=1,
        verbose_name=_('نسخه')
    )
    is_deployed = models.BooleanField(
        default=False,
        verbose_name=_('منتشر شده'),
        help_text=_('فقط نسخه‌های منتشر شده قابل اجرا هستند')
    )
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('فعال')
    )
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='created_process_definitions',
        verbose_name=_('ایجادکننده')
    )
    deployed_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_('تاریخ انتشار')
    )
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name=_('تاریخ ایجاد')
    )
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name=_('تاریخ بروزرسانی')
    )

    # Custom manager
    objects = ProcessDefinitionManager()

    class Meta:
        db_table = 'workflow_process_definition'
        verbose_name = _('تعریف فرآیند')
        verbose_name_plural = _('تعریف فرآیندها')
        unique_together = ['tenant', 'slug', 'version']
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['tenant', 'slug']),
            models.Index(fields=['tenant', 'is_deployed']),
            models.Index(fields=['tenant', 'is_active']),
        ]
        permissions = [
            ('deploy_processdefinition', _('انتشار فرآیند')),
        ]

    def __str__(self):
        return f"{self.name} (v{self.version})"

    def can_edit(self):
        """Check if definition can be edited (only draft versions)."""
        return not self.is_deployed

    def get_latest_version(self):
        """Get the latest version number for this slug."""
        return ProcessDefinition.objects.filter(
            tenant=self.tenant,
            slug=self.slug
        ).aggregate(models.Max('version'))['version__max'] or 0


class ProcessInstance(TenantAwareModel):
    """
    A running instance of a process definition.
    
    Tracks the execution state and variables of a process.
    """
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False
    )
    definition = models.ForeignKey(
        ProcessDefinition,
        on_delete=models.PROTECT,
        related_name='instances',
        verbose_name=_('تعریف فرآیند')
    )
    started_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='started_process_instances',
        verbose_name=_('شروع‌کننده')
    )
    business_key = models.CharField(
        max_length=255,
        blank=True,
        null=True,
        verbose_name=_('کلید کسب‌وکار'),
        help_text=_('شناسه منحصر به فرد کسب‌وکار (مثلاً شماره درخواست)')
    )
    status = models.CharField(
        max_length=20,
        choices=InstanceStatus.choices,
        default=InstanceStatus.RUNNING,
        verbose_name=_('وضعیت')
    )
    context = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_('داده‌های اولیه'),
        help_text=_('داده‌هایی که در شروع فرآیند ارسال شده')
    )
    current_element_ids = models.JSONField(
        default=list,
        blank=True,
        verbose_name=_('المان‌های فعلی'),
        help_text=_('لیست element IDهای فعال در BPMN')
    )
    started_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name=_('زمان شروع')
    )
    completed_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_('زمان پایان')
    )
    cancelled_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_('زمان لغو')
    )
    error_message = models.TextField(
        blank=True,
        null=True,
        verbose_name=_('پیام خطا')
    )

    # Custom manager
    objects = ProcessInstanceManager()

    class Meta:
        db_table = 'workflow_process_instance'
        verbose_name = _('نمونه فرآیند')
        verbose_name_plural = _('نمونه‌های فرآیند')
        ordering = ['-started_at']
        indexes = [
            models.Index(fields=['tenant', 'status']),
            models.Index(fields=['tenant', 'business_key']),
            models.Index(fields=['definition']),
            models.Index(fields=['started_by']),
        ]
        permissions = [
            ('start_processinstance', _('شروع فرآیند')),
            ('cancel_processinstance', _('لغو فرآیند')),
            ('view_all_instances', _('مشاهده همه نمونه‌ها')),
        ]

    def __str__(self):
        return f"{self.definition.name} - {self.business_key or self.id}"

    @property
    def is_active(self):
        """Check if instance is still active."""
        return self.status == InstanceStatus.RUNNING

    def can_cancel(self, user):
        """Check if user can cancel this instance."""
        return (
            self.is_active and 
            (self.started_by == user or user.has_perm('workflow.cancel_processinstance'))
        )


class Task(TenantAwareModel):
    """
    A task in a process instance.
    
    Supports user tasks, service tasks, and script tasks.
    """
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False
    )
    instance = models.ForeignKey(
        ProcessInstance,
        on_delete=models.CASCADE,
        related_name='tasks',
        verbose_name=_('نمونه فرآیند')
    )
    task_definition_key = models.CharField(
        max_length=255,
        verbose_name=_('کلید تعریف'),
        help_text=_('کلید task در BPMN')
    )
    element_id = models.CharField(
        max_length=255,
        verbose_name=_('شناسه المان'),
        help_text=_('شناسه element در BPMN XML')
    )
    task_type = models.CharField(
        max_length=20,
        choices=TaskType.choices,
        default=TaskType.USER_TASK,
        verbose_name=_('نوع وظیفه')
    )
    name = models.CharField(
        max_length=255,
        verbose_name=_('نام')
    )
    description = models.TextField(
        blank=True,
        null=True,
        verbose_name=_('توضیحات')
    )
    form_data = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_('تعریف فرم'),
        help_text=_('JSON Schema برای فرم کاربر')
    )
    input_variables = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_('متغیرهای ورودی')
    )
    output_variables = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_('متغیرهای خروجی')
    )
    assignee = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='assigned_workflow_tasks',
        verbose_name=_('مسئول')
    )
    status = models.CharField(
        max_length=20,
        choices=TaskStatus.choices,
        default=TaskStatus.CREATED,
        verbose_name=_('وضعیت')
    )
    priority = models.PositiveIntegerField(
        default=50,
        verbose_name=_('اولویت'),
        help_text=_('از 0 تا 100، عدد بالاتر = اولویت بیشتر')
    )
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name=_('زمان ایجاد')
    )
    claimed_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_('زمان گرفتن')
    )
    completed_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_('زمان تکمیل')
    )
    due_date = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_('مهلت')
    )
    error_message = models.TextField(
        blank=True,
        null=True,
        verbose_name=_('پیام خطا')
    )

    # Custom manager
    objects = TaskManager()

    class Meta:
        db_table = 'workflow_task'
        verbose_name = _('وظیفه')
        verbose_name_plural = _('وظایف')
        ordering = ['-priority', '-created_at']
        indexes = [
            models.Index(fields=['tenant', 'status']),
            models.Index(fields=['instance']),
            models.Index(fields=['assignee', 'status']),
            models.Index(fields=['due_date']),
            models.Index(fields=['task_definition_key']),
        ]
        permissions = [
            ('claim_task', _('گرفتن وظیفه')),
            ('complete_task', _('تکمیل وظیفه')),
            ('delegate_task', _('واگذاری وظیفه')),
            ('reassign_task', _('تخصیص مجدد')),
            ('view_all_tasks', _('مشاهده همه وظایف')),
        ]

    def __str__(self):
        return f"{self.name} ({self.get_status_display()})"

    @property
    def is_active(self):
        """Check if task is still active."""
        return self.status in [TaskStatus.CREATED, TaskStatus.ASSIGNED, TaskStatus.CLAIMED]

    def is_candidate(self, user):
        """Check if user is a candidate for this task."""
        # Check direct assignment
        if TaskAssignment.objects.filter(
            task=self,
            user=user,
            is_active=True
        ).exists():
            return True
        
        # Check group assignment
        user_groups = user.groups.values_list('id', flat=True)
        if TaskAssignment.objects.filter(
            task=self,
            group_id__in=user_groups,
            is_active=True
        ).exists():
            return True
        
        return False

    def can_claim(self, user):
        """Check if user can claim this task."""
        if self.status not in [TaskStatus.CREATED, TaskStatus.ASSIGNED]:
            return False
        return self.is_candidate(user)

    def can_complete(self, user):
        """Check if user can complete this task."""
        return self.status == TaskStatus.CLAIMED and self.assignee == user


class TaskAssignment(models.Model):
    """
    Task assignment to users or groups.
    
    Tracks both candidate users/groups and actual assignments.
    """
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False
    )
    task = models.ForeignKey(
        Task,
        on_delete=models.CASCADE,
        related_name='assignments',
        verbose_name=_('وظیفه')
    )
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='workflow_task_assignments',
        verbose_name=_('کاربر')
    )
    group = models.ForeignKey(
        'auth.Group',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='workflow_task_assignments',
        verbose_name=_('گروه')
    )
    assignment_type = models.CharField(
        max_length=20,
        choices=AssignmentType.choices,
        default=AssignmentType.CANDIDATE,
        verbose_name=_('نوع تخصیص')
    )
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('فعال')
    )
    assigned_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name=_('زمان تخصیص')
    )
    unassigned_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_('زمان لغو تخصیص')
    )

    # Custom manager
    objects = TaskAssignmentManager()

    class Meta:
        db_table = 'workflow_task_assignment'
        verbose_name = _('تخصیص وظیفه')
        verbose_name_plural = _('تخصیص‌های وظیفه')
        indexes = [
            models.Index(fields=['task']),
            models.Index(fields=['user', 'is_active']),
            models.Index(fields=['group', 'is_active']),
        ]

    def __str__(self):
        target = self.user.email if self.user else self.group.name
        return f"{self.task.name} → {target}"


class ProcessVariable(models.Model):
    """
    Process variables with scoping support.
    
    Variables can be global to the instance or scoped to specific elements.
    """
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False
    )
    instance = models.ForeignKey(
        ProcessInstance,
        on_delete=models.CASCADE,
        related_name='variables',
        verbose_name=_('نمونه فرآیند')
    )
    name = models.CharField(
        max_length=255,
        verbose_name=_('نام متغیر')
    )
    type = models.CharField(
        max_length=50,
        default='string',
        verbose_name=_('نوع داده'),
        help_text=_('string, number, boolean, object, array')
    )
    value = models.JSONField(
        null=True,
        blank=True,
        verbose_name=_('مقدار')
    )
    scope = models.CharField(
        max_length=255,
        blank=True,
        null=True,
        verbose_name=_('محدوده'),
        help_text=_('Element ID برای scope محلی، خالی برای global')
    )
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name=_('زمان ایجاد')
    )
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name=_('زمان بروزرسانی')
    )

    # Custom manager
    objects = ProcessVariableManager()

    class Meta:
        db_table = 'workflow_process_variable'
        verbose_name = _('متغیر فرآیند')
        verbose_name_plural = _('متغیرهای فرآیند')
        unique_together = ['instance', 'name', 'scope']
        indexes = [
            models.Index(fields=['instance', 'name']),
        ]

    def __str__(self):
        return f"{self.name} = {self.value}"


class ProcessHistory(models.Model):
    """
    Audit trail of process execution.
    
    Records all significant events in the process lifecycle.
    """
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False
    )
    instance = models.ForeignKey(
        ProcessInstance,
        on_delete=models.CASCADE,
        related_name='history',
        verbose_name=_('نمونه فرآیند')
    )
    task = models.ForeignKey(
        Task,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='history',
        verbose_name=_('وظیفه')
    )
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='workflow_history',
        verbose_name=_('کاربر')
    )
    event_type = models.CharField(
        max_length=50,
        choices=HistoryEventType.choices,
        verbose_name=_('نوع رویداد')
    )
    element_id = models.CharField(
        max_length=255,
        blank=True,
        null=True,
        verbose_name=_('شناسه المان')
    )
    old_value = models.JSONField(
        null=True,
        blank=True,
        verbose_name=_('مقدار قبلی')
    )
    new_value = models.JSONField(
        null=True,
        blank=True,
        verbose_name=_('مقدار جدید')
    )
    comment = models.TextField(
        blank=True,
        null=True,
        verbose_name=_('توضیحات')
    )
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name=_('زمان')
    )

    # Custom manager
    objects = ProcessHistoryManager()

    class Meta:
        db_table = 'workflow_process_history'
        verbose_name = _('تاریخچه فرآیند')
        verbose_name_plural = _('تاریخچه فرآیندها')
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['instance', 'created_at']),
            models.Index(fields=['event_type']),
        ]

    def __str__(self):
        return f"{self.get_event_type_display()} - {self.created_at}"


class SLADefinition(models.Model):
    """
    SLA definition for tasks in a process.
    
    Defines the expected completion time and actions on violation.
    """
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False
    )
    definition = models.ForeignKey(
        ProcessDefinition,
        on_delete=models.CASCADE,
        related_name='sla_definitions',
        verbose_name=_('تعریف فرآیند')
    )
    task_definition_key = models.CharField(
        max_length=255,
        verbose_name=_('کلید وظیفه'),
        help_text=_('کلید task در BPMN که SLA برای آن تعریف شده')
    )
    duration_value = models.PositiveIntegerField(
        verbose_name=_('مدت زمان')
    )
    duration_unit = models.CharField(
        max_length=20,
        choices=DurationUnit.choices,
        default=DurationUnit.HOURS,
        verbose_name=_('واحد زمان')
    )
    action_on_violation = models.CharField(
        max_length=20,
        choices=SLAAction.choices,
        default=SLAAction.NOTIFY,
        verbose_name=_('اقدام در صورت نقض')
    )
    escalation_config = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_('تنظیمات Escalation'),
        help_text=_('تنظیمات اضافی مانند گیرندگان notification')
    )
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('فعال')
    )

    # Custom manager
    objects = SLADefinitionManager()

    class Meta:
        db_table = 'workflow_sla_definition'
        verbose_name = _('تعریف SLA')
        verbose_name_plural = _('تعریف‌های SLA')
        unique_together = ['definition', 'task_definition_key']

    def __str__(self):
        return f"SLA: {self.task_definition_key} ({self.duration_value} {self.get_duration_unit_display()})"

    @property
    def duration_minutes(self):
        """Get duration in minutes."""
        multipliers = {
            DurationUnit.MINUTES: 1,
            DurationUnit.HOURS: 60,
            DurationUnit.DAYS: 60 * 24,
            DurationUnit.WEEKS: 60 * 24 * 7,
        }
        return self.duration_value * multipliers.get(self.duration_unit, 1)


class SLAViolation(models.Model):
    """
    Records of SLA violations.
    """
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False
    )
    task = models.ForeignKey(
        Task,
        on_delete=models.CASCADE,
        related_name='sla_violations',
        verbose_name=_('وظیفه')
    )
    sla_definition = models.ForeignKey(
        SLADefinition,
        on_delete=models.CASCADE,
        related_name='violations',
        verbose_name=_('تعریف SLA')
    )
    expected_completion = models.DateTimeField(
        verbose_name=_('زمان مورد انتظار')
    )
    violation_time = models.DateTimeField(
        auto_now_add=True,
        verbose_name=_('زمان نقض')
    )
    is_resolved = models.BooleanField(
        default=False,
        verbose_name=_('حل شده')
    )
    resolved_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_('زمان حل')
    )

    # Custom manager
    objects = SLAViolationManager()

    class Meta:
        db_table = 'workflow_sla_violation'
        verbose_name = _('نقض SLA')
        verbose_name_plural = _('نقض‌های SLA')
        indexes = [
            models.Index(fields=['task']),
            models.Index(fields=['is_resolved']),
        ]

    def __str__(self):
        return f"SLA Violation: {self.task.name}"
