"""Core Models

This module contains base models and common functionality used across
the entire Adtlas project. These models provide shared fields, methods,
and behaviors that other apps can inherit from.
"""

import uuid
from django.db import models
from django.urls import reverse
from django.utils import timezone
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from django.core.validators import MinValueValidator, MaxValueValidator


User = get_user_model()


class TimeStampedModel(models.Model):
    """Abstract base model that provides timestamp fields.
    
    This model provides created_at and updated_at fields that are
    automatically managed. All models should inherit from this.
    """
    
    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
        ordering = ['-created_at']
    
    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.
    """
    
    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.
    """
    
    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(
        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 delete(self, using=None, keep_parents=False, hard_delete=False):
        """Override delete to implement soft delete."""
        if hard_delete:
            # Perform actual deletion
            super().delete(using=using, keep_parents=keep_parents)
        else:
            # Soft delete
            self.is_deleted = True
            self.deleted_at = timezone.now()
            self.save(update_fields=['is_deleted', 'deleted_at'])
    
    def restore(self):
        """Restore a soft-deleted record."""
        self.is_deleted = False
        self.deleted_at = None
        self.deleted_by = None
        self.save(update_fields=['is_deleted', 'deleted_at', 'deleted_by'])
    
    @classmethod
    def active_objects(cls):
        """Return a queryset of non-deleted records."""
        return cls.objects.filter(is_deleted=False)
    
    @classmethod
    def deleted_objects(cls):
        """Return a queryset of deleted records."""
        return cls.objects.filter(is_deleted=True)


class BaseModel(TimeStampedModel, UUIDModel, SoftDeleteModel):
    """Base model that combines all common functionality.
    
    This model provides:
    - UUID primary key
    - Timestamp fields (created_at, updated_at)
    - Soft delete functionality
    
    Most models in the project should inherit from this.
    """
    
    class Meta:
        abstract = True


class AuditModel(models.Model):
    """Abstract model that provides audit trail functionality.
    
    This model tracks who created and last modified a record.
    """
    
    created_by = models.ForeignKey(
        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(
        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 to track user changes."""
        # Note: The user should be set in the view or form
        # This is just a placeholder for the audit functionality
        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 FullBaseModel(BaseModel, AuditModel, StatusModel, 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


class Setting(BaseModel):
    """Model for storing application settings.
    
    This model provides a flexible way to store configuration
    settings that can be modified at runtime.
    """
    
    SETTING_TYPES = [
        ('string', _('String')),
        ('integer', _('Integer')),
        ('float', _('Float')),
        ('boolean', _('Boolean')),
        ('json', _('JSON')),
        ('text', _('Text')),
    ]
    
    key = models.CharField(
        _('Key'),
        max_length=100,
        unique=True,
        help_text=_('Unique key for this setting')
    )
    
    value = models.TextField(
        _('Value'),
        help_text=_('Value of the setting')
    )
    
    setting_type = models.CharField(
        _('Type'),
        max_length=20,
        choices=SETTING_TYPES,
        default='string',
        help_text=_('Data type of the setting value')
    )
    
    description = models.TextField(
        _('Description'),
        blank=True,
        help_text=_('Description of what this setting controls')
    )
    
    is_system = models.BooleanField(
        _('Is System Setting'),
        default=False,
        help_text=_('Whether this is a system setting that should not be modified by users')
    )
    
    class Meta:
        verbose_name = _('Setting')
        verbose_name_plural = _('Settings')
        ordering = ['key']
    
    def __str__(self):
        return f"{self.key}: {self.value}"
    
    def get_value(self):
        """Get the typed value of the setting."""
        if self.setting_type == 'integer':
            return int(self.value)
        elif self.setting_type == 'float':
            return float(self.value)
        elif self.setting_type == 'boolean':
            return self.value.lower() in ('true', '1', 'yes', 'on')
        elif self.setting_type == 'json':
            import json
            return json.loads(self.value)
        else:
            return self.value
    
    def set_value(self, value):
        """Set the value with proper type conversion."""
        if self.setting_type == 'json':
            import json
            self.value = json.dumps(value)
        else:
            self.value = str(value)
        self.save(update_fields=['value'])
    
    @classmethod
    def get_setting(cls, key, default=None):
        """Get a setting value by key."""
        try:
            setting = cls.objects.get(key=key)
            return setting.get_value()
        except cls.DoesNotExist:
            return default
    
    @classmethod
    def set_setting(cls, key, value, setting_type='string', description=''):
        """Set a setting value by key."""
        setting, created = cls.objects.get_or_create(
            key=key,
            defaults={
                'setting_type': setting_type,
                'description': description
            }
        )
        setting.set_value(value)
        return setting



