# -*- coding: utf-8 -*-
"""
Common Models Module

This module contains base models and common functionality
that will be used across all applications in the project.

"""

import uuid
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _ 


class TimeStampedModel(models.Model):
    """Abstract base model that provides timestamp fields only.
    
    This model provides created_at and updated_at fields that are
    automatically managed. All models should inherit from this.
    
    This is a lighter version of BaseModel for cases where
    you only need timestamp tracking without user tracking
    or soft delete functionality.
    """
    
    # Timestamp fields for tracking creation and modification
    created_at = models.DateTimeField(
        _('Created At'),
        auto_now_add=True,
        help_text=_('Date and time when the record was created')
    )
    
    updated_at = models.DateTimeField(
        _('Updated At'),
        auto_now=True,
        help_text=_('Date and time when the record was last updated')
    )
    
    class Meta:
        abstract = True  # This makes it an abstract base class
        ordering = ['-created_at']  # Default ordering by creation date
    
    def save(self, *args, **kwargs):
        """Override save to add custom logic."""
        # Update the updated_at field manually if needed
        if not self._state.adding:
            self.updated_at = timezone.now()
        
        super().save(*args, **kwargs)


class UUIDModel(models.Model):
    """Abstract base model that provides UUID primary key.
    
    This model uses UUID as the primary key instead of auto-incrementing
    integers for better security and distributed systems compatibility.
    """
    
    # Primary key using UUID for better security and uniqueness
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False,
        help_text=_('Unique identifier for this record')
    )
    
    class Meta:
        abstract = True


class SoftDeleteModel(models.Model):
    """Abstract base model that provides soft delete functionality.
    
    Instead of actually deleting records, this model marks them as deleted
    and provides methods to filter out deleted records.
    """
    
    # Soft delete functionality
    is_deleted = models.BooleanField(
        _('Is Deleted'),
        default=False,
        help_text=_('Whether this record has been soft deleted')
    )
    
    deleted_at = models.DateTimeField(
        _('Deleted At'),
        null=True,
        blank=True,
        help_text=_('Date and time when the record was deleted')
    )
    
    deleted_by = models.ForeignKey(
        'accounts.User',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='%(class)s_deleted_records',
        help_text=_('User who deleted this record')
    )
    
    class Meta:
        abstract = True
    
    def hard_delete(self, using=None, keep_parents=False):
        """Override delete to implement soft delete."""
        # Perform actual deletion
        super().delete(using=using, keep_parents=keep_parents) 

    def soft_delete(self, user=None):
        """
        Perform a soft delete on this record.
        
        Args:
            user: The user performing the delete operation
        """
        self.is_deleted = True
        self.deleted_at = timezone.now()
        if user:
            self.deleted_by = user
        self.save(update_fields=['is_deleted', 'deleted_at', 'deleted_by'])
    
    def restore(self, user=None):
        """
        Restore a soft deleted record.
        
        Args:
            user: The user performing the restore operation
        """
        self.is_deleted = False
        self.deleted_at = None
        self.deleted_by = None
        if user:
            self.updated_by = user
        self.save(update_fields=['is_deleted', 'deleted_at', 'deleted_by', 'updated_by'])

    @classmethod
    def get_active_objects(cls):
        """
        Get a queryset of non-deleted objects.
        
        Returns:
            QuerySet: All objects where is_deleted=False
        """
        return cls.objects.filter(is_deleted=False)
    
    @classmethod
    def get_deleted_objects(cls):
        """
        Get a queryset of soft deleted objects.
        
        Returns:
            QuerySet: All objects where is_deleted=True
        """
        return cls.objects.filter(is_deleted=True)


class SlugModel(models.Model):
    """
    Abstract model that provides slug functionality.
    
    This model adds a slug field that can be used for SEO-friendly URLs.
    The slug is automatically generated from a specified field.
    """
    
    # Slug field for SEO-friendly URLs
    slug = models.SlugField(
        max_length=255,
        unique=True,
        verbose_name=_("Slug"),
        help_text=_("SEO-friendly URL identifier")
    )

    class Meta:
        abstract = True  # This makes it an abstract base class
    
    def save(self, *args, **kwargs):
        """
        Override save method to auto-generate slug if not provided.
        
        Child models should define a get_slug_source() method
        that returns the field value to generate the slug from.
        """
        if not self.slug and hasattr(self, 'get_slug_source'):
            from django.utils.text import slugify
            self.slug = slugify(self.get_slug_source())
        
        super().save(*args, **kwargs)
    
    def get_slug_source(self):
        """
        Get the source value for slug generation.
        
        This method should be overridden in child models
        to specify which field should be used for slug generation.
        
        Returns:
            str: The value to generate slug from
        """
        return str(self.pk)


class SortableModel(models.Model):
    """
    Abstract model that provides sorting functionality.
    
    This model adds an order field that can be used to sort records
    in a specific order defined by the user.
    """
    
    # Order field for custom sorting
    order = models.PositiveIntegerField(
        default=0,
        verbose_name=_("Order"),
        help_text=_("Order for sorting (lower numbers appear first)")
    )

    class Meta:
        abstract = True  # This makes it an abstract base class
        ordering = ['order', 'id']  # Default ordering by order field
    
    def move_up(self):
        """
        Move this item up in the sort order.
        
        Swaps the order with the item that has the next lower order value.
        """
        try:
            previous_item = self.__class__.objects.filter(
                order__lt=self.order
            ).order_by('-order').first()
            
            if previous_item:
                # Swap order values
                previous_order = previous_item.order
                previous_item.order = self.order
                self.order = previous_order
                
                # Save both items
                previous_item.save(update_fields=['order'])
                self.save(update_fields=['order'])
        except Exception:
            pass  # Silently fail if something goes wrong
    
    def move_down(self):
        """
        Move this item down in the sort order.
        
        Swaps the order with the item that has the next higher order value.
        """
        try:
            next_item = self.__class__.objects.filter(
                order__gt=self.order
            ).order_by('order').first()
            
            if next_item:
                # Swap order values
                next_order = next_item.order
                next_item.order = self.order
                self.order = next_order
                
                # Save both items
                next_item.save(update_fields=['order'])
                self.save(update_fields=['order'])
        except Exception:
            pass  # Silently fail if something goes wrong


class AuditModel(models.Model):
    """Abstract model that provides audit trail functionality.
    
    This model tracks who created and last modified a record.
    """
    
    # User tracking fields - who created and last updated the record
    created_by = models.ForeignKey(
        'accounts.User',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='%(class)s_created_records',
        help_text=_('User who created this record')
    )
    
    updated_by = models.ForeignKey(
        'accounts.User',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='%(class)s_updated_records',
        help_text=_('User who last updated this record')
    )
    
    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        """
        Override save method to handle user tracking.
        
        This method automatically sets created_by and updated_by
        based on the current user in the request context.
        """
        # Get the current user from the request context if available
        user = getattr(self, '_current_user', None)
        
        # Set created_by for new records
        if not self.pk and user:
            self.created_by = user
        
        # Always set updated_by for any save operation
        if user:
            self.updated_by = user
        
        super().save(*args, **kwargs)


class StatusModel(models.Model):
    """Abstract model that provides status functionality.
    
    This model provides common status fields used across the application.
    """
    
    STATUS_CHOICES = [
        ('active', _('Active')),
        ('inactive', _('Inactive')),
        ('pending', _('Pending')),
        ('suspended', _('Suspended')),
        ('archived', _('Archived')),
    ]
    
    status = models.CharField(
        _('Status'),
        max_length=20,
        choices=STATUS_CHOICES,
        default='active',
        help_text=_('Current status of this record')
    )
    
    is_active = models.BooleanField(
        _('Is Active'),
        default=True,
        help_text=_('Whether this record is active')
    )
    
    class Meta:
        abstract = True
    
    def activate(self):
        """Activate this record."""
        self.status = 'active'
        self.is_active = True
        self.save(update_fields=['status', 'is_active'])
    
    def deactivate(self):
        """Deactivate this record."""
        self.status = 'inactive'
        self.is_active = False
        self.save(update_fields=['status', 'is_active'])
    
    def suspend(self):
        """Suspend this record."""
        self.status = 'suspended'
        self.is_active = False
        self.save(update_fields=['status', 'is_active'])
    
    def archive(self):
        """Archive this record."""
        self.status = 'archived'
        self.is_active = False
        self.save(update_fields=['status', 'is_active'])


class MetaDataModel(models.Model):
    """Abstract model that provides metadata functionality.
    
    This model provides fields for storing additional metadata
    as JSON data.
    """
    
    metadata = models.JSONField(
        _('Metadata'),
        default=dict,
        blank=True,
        help_text=_('Additional metadata stored as JSON')
    )
    
    tags = models.JSONField(
        _('Tags'),
        default=list,
        blank=True,
        help_text=_('Tags associated with this record')
    )
    
    class Meta:
        abstract = True
    
    def add_tag(self, tag):
        """Add a tag to this record."""
        if tag not in self.tags:
            self.tags.append(tag)
            self.save(update_fields=['tags'])
    
    def remove_tag(self, tag):
        """Remove a tag from this record."""
        if tag in self.tags:
            self.tags.remove(tag)
            self.save(update_fields=['tags'])
    
    def set_metadata(self, key, value):
        """Set a metadata value."""
        self.metadata[key] = value
        self.save(update_fields=['metadata'])
    
    def get_metadata(self, key, default=None):
        """Get a metadata value."""
        return self.metadata.get(key, default)


class BaseModel(TimeStampedModel, UUIDModel, SoftDeleteModel, AuditModel):
    """
    Abstract base model that provides common fields for all models.
    
    This model includes:    
    - UUID :: UUID primary key for better security
    - Timestamps :: Created and updated timestamps
    - Soft delete :: Soft delete functionality
    - Audit trail :: Created and updated by user tracking 
    - Common methods for all models 
    """  
    
    
    class Meta:
        abstract = True  # This makes it an abstract base class s

    def __str__(self):
        """Default string representation using the model name and ID."""
        return f"{self.__class__.__name__} ({self.id})"
    
    def get_absolute_url(self):
        """
        Get the absolute URL for this model instance.
        
        This method should be overridden in child models to provide
        the correct URL for the model's detail view.
        """
        from django.urls import reverse
        model_name = self.__class__.__name__.lower()
        try:
            return reverse(f'{model_name}-detail', kwargs={'pk': self.pk})
        except:
            return '#'  # Fallback if URL pattern doesn't exist


class FullBaseModel(BaseModel, SlugModel, StatusModel, SortableModel, MetaDataModel):
    """Complete base model with all functionality.
    
    This model combines all available base functionality:
    - UUID primary key
    - Timestamps
    - Soft delete
    - Audit trail
    - Status management
    - Metadata storage
    
    Use this for models that need comprehensive functionality.
    """
    
    class Meta:
        abstract = True

