from django.db import models
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils import timezone
import os
import uuid


def validate_file_extension(value):
    """Validate that file extension is allowed"""
    ext = os.path.splitext(value.name)[1][1:].lower()
    if ext not in settings.ALLOWED_FILE_EXTENSIONS:
        raise ValidationError(
            f'Extensión de archivo no permitida. Extensiones permitidas: {", ".join(settings.ALLOWED_FILE_EXTENSIONS)}'
        )


class SoftDeleteQuerySet(models.QuerySet):
    def delete(self):
        return super().update(deleted_at=timezone.now())

    def hard_delete(self):
        return super().delete()

    def alive(self):
        return self.filter(deleted_at__isnull=True)

    def dead(self):
        return self.exclude(deleted_at__isnull=True)


class SoftDeleteManager(models.Manager):
    def get_queryset(self):
        return SoftDeleteQuerySet(self.model, using=self._db).alive()


class Folder(models.Model):
    """Folder model for organizing documents"""

    department = models.ForeignKey(
        'users.Department',
        on_delete=models.PROTECT,
        null=True, blank=True,
        related_name='folders',
        verbose_name='Departamento'
    )
    name = models.CharField(max_length=255, verbose_name='Nombre')
    description = models.TextField(blank=True, null=True, verbose_name='Descripción')
    parent = models.ForeignKey(
        'self',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='subfolders',
        verbose_name='Carpeta padre'
    )
    allowed_users = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='accessible_folders',
        blank=True,
        verbose_name='Usuarios con acceso'
    )
    is_public = models.BooleanField(default=False, verbose_name='Es pública')
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        related_name='created_folders',
        verbose_name='Creado por'
    )
    created_at = models.DateTimeField(auto_now_add=True, verbose_name='Fecha de creación')
    updated_at = models.DateTimeField(auto_now=True, verbose_name='Última modificación')
    updated_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        related_name='updated_folders',
        verbose_name='Modificado por'
    )

    # Soft delete
    deleted_at = models.DateTimeField(null=True, blank=True)
    objects = SoftDeleteManager()
    all_objects = SoftDeleteQuerySet.as_manager()

    class Meta:
        verbose_name = 'Carpeta'
        verbose_name_plural = 'Carpetas'
        ordering = ['name']
        unique_together = [['name', 'parent']]

    def __str__(self):
        return self.get_full_path()

    def get_full_path(self):
        if self.parent:
            return f"{self.parent.get_full_path()}/{self.name}"
        return self.name

    def user_has_access(self, user):
        if user.is_staff or user.is_superuser:
            return True
        elif self.is_public:
            return True
        elif self.created_by == user:
            return True
        elif self.allowed_users.filter(id=user.id).exists():
            return True
        elif self.allowed_users.exists():
            return False
        elif hasattr(user, 'department') and user.department == self.department:
            return True
        return False
        
    def get_all_subfolders(self):
        subfolders = list(self.subfolders.all())
        for subfolder in list(subfolders):
            subfolders.extend(subfolder.get_all_subfolders())
        return subfolders

    # Soft delete methods
    def delete(self, using=None, keep_parents=False):
        self.deleted_at = timezone.now()
        self.save(update_fields=["deleted_at"])

    def hard_delete(self, using=None, keep_parents=False):
        super().delete(using=using, keep_parents=keep_parents)

    def restore(self):
        self.deleted_at = None
        self.save(update_fields=["deleted_at"])


class DocumentShareLink(models.Model):
    token = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
    link_type = models.CharField(max_length=10)
    document = models.ForeignKey(
        'Document',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='share_links',
    )
    document_ids = models.JSONField(null=True, blank=True)
    zip_name = models.CharField(max_length=100, blank=True)
    expires_at = models.DateTimeField()
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        related_name='created_document_share_links',
    )
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-created_at']

    def __str__(self):
        return str(self.token)


def document_upload_path(instance, filename):
    if instance.folder:
        folder_path = instance.folder.get_full_path()
        return f'documents/{folder_path}/{filename}'
    return f'documents/{filename}'


class Document(models.Model):
    """Document model for file storage"""

    name = models.CharField(max_length=255, verbose_name='Nombre')
    description = models.TextField(blank=True, null=True, verbose_name='Descripción')
    file = models.FileField(
        upload_to=document_upload_path,
        max_length=500,
        validators=[validate_file_extension],
        verbose_name='Archivo'
    )
    folder = models.ForeignKey(
        Folder,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='documents',
        verbose_name='Carpeta'
    )
    file_size = models.BigIntegerField(default=0, verbose_name='Tamaño del archivo (bytes)')
    file_type = models.CharField(max_length=100, blank=True, verbose_name='Tipo de archivo')
    allowed_users = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='accessible_documents',
        blank=True,
        verbose_name='Usuarios con acceso'
    )
    is_public = models.BooleanField(default=False, verbose_name='Es público')
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        related_name='created_documents',
        verbose_name='Creado por'
    )
    created_at = models.DateTimeField(auto_now_add=True, verbose_name='Fecha de creación')
    updated_at = models.DateTimeField(auto_now=True, verbose_name='Última modificación')
    updated_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        related_name='updated_documents',
        verbose_name='Modificado por'
    )

    # Soft delete
    deleted_at = models.DateTimeField(null=True, blank=True)
    objects = SoftDeleteManager()
    all_objects = SoftDeleteQuerySet.as_manager()

    class Meta:
        verbose_name = 'Documento'
        verbose_name_plural = 'Documentos'
        ordering = ['-updated_at']

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if self.file:
            self.file_size = self.file.size
            self.file_type = os.path.splitext(self.file.name)[1][1:].lower()
        super().save(*args, **kwargs)

    def user_has_access(self, user):
        if user.is_staff or user.is_superuser:
            return True
        if self.is_public:
            return True
        if self.created_by == user:
            return True
        if self.allowed_users.filter(id=user.id).exists():
            return True
        if self.folder and self.folder.user_has_access(user):
            return True
        return False

    def get_file_info(self):
        return {
            'name': self.name,
            'size': self.file_size,
            'type': self.file_type,
            'url': self.file.url if self.file else None,
            'created_at': self.created_at,
            'updated_at': self.updated_at,
            'created_by': self.created_by.name if self.created_by else None,
            'updated_by': self.updated_by.name if self.updated_by else None,
        }

    # Soft delete methods
    def delete(self, using=None, keep_parents=False):
        self.deleted_at = timezone.now()
        self.save(update_fields=["deleted_at"])

    def hard_delete(self, using=None, keep_parents=False):
        super().delete(using=using, keep_parents=keep_parents)

    def restore(self):
        self.deleted_at = None
        self.save(update_fields=["deleted_at"])


class DocumentVersion(models.Model):
    """Model to track document versions"""

    document = models.ForeignKey(
        Document,
        on_delete=models.CASCADE,
        related_name='versions',
        verbose_name='Documento'
    )
    file = models.FileField(upload_to='document_versions/', max_length=500, verbose_name='Archivo')
    version_number = models.IntegerField(verbose_name='Número de versión')
    comment = models.TextField(blank=True, null=True, verbose_name='Comentario')
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        verbose_name='Creado por'
    )
    created_at = models.DateTimeField(auto_now_add=True, verbose_name='Fecha de creación')

    # Soft delete
    deleted_at = models.DateTimeField(null=True, blank=True)
    objects = SoftDeleteManager()
    all_objects = SoftDeleteQuerySet.as_manager()

    class Meta:
        verbose_name = 'Versión de documento'
        verbose_name_plural = 'Versiones de documentos'
        ordering = ['-version_number']
        unique_together = [['document', 'version_number']]

    def __str__(self):
        return f"{self.document.name} - v{self.version_number}"

    # Soft delete methods
    def delete(self, using=None, keep_parents=False):
        self.deleted_at = timezone.now()
        self.save(update_fields=["deleted_at"])

    def hard_delete(self, using=None, keep_parents=False):
        super().delete(using=using, keep_parents=keep_parents)

    def restore(self):
        self.deleted_at = None
        self.save(update_fields=["deleted_at"])
