"""
Moderation models for ThrillWiki.

This module implements the content submission and approval workflow with:
- State machine using django-fsm
- Atomic transaction support for approvals
- 15-minute review lock mechanism
- Selective approval of individual items
"""

import uuid
from django.db import models
from django.utils import timezone
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django_fsm import FSMField, transition
from apps.core.models import BaseModel


class ContentSubmission(BaseModel):
    """
    Main submission model with FSM state machine.
    
    Represents a batch of changes submitted by a user for moderation.
    Can contain multiple SubmissionItem objects representing individual field changes.
    """
    
    # State choices for FSM
    STATE_DRAFT = 'draft'
    STATE_PENDING = 'pending'
    STATE_REVIEWING = 'reviewing'
    STATE_APPROVED = 'approved'
    STATE_REJECTED = 'rejected'
    
    STATE_CHOICES = [
        (STATE_DRAFT, 'Draft'),
        (STATE_PENDING, 'Pending Review'),
        (STATE_REVIEWING, 'Under Review'),
        (STATE_APPROVED, 'Approved'),
        (STATE_REJECTED, 'Rejected'),
    ]
    
    # FSM State field
    status = FSMField(
        max_length=20,
        choices=STATE_CHOICES,
        default=STATE_DRAFT,
        db_index=True,
        protected=True,  # Prevents direct status changes
        help_text="Current submission state (managed by FSM)"
    )
    
    # Submitter
    user = models.ForeignKey(
        'users.User',
        on_delete=models.CASCADE,
        related_name='submissions',
        help_text="User who submitted the changes"
    )
    
    # Entity being modified (generic relation)
    entity_type = models.ForeignKey(
        ContentType,
        on_delete=models.CASCADE,
        help_text="Type of entity being modified"
    )
    entity_id = models.UUIDField(
        help_text="ID of the entity being modified"
    )
    entity = GenericForeignKey('entity_type', 'entity_id')
    
    # Submission type
    SUBMISSION_TYPE_CHOICES = [
        ('create', 'Create'),
        ('update', 'Update'),
        ('delete', 'Delete'),
    ]
    
    submission_type = models.CharField(
        max_length=20,
        choices=SUBMISSION_TYPE_CHOICES,
        db_index=True,
        help_text="Type of operation (create, update, delete)"
    )
    
    # Submission details
    title = models.CharField(
        max_length=255,
        help_text="Brief description of changes"
    )
    description = models.TextField(
        blank=True,
        help_text="Detailed description of changes"
    )
    
    # Review lock mechanism (15-minute lock)
    locked_by = models.ForeignKey(
        'users.User',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='locked_submissions',
        help_text="Moderator currently reviewing this submission"
    )
    locked_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When the submission was locked for review"
    )
    
    # Review details
    reviewed_by = models.ForeignKey(
        'users.User',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='reviewed_submissions',
        help_text="Moderator who reviewed this submission"
    )
    reviewed_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When the submission was reviewed"
    )
    rejection_reason = models.TextField(
        blank=True,
        help_text="Reason for rejection (if rejected)"
    )
    
    # Metadata
    source = models.CharField(
        max_length=50,
        default='web',
        help_text="Source of submission (web, api, mobile, etc.)"
    )
    ip_address = models.GenericIPAddressField(
        null=True,
        blank=True,
        help_text="IP address of submitter"
    )
    user_agent = models.CharField(
        max_length=500,
        blank=True,
        help_text="User agent of submitter"
    )
    
    # Additional data
    metadata = models.JSONField(
        default=dict,
        blank=True,
        help_text="Additional submission metadata"
    )
    
    class Meta:
        db_table = 'content_submissions'
        ordering = ['-created']
        indexes = [
            models.Index(fields=['status', 'created']),
            models.Index(fields=['user', 'status']),
            models.Index(fields=['entity_type', 'entity_id']),
            models.Index(fields=['locked_by', 'locked_at']),
        ]
        verbose_name = 'Content Submission'
        verbose_name_plural = 'Content Submissions'
    
    def __str__(self):
        return f"{self.get_submission_type_display()} - {self.title} ({self.get_status_display()})"
    
    # FSM Transitions
    
    @transition(field=status, source=STATE_DRAFT, target=STATE_PENDING)
    def submit(self):
        """Submit for review - moves from draft to pending"""
        pass
    
    @transition(field=status, source=STATE_PENDING, target=STATE_REVIEWING)
    def start_review(self, reviewer):
        """Lock submission for review"""
        self.locked_by = reviewer
        self.locked_at = timezone.now()
    
    @transition(field=status, source=STATE_REVIEWING, target=STATE_APPROVED)
    def approve(self, reviewer):
        """Approve submission"""
        self.reviewed_by = reviewer
        self.reviewed_at = timezone.now()
        self.locked_by = None
        self.locked_at = None
    
    @transition(field=status, source=STATE_REVIEWING, target=STATE_REJECTED)
    def reject(self, reviewer, reason):
        """Reject submission"""
        self.reviewed_by = reviewer
        self.reviewed_at = timezone.now()
        self.rejection_reason = reason
        self.locked_by = None
        self.locked_at = None
    
    @transition(field=status, source=STATE_REVIEWING, target=STATE_PENDING)
    def unlock(self):
        """Unlock submission (timeout or manual unlock)"""
        self.locked_by = None
        self.locked_at = None
    
    # Helper methods
    
    def is_locked(self):
        """Check if submission is currently locked"""
        if not self.locked_by or not self.locked_at:
            return False
        
        # Check if lock has expired (15 minutes)
        lock_duration = timezone.now() - self.locked_at
        return lock_duration.total_seconds() < 15 * 60
    
    def can_review(self, user):
        """Check if user can review this submission"""
        if self.status != self.STATE_REVIEWING:
            return False
        
        # Check if locked by another user
        if self.locked_by and self.locked_by != user:
            return not self.is_locked()
        
        return True
    
    def get_items_count(self):
        """Get count of submission items"""
        return self.items.count()
    
    def get_approved_items_count(self):
        """Get count of approved items"""
        return self.items.filter(status='approved').count()
    
    def get_rejected_items_count(self):
        """Get count of rejected items"""
        return self.items.filter(status='rejected').count()


class SubmissionItem(BaseModel):
    """
    Individual change within a submission.
    
    Represents a single field change (or entity creation/deletion).
    Supports selective approval - each item can be approved/rejected independently.
    """
    
    STATUS_CHOICES = [
        ('pending', 'Pending'),
        ('approved', 'Approved'),
        ('rejected', 'Rejected'),
    ]
    
    # Parent submission
    submission = models.ForeignKey(
        ContentSubmission,
        on_delete=models.CASCADE,
        related_name='items',
        help_text="Parent submission"
    )
    
    # Item details
    field_name = models.CharField(
        max_length=100,
        help_text="Name of the field being changed"
    )
    field_label = models.CharField(
        max_length=200,
        blank=True,
        help_text="Human-readable field label"
    )
    
    # Values (stored as JSON for flexibility)
    old_value = models.JSONField(
        null=True,
        blank=True,
        help_text="Previous value (null for new fields)"
    )
    new_value = models.JSONField(
        null=True,
        blank=True,
        help_text="New value (null for deletions)"
    )
    
    # Item status (for selective approval)
    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='pending',
        db_index=True,
        help_text="Status of this individual item"
    )
    
    # Review details (for selective approval)
    reviewed_by = models.ForeignKey(
        'users.User',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='reviewed_items',
        help_text="Moderator who reviewed this item"
    )
    reviewed_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When this item was reviewed"
    )
    rejection_reason = models.TextField(
        blank=True,
        help_text="Reason for rejecting this specific item"
    )
    
    # Metadata
    change_type = models.CharField(
        max_length=20,
        choices=[
            ('add', 'Add'),
            ('modify', 'Modify'),
            ('remove', 'Remove'),
        ],
        default='modify',
        help_text="Type of change"
    )
    
    is_required = models.BooleanField(
        default=False,
        help_text="Whether this change is required for the submission"
    )
    
    order = models.IntegerField(
        default=0,
        help_text="Display order within submission"
    )
    
    class Meta:
        db_table = 'submission_items'
        ordering = ['submission', 'order', 'created']
        indexes = [
            models.Index(fields=['submission', 'status']),
            models.Index(fields=['status']),
        ]
        verbose_name = 'Submission Item'
        verbose_name_plural = 'Submission Items'
    
    def __str__(self):
        return f"{self.submission.title} - {self.field_label or self.field_name}"
    
    def approve(self, reviewer):
        """Approve this item"""
        self.status = 'approved'
        self.reviewed_by = reviewer
        self.reviewed_at = timezone.now()
        self.save(update_fields=['status', 'reviewed_by', 'reviewed_at', 'modified'])
    
    def reject(self, reviewer, reason=''):
        """Reject this item"""
        self.status = 'rejected'
        self.reviewed_by = reviewer
        self.reviewed_at = timezone.now()
        self.rejection_reason = reason
        self.save(update_fields=['status', 'reviewed_by', 'reviewed_at', 'rejection_reason', 'modified'])
    
    def get_display_value(self, value):
        """Get human-readable display value"""
        if value is None:
            return 'None'
        if isinstance(value, bool):
            return 'Yes' if value else 'No'
        if isinstance(value, (list, dict)):
            return str(value)
        return str(value)
    
    @property
    def old_value_display(self):
        """Human-readable old value"""
        return self.get_display_value(self.old_value)
    
    @property
    def new_value_display(self):
        """Human-readable new value"""
        return self.get_display_value(self.new_value)


class ModerationLock(BaseModel):
    """
    Lock record for submissions under review.
    
    Provides additional tracking beyond the ContentSubmission lock fields.
    Helps with monitoring and debugging lock issues.
    """
    
    submission = models.OneToOneField(
        ContentSubmission,
        on_delete=models.CASCADE,
        related_name='lock_record',
        help_text="Submission that is locked"
    )
    
    locked_by = models.ForeignKey(
        'users.User',
        on_delete=models.CASCADE,
        related_name='moderation_locks',
        help_text="User who holds the lock"
    )
    
    locked_at = models.DateTimeField(
        auto_now_add=True,
        help_text="When the lock was acquired"
    )
    
    expires_at = models.DateTimeField(
        help_text="When the lock expires"
    )
    
    is_active = models.BooleanField(
        default=True,
        db_index=True,
        help_text="Whether the lock is currently active"
    )
    
    released_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When the lock was released"
    )
    
    class Meta:
        db_table = 'moderation_locks'
        ordering = ['-locked_at']
        indexes = [
            models.Index(fields=['is_active', 'expires_at']),
            models.Index(fields=['locked_by', 'is_active']),
        ]
        verbose_name = 'Moderation Lock'
        verbose_name_plural = 'Moderation Locks'
    
    def __str__(self):
        return f"Lock on {self.submission.title} by {self.locked_by.email}"
    
    def is_expired(self):
        """Check if lock has expired"""
        return timezone.now() > self.expires_at
    
    def release(self):
        """Release the lock"""
        self.is_active = False
        self.released_at = timezone.now()
        self.save(update_fields=['is_active', 'released_at', 'modified'])
    
    def extend(self, minutes=15):
        """Extend the lock duration"""
        from datetime import timedelta
        self.expires_at = timezone.now() + timedelta(minutes=minutes)
        self.save(update_fields=['expires_at', 'modified'])
    
    @classmethod
    def cleanup_expired(cls):
        """Cleanup expired locks (for periodic task)"""
        expired_locks = cls.objects.filter(
            is_active=True,
            expires_at__lt=timezone.now()
        )
        
        count = 0
        for lock in expired_locks:
            # Release lock
            lock.release()
            
            # Unlock submission if still in reviewing state
            submission = lock.submission
            if submission.status == ContentSubmission.STATE_REVIEWING:
                submission.unlock()
                submission.save()
            
            count += 1
        
        return count
