"""
Core Models for Stream Processor Application

This module contains abstract base models and shared functionality
that is used across multiple applications in the Stream Processor project.
These models provide common fields and methods that promote consistency
and reduce code duplication.
"""

import uuid
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User


class TimestampedModel(models.Model):
    """
    Abstract base model that provides timestamp fields.
    
    This model adds created_at and updated_at fields to any model
    that inherits from it. These fields are automatically managed
    and provide audit trail capabilities.
    
    Attributes:
        created_at (DateTimeField): Timestamp when the record was created
        updated_at (DateTimeField): Timestamp when the record was last modified
    """
    
    # Automatically set when the record is created
    created_at = models.DateTimeField(
        auto_now_add=True,
        help_text="Timestamp when this record was created"
    )
    
    # Automatically updated whenever the record is saved
    updated_at = models.DateTimeField(
        auto_now=True,
        help_text="Timestamp when this record was last modified"
    )
    
    class Meta:
        # This is an abstract model - no database table will be created
        abstract = True
        
    def save(self, *args, **kwargs):
        """
        Override save method to ensure updated_at is always current.
        
        This method ensures that the updated_at field is always set
        to the current timestamp when a record is saved, regardless
        of whether it's a new record or an update.
        
        Args:
            *args: Variable length argument list for parent save method
            **kwargs: Arbitrary keyword arguments for parent save method
        """
        # Set updated_at to current time
        self.updated_at = timezone.now()
        # Call the parent save method
        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 the default
    auto-incrementing integer. UUIDs are useful for distributed systems
    and provide better security by making IDs unpredictable.
    
    Attributes:
        id (UUIDField): Primary key using UUID format
    """
    
    # UUID primary key field
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False,
        help_text="Unique identifier for this record"
    )
    
    class Meta:
        # This is an abstract model - no database table will be created
        abstract = True


class UserOwnedModel(models.Model):
    """
    Abstract base model for records that belong to a specific user.
    
    This model adds a foreign key relationship to the User model,
    allowing records to be associated with specific users. It also
    provides utility methods for checking ownership.
    
    Attributes:
        owner (ForeignKey): Reference to the User who owns this record
    """
    
    # Foreign key to Django's built-in User model
    owner = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='%(class)s_set',
        help_text="User who owns this record"
    )
    
    class Meta:
        # This is an abstract model - no database table will be created
        abstract = True
    
    def is_owned_by(self, user):
        """
        Check if this record is owned by the specified user.
        
        This method provides a convenient way to check ownership
        of a record, which is useful for permission checking.
        
        Args:
            user (User): The user to check ownership against
            
        Returns:
            bool: True if the user owns this record, False otherwise
        """
        return self.owner == user


class StatusModel(models.Model):
    """
    Abstract base model that provides status tracking.
    
    This model adds status field with common status choices
    that can be used across different types of records.
    
    Attributes:
        status (CharField): Current status of the record
    """
    
    # Status choices common across multiple models
    STATUS_CHOICES = [
        ('active', 'Active'),
        ('inactive', 'Inactive'),
        ('pending', 'Pending'),
        ('processing', 'Processing'),
        ('completed', 'Completed'),
        ('failed', 'Failed'),
        ('cancelled', 'Cancelled'),
    ]
    
    # Status field with predefined choices
    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='pending',
        db_index=True,
        help_text="Current status of this record"
    )
    
    class Meta:
        # This is an abstract model - no database table will be created
        abstract = True
    
    def is_active(self):
        """
        Check if this record has an active status.
        
        Returns:
            bool: True if status is 'active', False otherwise
        """
        return self.status == 'active'
    
    def is_processing(self):
        """
        Check if this record is currently being processed.
        
        Returns:
            bool: True if status is 'processing', False otherwise
        """
        return self.status == 'processing'
    
    def is_completed(self):
        """
        Check if this record has completed processing.
        
        Returns:
            bool: True if status is 'completed', False otherwise
        """
        return self.status == 'completed'
    
    def is_failed(self):
        """
        Check if this record has failed processing.
        
        Returns:
            bool: True if status is 'failed', False otherwise
        """
        return self.status == 'failed'


class ConfigurationModel(models.Model):
    """
    Abstract base model for configuration records.
    
    This model provides a structure for storing configuration
    key-value pairs with metadata about when they were created
    and last modified.
    
    Attributes:
        key (CharField): Configuration key identifier
        value (TextField): Configuration value (can store JSON)
        description (TextField): Human-readable description of the setting
        is_active (BooleanField): Whether this configuration is active
    """
    
    # Unique configuration key
    key = models.CharField(
        max_length=100,
        unique=True,
        db_index=True,
        help_text="Unique identifier for this configuration setting"
    )
    
    # Configuration value (can store various data types as strings)
    value = models.TextField(
        help_text="Configuration value (can be JSON for complex data)"
    )
    
    # Human-readable description
    description = models.TextField(
        blank=True,
        help_text="Description of what this configuration setting does"
    )
    
    # Whether this configuration is currently active
    is_active = models.BooleanField(
        default=True,
        db_index=True,
        help_text="Whether this configuration setting is active"
    )
    
    class Meta:
        # This is an abstract model - no database table will be created
        abstract = True
        # Default ordering by key name
        ordering = ['key']
    
    def __str__(self):
        """
        String representation of the configuration.
        
        Returns:
            str: Configuration key for easy identification
        """
        return self.key
    
    @classmethod
    def get_config_value(cls, key, default=None):
        """
        Get configuration value by key with optional default.
        
        This class method provides a convenient way to retrieve
        configuration values with fallback to default values.
        
        Args:
            key (str): Configuration key to look up
            default: Default value if key is not found
            
        Returns:
            str: Configuration value or default if not found
        """
        try:
            config = cls.objects.get(key=key, is_active=True)
            return config.value
        except cls.DoesNotExist:
            return default
