from django.db import models
from django.contrib.auth import get_user_model
from common.models import NameSlugModel, SluggedModel
from simple_history.models import HistoricalRecords
from django.core.exceptions import ValidationError
from django.utils import timezone
from django.conf import settings
from accounts.models import Role, Broker
from _helpers.utils import generate_unique_slug
import random
User = get_user_model()
class Process(NameSlugModel):
    """مدل فرآیند اصلی"""
    description = models.TextField(verbose_name="توضیحات", blank=True)
    is_active = models.BooleanField(default=True, verbose_name="فعال")
    created_by = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="ایجاد کننده")
    history = HistoricalRecords()
    class Meta:
        verbose_name = "فرآیند"
        verbose_name_plural = "فرآیندها"
        ordering = ['-created']
    def __str__(self):
        return self.name
class ProcessStep(NameSlugModel):
    """مدل مراحل فرآیند"""
    process = models.ForeignKey(Process, on_delete=models.CASCADE, related_name='steps', verbose_name="فرآیند")
    order = models.PositiveIntegerField(verbose_name="ترتیب")
    description = models.TextField(verbose_name="توضیحات", blank=True)
    is_required = models.BooleanField(default=True, verbose_name="اجباری")
    estimated_duration = models.PositiveIntegerField(verbose_name="مدت زمان تخمینی (روز)", null=True, blank=True)
    
    # فیلدهای جدید برای کنترل وابستگیها
    blocks_previous = models.BooleanField(
        default=False, 
        verbose_name="مسدود کننده مراحل قبلی",
        help_text="اگر فعال باشد، پس از تکمیل این مرحله، مراحل قبلی غیرقابل ویرایش میشوند"
    )
    can_go_back = models.BooleanField(
        default=True, 
        verbose_name="قابل بازگشت",
        help_text="آیا میتوان به مراحل قبلی بازگشت"
    )
    history = HistoricalRecords()
    # Note: approver requirements are defined via StepApproverRequirement through model
    # See StepApproverRequirement below
    class Meta:
        verbose_name = "مرحله فرآیند"
        verbose_name_plural = "مراحل فرآیند"
        ordering = ['process', 'order']
        unique_together = ['process', 'order']
    def __str__(self):
        return f"{self.process.name} - {self.name}"
    def get_dependencies(self):
        """دریافت مراحل وابسته"""
        return StepDependency.objects.filter(dependent_step=self).values_list('dependency_step', flat=True)
    def get_dependents(self):
        """دریافت مراحلی که به این مرحله وابسته هستند"""
        return StepDependency.objects.filter(dependency_step=self).values_list('dependent_step', flat=True)
class StepDependency(models.Model):
    """مدل وابستگی بین مراحل"""
    dependent_step = models.ForeignKey(
        ProcessStep, 
        on_delete=models.CASCADE, 
        related_name='dependencies',
        verbose_name="مرحله وابسته"
    )
    dependency_step = models.ForeignKey(
        ProcessStep, 
        on_delete=models.CASCADE, 
        related_name='dependents',
        verbose_name="مرحله مورد نیاز"
    )
    created_at = models.DateTimeField(auto_now_add=True, verbose_name="تاریخ ایجاد")
    class Meta:
        verbose_name = "وابستگی مرحله"
        verbose_name_plural = "وابستگیهای مراحل"
        unique_together = ['dependent_step', 'dependency_step']
        ordering = ['dependent_step__order', 'dependency_step__order']
    def __str__(self):
        return f"{self.dependent_step} ← {self.dependency_step}"
    def clean(self):
        """اعتبارسنجی مدل"""
        if self.dependent_step == self.dependency_step:
            raise ValidationError("مرحله نمیتواند به خودش وابسته باشد")
        
        if self.dependent_step.process != self.dependency_step.process:
            raise ValidationError("مراحل باید از یک فرآیند باشند")
        
        if self.dependent_step.order <= self.dependency_step.order:
            raise ValidationError("مرحله وابسته باید بعد از مرحله مورد نیاز باشد")
class ProcessInstance(SluggedModel):
    code = models.CharField(
        max_length=5,
        unique=True,
        verbose_name="کد درخواست",
        help_text="کد ۵ رقمی یکتا برای هر درخواست"
    )
    """مدل نمونه فرآیند (برای هر درخواست)"""
    PRIORITY_CHOICES = [
        ('low', 'کم'),
        ('medium', 'متوسط'),
        ('high', 'زیاد'),
        ('urgent', 'فوری'),
    ]
    STATUS_CHOICES = [
        ('pending', 'در انتظار'),
        ('in_progress', 'در حال انجام'),
        ('completed', 'تکمیل شده'),
        ('cancelled', 'لغو شده'),
        ('rejected', 'رد شده'),
    ]
    
    description = models.TextField(
        verbose_name="توضیحات درخواست", 
        blank=True,
        null=True
    )
    
    process = models.ForeignKey(
        Process,
        on_delete=models.CASCADE,
        related_name='instances',
        verbose_name="فرآیند",
        null=True,
        blank=True
    )
    
    well = models.ForeignKey(
        'wells.Well', 
        on_delete=models.CASCADE, 
        related_name='process_instances',
        verbose_name="چاه",
    )
    representative = models.ForeignKey(
        User, 
        on_delete=models.SET_NULL, 
        related_name='representative_instances',
        verbose_name="نماینده چاه",
        null=True,
    )
    
    requester = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        verbose_name="درخواست کننده",
        null=True,
        blank=True
    )
    current_step = models.ForeignKey(
        ProcessStep,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name="مرحله فعلی",
    )
    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='pending',
        verbose_name="وضعیت"
    )
    
    priority = models.CharField(
        max_length=20,
        choices=PRIORITY_CHOICES,
        default='medium',
        verbose_name="اولویت"
    )
    broker = models.ForeignKey(
        Broker,
        on_delete=models.SET_NULL,
        verbose_name="کارگزار",
        blank=True,
        null=True,
        related_name='process_instances'
    )
    
    completed_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name="تاریخ تکمیل"
        )
    class Meta:
        verbose_name = "درخواست"
        verbose_name_plural = "درخواستها"
        ordering = ['-created']
    def __str__(self):
        if self.well:
            return f"{self.process.name} - {self.well.water_subscription_number}"
        return f"{self.process.name} - {self.requester.get_full_name()}"
    def save(self, *args, **kwargs):
        # Generate unique 5-digit numeric code if missing
        if not self.code:
            # Try a few times to avoid rare collisions
            for _ in range(10):
                candidate = f"{random.randint(10000, 99999)}"
                if not ProcessInstance.objects.filter(code=candidate).exists():
                    self.code = candidate
                    break
            # As a fallback if collision persists (very unlikely)
            if not self.code:
                self.code = f"{random.randint(10000, 99999)}"
        if not self.slug:
            slug_text = f"{self.process.name}-{self.well.water_subscription_number if self.well else 'unknown'}-{timezone.now().strftime('%Y%m%d')}"
            self.slug = generate_unique_slug(slug_text)
        
        if self.status == 'completed' and not self.completed_at:
            self.completed_at = timezone.now()
        
        # Auto-set broker if not already set
        if not self.broker:
            if self.well and hasattr(self.well, 'broker') and self.well.broker:
                self.broker = self.well.broker
            elif self.requester and hasattr(self.requester, 'profile') and self.requester.profile and hasattr(self.requester.profile, 'broker') and self.requester.profile.broker:
                self.broker = self.requester.profile.broker
        
        super().save(*args, **kwargs)
    def get_status_display_with_color(self):
        """نمایش وضعیت با رنگ"""
        status_colors = {
            'pending': 'info',
            'in_progress': 'primary',
            'completed': 'success',
            'rejected': 'danger',
            'cancelled': 'warning',
        }
        color = status_colors.get(self.status, 'secondary')
        return '{}'.format(color, self.get_status_display())
    def get_priority_display_with_color(self):
        """نمایش اولویت با رنگ"""
        priority_colors = {
            'low': 'success',
            'medium': 'info',
            'high': 'warning',
            'urgent': 'danger',
        }
        color = priority_colors.get(self.priority, 'secondary')
        return '{}'.format(color, self.get_priority_display())
    def can_edit(self, user):
        """بررسی امکان ویرایش درخواست"""
        if self.status == 'pending' and self.requester == user:
            return True
        
        if self.representative == user and self.status in ['pending']:
            return True
        
        return False
    def get_available_steps(self):
        """دریافت مراحل قابل دسترس"""
        available_steps = []
        for step in self.process.steps.all():
            if self.can_access_step(step):
                available_steps.append(step)
        return available_steps
    def can_access_step(self, step):
        """بررسی امکان دسترسی به مرحله"""
        dependencies = step.get_dependencies()
        for dependency_id in dependencies:
            step_instance = self.step_instances.filter(step_id=dependency_id).first()
            if step_instance and step_instance.status == 'in_progress' and step_instance.step.order == 3 and step.order == 4:
                return True
            if step_instance and step_instance.status == 'approved' and step_instance.step.order == 8 and step.order == 9:
                return True
            if not step_instance or step_instance.status != 'completed':
                return False
        return True
    def can_edit_step(self, step):
        """بررسی امکان ویرایش مرحله"""
        if step.blocks_previous:
            later_steps = self.step_instances.filter(
                step__order__gt=step.order,
                status='completed'
            )
            if later_steps.exists():
                return False
        return True
class StepInstance(models.Model):
    """مدل نمونه مرحله (برای هر مرحله در هر درخواست)"""
    process_instance = models.ForeignKey(ProcessInstance, on_delete=models.CASCADE, related_name='step_instances', verbose_name="نمونه فرآیند")
    step = models.ForeignKey(ProcessStep, on_delete=models.CASCADE, verbose_name="مرحله")
    assigned_to = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="واگذار شده به")
    status = models.CharField(
        max_length=20,
        choices=[
            ('pending', 'در انتظار'),
            ('in_progress', 'در حال انجام'),
            ('completed', 'تکمیل شده'),
            ('skipped', 'رد شده'),
            ('blocked', 'مسدود شده'),
            ('rejected', 'رد شده و نیاز به اصلاح'),
            ('approved', 'تایید اضطراری'),
        ],
        default='pending',
        verbose_name="وضعیت"
    )
    notes = models.TextField(verbose_name="یادداشتها", blank=True)
    started_at = models.DateTimeField(auto_now_add=True, verbose_name="تاریخ شروع")
    completed_at = models.DateTimeField(null=True, blank=True, verbose_name="تاریخ تکمیل")
    # Generic edit-tracking for post-completion modifications
    edited_after_completion = models.BooleanField(default=False, verbose_name="ویرایش پس از تکمیل")
    last_edited_at = models.DateTimeField(null=True, blank=True, verbose_name="آخرین زمان ویرایش پس از تکمیل")
    last_edited_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='step_instances_edited', verbose_name="ویرایش توسط")
    edit_count = models.PositiveIntegerField(default=0, verbose_name="تعداد ویرایش پس از تکمیل")
    
    history = HistoricalRecords()
    class Meta:
        verbose_name = "نمونه مرحله"
        verbose_name_plural = "نمونههای مرحله"
        ordering = ['process_instance', 'step__order']
    def __str__(self):
        return f"{self.process_instance} - {self.step.name}"
    def save(self, *args, **kwargs):
        """ذخیره با اعتبارسنجی"""
        if self.status == 'in_progress' or self.status == 'completed':
            if not self.process_instance.can_access_step(self.step):
                raise ValidationError("مراحل وابسته تکمیل نشدهاند")
        
        if self.status == 'completed' and not self.process_instance.can_edit_step(self.step):
            raise ValidationError("این مرحله قابل ویرایش نیست")
        
        super().save(*args, **kwargs)
    def get_status_display_with_color(self):
        """نمایش وضعیت با رنگ"""
        status_colors = {
            'pending': 'secondary',
            'in_progress': 'primary',
            'completed': 'success',
            'skipped': 'warning',
            'blocked': 'danger',
            'rejected': 'danger',
        }
        color = status_colors.get(self.status, 'secondary')
        return '{}'.format(color, self.get_status_display())
    def get_rejection_count(self):
        """دریافت تعداد رد شدنها"""
        return self.rejections.count()
    def get_latest_rejection(self):
        """دریافت آخرین رد شدن"""
        return self.rejections.order_by('-created_at').first()
    # -------- Multi-role approval helpers --------
    def required_roles(self):
        return [req.role for req in self.step.approver_requirements.select_related('role').all()]
    def approvals_by_role(self):
        decisions = {}
        for a in self.approvals.select_related('role').order_by('created_at'):
            decisions[a.role_id] = a.decision
        return decisions
    def is_fully_approved(self) -> bool:
        req_roles = self.required_roles()
        if not req_roles:
            return True
        role_to_decision = self.approvals_by_role()
        for r in req_roles:
            if role_to_decision.get(r.id) != 'approved':
                return False
        return True
class StepRejection(models.Model):
    """مدل رد شدن مرحله"""
    step_instance = models.ForeignKey(
        StepInstance, 
        on_delete=models.CASCADE, 
        related_name='rejections',
        verbose_name="نمونه مرحله"
    )
    rejected_by = models.ForeignKey(
        User, 
        on_delete=models.CASCADE, 
        verbose_name="رد کننده",
        related_name='step_rejections'
    )
    reason = models.TextField(verbose_name="دلیل رد شدن", help_text="توضیح کامل دلیل رد شدن")
    instructions = models.TextField(
        verbose_name="دستورالعملهای اصلاح", 
        help_text="دستورالعملهایی برای اصلاح مرحله",
        blank=True
    )
    created_at = models.DateTimeField(auto_now_add=True, verbose_name="تاریخ رد شدن")
    is_deleted = models.BooleanField(default=False, verbose_name='حذف شده')
    history = HistoricalRecords()
    class Meta:
        verbose_name = "رد شدن مرحله"
        verbose_name_plural = "رد شدنهای مراحل"
        ordering = ['-created_at']
    def __str__(self):
        return f"رد شدن {self.step_instance} توسط {self.rejected_by.get_full_name()}"
    def save(self, *args, **kwargs):
        """ذخیره با تغییر وضعیت مرحله"""
        self.step_instance.status = 'rejected'
        self.step_instance.save()
        super().save(*args, **kwargs)
    def hard_delete(self):
        super().delete()
    def delete(self, *args, **kwargs):
        self.is_deleted = True
        self.save()
class StepApproverRequirement(models.Model):
    """Required approver roles for a step."""
    step = models.ForeignKey(ProcessStep, on_delete=models.CASCADE, related_name='approver_requirements', verbose_name="مرحله")
    role = models.ForeignKey(Role, on_delete=models.CASCADE, verbose_name="نقش تاییدکننده")
    required_count = models.PositiveIntegerField(default=1, verbose_name="تعداد موردنیاز")
    class Meta:
        unique_together = ('step', 'role')
        verbose_name = "نیازمندی تایید نقش"
        verbose_name_plural = "نیازمندیهای تایید نقش"
    def __str__(self):
        return f"{self.step} ← {self.role} (x{self.required_count})"
class StepApproval(models.Model):
    """Approvals per role for a concrete step instance."""
    step_instance = models.ForeignKey(StepInstance, on_delete=models.CASCADE, related_name='approvals', verbose_name="نمونه مرحله")
    role = models.ForeignKey(Role, on_delete=models.CASCADE, verbose_name="نقش")
    approved_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name="تاییدکننده")
    decision = models.CharField(max_length=8, choices=[('approved', 'تایید'), ('rejected', 'رد')], verbose_name='نتیجه')
    reason = models.TextField(blank=True, verbose_name='علت (برای رد)')
    created_at = models.DateTimeField(auto_now_add=True, verbose_name='تاریخ')
    is_deleted = models.BooleanField(default=False, verbose_name='حذف شده')
    class Meta:
        unique_together = ('step_instance', 'role')
        verbose_name = 'تایید مرحله'
        verbose_name_plural = 'تاییدهای مرحله'
    
    def delete(self, *args, **kwargs):
        self.is_deleted = True
        self.save()
    def hard_delete(self):
        super().delete()
    def __str__(self):
        return f"{self.step_instance} - {self.role} - {self.decision}"