from drf_spectacular.utils import (
    extend_schema, extend_schema_view, OpenApiParameter, OpenApiResponse
)
from drf_spectacular.types import OpenApiTypes
from rest_framework import viewsets, status, filters
from django.http import HttpResponse
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny
from django.http import FileResponse, Http404
from django.db.models import Q
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.utils import timezone
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.templatetags.static import static
from django.contrib.staticfiles import finders
from email.mime.image import MIMEImage
from .models import Folder, Document, DocumentVersion
from .serializers import (
    FolderSerializer, FolderListSerializer,
    DocumentSerializer, DocumentListSerializer,
    DocumentVersionSerializer, DocumentEmailSerializer,
    DocumentBulkDownloadSerializer,
    DocumentShareLinkSerializer,
    DocumentBulkShareLinkSerializer,
)
from .permissions import FolderPermissions, DocumentPermissions, CanDownload
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
from datetime import timedelta
import traceback, io, os, zipfile


@extend_schema_view(
    list=extend_schema(
        summary="Listar carpetas",
        description="Devuelve todas las carpetas accesibles al usuario autenticado.",
        responses=FolderListSerializer,
    ),
    retrieve=extend_schema(
        summary="Obtener carpeta",
        description="Devuelve el detalle de una carpeta específica.",
        responses=FolderSerializer,
    ),
    create=extend_schema(
        summary="Crear carpeta",
        description="Crea una nueva carpeta asignando automáticamente el usuario como creador.",
        request=FolderSerializer,
        responses=FolderSerializer,
    ),
    update=extend_schema(
        summary="Actualizar carpeta",
        description="Actualiza los datos de una carpeta existente.",
        request=FolderSerializer,
        responses=FolderSerializer,
    ),
    destroy=extend_schema(
        summary="Eliminar carpeta (soft delete)",
        description="Marca la carpeta como eliminada (soft delete).",
        responses={204: OpenApiResponse(description="Carpeta eliminada exitosamente")},
    ),
)


class FolderViewSet(viewsets.ModelViewSet):
    """API para gestionar carpetas con soporte de soft delete y control de acceso."""

    queryset = Folder.objects.all()
    serializer_class = FolderSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    search_fields = ['name', 'description', 'department_id']
    ordering_fields = ['name', 'created_at', 'updated_at']
    ordering = ['name']

    def get_serializer_class(self):
        if self.action == 'list':
            return FolderListSerializer
        return FolderSerializer

    def get_queryset(self):
        """Filtra carpetas según permisos del usuario."""
        user = self.request.user
        if user.is_staff or user.is_superuser:
            return Folder.objects.all()
        return Folder.objects.filter(
            Q(is_public=True) |
            Q(created_by=user) |
            Q(allowed_users=user) |
            (Q(allowed_users__isnull=True) & Q(department=user.department))
        ).distinct()

    def perform_destroy(self, instance):
        """Soft delete en lugar de eliminación física."""
        instance.delete()

    @extend_schema(
        summary="Restaurar carpeta",
        description="Restaura una carpeta previamente eliminada (soft delete).",
        responses={200: OpenApiResponse(description="Carpeta restaurada exitosamente")},
    )
    @action(detail=True, methods=['post'])
    def restore(self, request, pk=None):
        folder = self.get_object()
        if folder.deleted_at is None:
            return Response({'message': 'La carpeta no está eliminada'}, status=status.HTTP_400_BAD_REQUEST)
        folder.restore()
        return Response({'message': 'Carpeta restaurada exitosamente'})

    @extend_schema(
        summary="Listar carpetas eliminadas",
        description="Devuelve todas las carpetas que fueron eliminadas (soft delete).",
        responses=FolderListSerializer,
    )
    @extend_schema(
        summary="Listar carpetas eliminadas",
        description="Devuelve todas las carpetas que fueron eliminadas (soft delete).",
        responses={200: FolderListSerializer},   # 👈 aquí va la CLASE del serializer
    )
    @action(detail=False, methods=['get'])

    @extend_schema(
        summary="Obtener contenidos de carpeta",
        description="Devuelve subcarpetas y documentos dentro de una carpeta, filtrados por permisos.",
        responses={
            200: OpenApiResponse(
                response={
                    'folder': FolderSerializer,
                    'subfolders': FolderListSerializer,
                    'documents': DocumentListSerializer,
                }
            )
        },
    )
    @action(detail=True, methods=['get'])
    def contents(self, request, pk=None):
        folder = self.get_object()
        subfolders = folder.subfolders.all()
        if not (request.user.is_staff or request.user.is_superuser):
            subfolders = subfolders.filter(
                Q(is_public=True) |
                Q(created_by=request.user) |
                Q(allowed_users=request.user) |
                (Q(allowed_users__isnull=True) & Q(department=request.user.department_id))
            ).distinct()

        documents = folder.documents.all()
        if not (request.user.is_staff or request.user.is_superuser):
            documents = documents.filter(
                Q(is_public=True) |
                Q(created_by=request.user) |
                Q(allowed_users=request.user) |
                (Q(allowed_users__isnull=True) & Q(folder__department_id=request.user.department_id))
            ).distinct()

        return Response({
            'folder': FolderSerializer(folder, context={'request': request}).data,
            'subfolders': FolderListSerializer(subfolders, many=True, context={'request': request}).data,
            'documents': DocumentListSerializer(documents, many=True, context={'request': request}).data,
        })

    @extend_schema(
        summary="Agregar acceso de usuario",
        description="Agrega un usuario a la lista de permitidos para acceder a la carpeta.",
        parameters=[OpenApiParameter("user_id", int, OpenApiParameter.QUERY, description="ID del usuario a agregar")],
        responses={200: OpenApiResponse(description="Usuario agregado exitosamente")},
    )
    @action(detail=True, methods=['post'])
    def add_user_access(self, request, pk=None):
        folder = self.get_object()
        if (
            not request.user.can_edit_folders
            and not request.user.is_staff
            and folder.created_by != request.user
        ):
            return Response({'error': 'No tienes permisos para editar carpetas'}, status=status.HTTP_403_FORBIDDEN)

        user_id = request.data.get('user_id')
        if not user_id:
            return Response({'error': 'user_id es requerido'}, status=status.HTTP_400_BAD_REQUEST)

        from users.models import User
        try:
            user = User.objects.get(id=user_id)
            folder.allowed_users.add(user)
            return Response({'message': f'Usuario {user.name} agregado exitosamente'})
        except User.DoesNotExist:
            return Response({'error': 'Usuario no encontrado'}, status=status.HTTP_404_NOT_FOUND)

    @extend_schema(
        summary="Remover acceso de usuario",
        description="Remueve un usuario de la lista de permitidos para acceder a la carpeta.",
        parameters=[OpenApiParameter("user_id", int, OpenApiParameter.QUERY, description="ID del usuario a remover")],
        responses={200: OpenApiResponse(description="Usuario removido exitosamente")},
    )
    @action(detail=True, methods=['post'])
    def remove_user_access(self, request, pk=None):
        folder = self.get_object()
        if not request.user.can_edit_folders and not request.user.is_staff:
            return Response({'error': 'No tienes permisos para editar carpetas'}, status=status.HTTP_403_FORBIDDEN)

        user_id = request.data.get('user_id')
        if not user_id:
            return Response({'error': 'user_id es requerido'}, status=status.HTTP_400_BAD_REQUEST)

        from users.models import User
        try:
            user = User.objects.get(id=user_id)
            folder.allowed_users.remove(user)
            return Response({'message': f'Usuario {user.name} removido exitosamente'})
        except User.DoesNotExist:
            return Response({'error': 'Usuario no encontrado'}, status=status.HTTP_404_NOT_FOUND)


class DocumentViewSet(viewsets.ModelViewSet):
    """ViewSet for Document model with soft delete"""

    parser_classes = [MultiPartParser, FormParser, JSONParser]
    queryset = Document.objects.all()
    serializer_class = DocumentSerializer
    permission_classes = [DocumentPermissions]
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    search_fields = ['name', 'description', 'file_type']
    ordering_fields = ['name', 'created_at', 'updated_at', 'file_size']
    ordering = ['-updated_at']

    def get_serializer_class(self):
        if self.action == 'list':
            return DocumentListSerializer
        return DocumentSerializer

    def get_queryset(self):
        user = self.request.user

        if user.is_staff or user.is_superuser:
            queryset = Document.objects.all()
        else:
            queryset = Document.objects.filter(
                Q(is_public=True) |
                Q(created_by=user) |
                Q(allowed_users=user) |
                Q(folder__is_public=True) |
                Q(folder__created_by=user) |
                Q(folder__allowed_users=user) |
                (Q(allowed_users__isnull=True) & Q(folder__department=user.department))
            ).distinct()

        folder_id = self.request.query_params.get('folder', None)
        if folder_id:
            if folder_id in ['null', 'none']:
                queryset = queryset.filter(folder__isnull=True)
            else:
                queryset = queryset.filter(folder_id=folder_id)

        return queryset

    def perform_destroy(self, instance):
        """Override destroy to use soft delete"""
        instance.delete()

    @action(detail=True, methods=['post'])
    def restore(self, request, pk=None):
        """Restore a soft-deleted document"""
        document = self.get_object()
        if document.deleted_at is None:
            return Response({'message': 'El documento no está eliminado'}, status=status.HTTP_400_BAD_REQUEST)
        document.restore()
        return Response({'message': 'Documento restaurado exitosamente'})

    @action(detail=False, methods=['get'])
    def deleted(self, request):
        """List soft-deleted documents"""
        deleted_docs = Document.all_objects.dead()
        serializer = DocumentListSerializer(deleted_docs, many=True, context={'request': request})
        return Response(serializer.data)

    @action(detail=True, methods=['get'], permission_classes=[CanDownload])
    def download(self, request, pk=None):
        document = self.get_object()
        if not document.file:
            raise Http404("Archivo no encontrado")
        response = FileResponse(document.file.open('rb'), as_attachment=True)
        response['Content-Disposition'] = f'attachment; filename="{document.name}"'
        return response

    @action(detail=True, methods=['post'], permission_classes=[CanDownload])
    def create_share_link(self, request, pk=None):
        from .models import DocumentShareLink

        document = self.get_object()
        serializer = DocumentShareLinkSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        expires_in_minutes = serializer.validated_data['expires_in_minutes']
        expires_at = timezone.now() + timedelta(minutes=expires_in_minutes)

        share_link = DocumentShareLink.objects.create(
            link_type='single',
            document=document,
            expires_at=expires_at,
            created_by=request.user,
        )

        url = request.build_absolute_uri(
            reverse('document-share-download', kwargs={'token': str(share_link.token)})
        )
        return Response(
            {
                'token': str(share_link.token),
                'expires_at': share_link.expires_at,
                'url': url,
            },
            status=status.HTTP_201_CREATED,
        )

    @action(detail=False, methods=['post'], permission_classes=[CanDownload])
    def create_share_link_bulk(self, request):
        from .models import DocumentShareLink

        serializer = DocumentBulkShareLinkSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        document_ids = serializer.validated_data['document_ids']
        zip_name = serializer.validated_data.get('zip_name', 'documentos')
        expires_in_minutes = serializer.validated_data['expires_in_minutes']
        expires_at = timezone.now() + timedelta(minutes=expires_in_minutes)

        base_queryset = self.get_queryset()
        accessible_docs = base_queryset.filter(id__in=document_ids)
        accessible_ids = set(accessible_docs.values_list('id', flat=True))
        requested_ids = set(document_ids)

        if not accessible_docs.exists():
            return Response(
                {'error': 'No tienes acceso a ninguno de los documentos solicitados'},
                status=status.HTTP_403_FORBIDDEN,
            )

        unauthorized_ids = requested_ids - accessible_ids
        if unauthorized_ids:
            return Response(
                {
                    'error': 'No tienes acceso a algunos documentos',
                    'unauthorized_documents': list(unauthorized_ids),
                },
                status=status.HTTP_403_FORBIDDEN,
            )

        docs_without_files = accessible_docs.filter(file__isnull=True) | accessible_docs.filter(file='')
        if docs_without_files.exists():
            missing_ids = list(docs_without_files.values_list('id', flat=True))
            return Response(
                {
                    'error': 'Algunos documentos no tienen archivos adjuntos',
                    'documents_without_files': missing_ids,
                },
                status=status.HTTP_400_BAD_REQUEST,
            )

        share_link = DocumentShareLink.objects.create(
            link_type='bulk',
            document_ids=document_ids,
            zip_name=zip_name,
            expires_at=expires_at,
            created_by=request.user,
        )

        url = request.build_absolute_uri(
            reverse('document-share-download-bulk', kwargs={'token': str(share_link.token)})
        )
        return Response(
            {
                'token': str(share_link.token),
                'expires_at': share_link.expires_at,
                'url': url,
            },
            status=status.HTTP_201_CREATED,
        )

    @action(
        detail=False,
        methods=['get'],
        url_path=r'share/(?P<token>[^/.]+)',
        permission_classes=[AllowAny],
        authentication_classes=[],
    )
    def share_download(self, request, token=None):
        from .models import DocumentShareLink

        share_link = get_object_or_404(DocumentShareLink, token=token, link_type='single')
        if share_link.expires_at <= timezone.now():
            return Response({'error': 'Enlace expirado'}, status=status.HTTP_410_GONE)

        document = share_link.document
        if not document or not document.file:
            raise Http404("Archivo no encontrado")

        response = FileResponse(document.file.open('rb'), as_attachment=True)
        response['Content-Disposition'] = f'attachment; filename="{document.name}"'
        return response

    @action(
        detail=False,
        methods=['get'],
        url_path=r'share/bulk/(?P<token>[^/.]+)',
        permission_classes=[AllowAny],
        authentication_classes=[],
    )
    def share_download_bulk(self, request, token=None):
        from .models import DocumentShareLink

        share_link = get_object_or_404(DocumentShareLink, token=token, link_type='bulk')
        if share_link.expires_at <= timezone.now():
            return Response({'error': 'Enlace expirado'}, status=status.HTTP_410_GONE)

        document_ids = share_link.document_ids or []
        if not document_ids:
            return Response({'error': 'Enlace inválido'}, status=status.HTTP_400_BAD_REQUEST)

        accessible_docs = Document.objects.filter(id__in=document_ids)
        accessible_ids = set(accessible_docs.values_list('id', flat=True))
        requested_ids = set(document_ids)
        missing_ids = requested_ids - accessible_ids
        if missing_ids:
            return Response(
                {
                    'error': 'Algunos documentos no fueron encontrados',
                    'not_found': list(missing_ids),
                },
                status=status.HTTP_404_NOT_FOUND,
            )

        docs_without_files = accessible_docs.filter(file__isnull=True) | accessible_docs.filter(file='')
        if docs_without_files.exists():
            missing_file_ids = list(docs_without_files.values_list('id', flat=True))
            return Response(
                {
                    'error': 'Algunos documentos no tienen archivos adjuntos',
                    'documents_without_files': missing_file_ids,
                },
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            zip_buffer = io.BytesIO()

            with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
                for document in accessible_docs:
                    try:
                        original_filename = os.path.basename(document.file.name)
                        file_extension = os.path.splitext(original_filename)[1]

                        safe_name = f"{document.id}_{document.name}"
                        safe_name = ''.join(c for c in safe_name if c.isalnum() or c in (' ', '.', '_')).rstrip()
                        safe_name += file_extension

                        with document.file.open('rb') as file:
                            file_content = file.read()
                            zip_file.writestr(safe_name, file_content)
                    except Exception as e:
                        print(f"Error procesando documento {document.id}: {str(e)}")
                        continue

            zip_buffer.seek(0, 2)
            file_size = zip_buffer.tell()
            zip_buffer.seek(0)

            zip_name = share_link.zip_name or 'documentos'
            response = HttpResponse(zip_buffer.getvalue(), content_type='application/zip')
            response['Content-Disposition'] = f'attachment; filename="{zip_name}.zip"'
            response['Content-Length'] = file_size
            response['X-Accel-Buffering'] = 'no'
            response['Cache-Control'] = 'no-cache'
            return response
        except Exception as e:
            return Response(
                {
                    'error': f'Error al crear el archivo ZIP: {str(e)}',
                    'details': str(e),
                    'traceback': traceback.format_exc(),
                },
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )

    @action(detail=True, methods=['post'])
    def add_user_access(self, request, pk=None):
        document = self.get_object()
        if not request.user.can_edit_documents and not request.user.is_staff:
            return Response({'error': 'No tienes permisos para editar documentos'}, status=status.HTTP_403_FORBIDDEN)

        user_id = request.data.get('user_id')
        if not user_id:
            return Response({'error': 'user_id es requerido'}, status=status.HTTP_400_BAD_REQUEST)

        from users.models import User
        try:
            user = User.objects.get(id=user_id)
            document.allowed_users.add(user)
            return Response({'message': f'Usuario {user.name} agregado exitosamente'})
        except User.DoesNotExist:
            return Response({'error': 'Usuario no encontrado'}, status=status.HTTP_404_NOT_FOUND)

    @action(detail=True, methods=['post'])
    def remove_user_access(self, request, pk=None):
        document = self.get_object()
        if not request.user.can_edit_documents and not request.user.is_staff:
            return Response({'error': 'No tienes permisos para editar documentos'}, status=status.HTTP_403_FORBIDDEN)

        user_id = request.data.get('user_id')
        if not user_id:
            return Response({'error': 'user_id es requerido'}, status=status.HTTP_400_BAD_REQUEST)

        from users.models import User
        try:
            user = User.objects.get(id=user_id)
            document.allowed_users.remove(user)
            return Response({'message': f'Usuario {user.name} removido exitosamente'})
        except User.DoesNotExist:
            return Response({'error': 'Usuario no encontrado'}, status=status.HTTP_404_NOT_FOUND)

    @action(detail=True, methods=['post'])
    def copy(self, request, pk=None):
        """Copy a document to a new location or with a new name"""
        document = self.get_object()
        
        if not request.user.can_add_documents and not request.user.is_staff:
            return Response(
                {'error': 'No tienes permisos para crear documentos'}, 
                status=status.HTTP_403_FORBIDDEN
            )

        new_name = request.data.get('name', f"{document.name} (Copia)")
        folder_id = request.data.get('folder_id')
        
        try:
            new_document = Document.objects.create(
                name=new_name,
                description=document.description,
                file=document.file,
                file_type=document.file_type,
                file_size=document.file_size,
                is_public=document.is_public,
                folder_id=folder_id,
                created_by=request.user
            )
            
            new_document.allowed_users.set(document.allowed_users.all())
            
            serializer = DocumentSerializer(new_document, context={'request': request})
            return Response(serializer.data, status=status.HTTP_201_CREATED)
            
        except Exception as e:
            return Response(
                {'error': f'Error al copiar el documento: {str(e)}'}, 
                status=status.HTTP_400_BAD_REQUEST
            )

    @action(detail=True, methods=['post'])
    def move(self, request, pk=None):
        """Move a document to a different folder"""
        document = self.get_object()
        
        if not request.user.can_edit_documents and not request.user.is_staff:
            return Response(
                {'error': 'No tienes permisos para editar documentos'}, 
                status=status.HTTP_403_FORBIDDEN
            )

        folder_id = request.data.get('folder_id')
        
        if folder_id and str(folder_id) == str(document.folder_id):
            return Response(
                {'error': 'El documento ya se encuentra en esta carpeta'}, 
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            document.folder_id = folder_id  # Puede ser None para mover a raíz
            document.save()
            
            serializer = DocumentSerializer(document, context={'request': request})
            return Response(serializer.data)
            
        except Exception as e:
            return Response(
                {'error': f'Error al mover el documento: {str(e)}'}, 
                status=status.HTTP_400_BAD_REQUEST
            )

    @extend_schema(
        request=DocumentEmailSerializer,
        responses={
            200: OpenApiResponse(
                description="Documento enviado exitosamente",
                examples={
                    "application/json": {
                        "message": "Documento enviado exitosamente a 2 destinatario(s)",
                        "recipients": ["usuario1@example.com", "usuario2@example.com"],
                        "subject": "Documento importante",
                        "document": "documento.pdf"
                    }
                }
            ),
            400: OpenApiResponse(
                description="Error en la validación de datos",
                examples={
                    "application/json": {
                        "emails": ["Este campo es requerido."],
                        "subject": ["Este campo no puede tener más de 200 caracteres."]
                    }
                }
            ),
            403: OpenApiResponse(
                description="Permisos insuficientes",
                examples={
                    "application/json": {
                        "error": "No tienes permisos para enviar documentos por correo"
                    }
                }
            ),
            404: OpenApiResponse(
                description="Documento no encontrado",
                examples={
                    "application/json": {
                        "error": "El documento no tiene archivo adjunto"
                    }
                }
            ),
        },
        description="Enviar el documento por correo electrónico a uno o más destinatarios",
        methods=["POST"]
    )
    
    @action(detail=True, methods=['post'])
    def send_by_email(self, request, pk=None):
        """
        Enviar el documento por correo electrónico
        """
        document = self.get_object()
        
        if not request.user.can_share_by_email and not request.user.is_staff:
            return Response(
                {'error': 'No tienes permisos para enviar documentos por correo'}, 
                status=status.HTTP_403_FORBIDDEN
            )

        if not document.file:
            return Response(
                {'error': 'El documento no tiene archivo adjunto'}, 
                status=status.HTTP_400_BAD_REQUEST
            )

        email_serializer = DocumentEmailSerializer(data=request.data)
        email_serializer.is_valid(raise_exception=True)
        
        validated_data = email_serializer.validated_data
        recipient_emails = validated_data['emails']
        subject = validated_data.get('subject')
        message = validated_data.get('message', '')
        
        if not subject:
            subject = f'Documento: {document.name}'

        try:
            from django.core.mail import EmailMultiAlternatives
            from django.conf import settings
            
            def format_file_size(size_bytes):
                if size_bytes < 1024:
                    return f"{size_bytes} bytes"
                elif size_bytes < 1024 * 1024:
                    return f"{size_bytes / 1024:.1f} KB"
                elif size_bytes < 1024 * 1024 * 1024:
                    return f"{size_bytes / (1024 * 1024):.1f} MB"
                else:
                    return f"{size_bytes / (1024 * 1024 * 1024):.1f} GB"
            
            logo_cid = 'email-logo'
            logo_url = request.build_absolute_uri(static('documents/email/logo.png'))
            logo_path = finders.find('documents/email/logo.png')
            if logo_path:
                logo_url = f'cid:{logo_cid}'

            email_context = {
                'subject': subject,
                'brand_name': 'Coinsamatik',
                'logo_url': logo_url,
                'message': message,
                'document_name': document.name,
                'document_description': document.description or 'Sin descripción',
                'document_type': document.file_type or 'No especificado',
                'document_size': format_file_size(document.file_size),
                'document_created_at': document.created_at.strftime('%d/%m/%Y %H:%M'),
                'sender_name': request.user.name,
                'sender_email': request.user.email,
                'current_year': timezone.now().year,
            }

            html_body = render_to_string('documents/email/document_share.html', email_context)
            text_body = strip_tags(html_body)

            email = EmailMultiAlternatives(
                subject=subject,
                body=text_body.strip(),
                from_email=settings.DEFAULT_FROM_EMAIL,
                to=recipient_emails,
            )
            email.attach_alternative(html_body, 'text/html')

            if logo_path:
                with open(logo_path, 'rb') as f:
                    logo_image = MIMEImage(f.read(), _subtype='png')
                logo_image.add_header('Content-ID', f'<{logo_cid}>')
                logo_image.add_header('Content-Disposition', 'inline', filename='logo.png')
                email.attach(logo_image)
            
            document.file.seek(0)
            file_content = document.file.read()
            
            mimetype = document.file_type
            if not mimetype or '/' not in mimetype:
                mimetype = 'application/octet-stream'
            
            email.attach(
                filename=f"{document.name}.{document.file_type}",
                content=file_content,
                mimetype=mimetype
            )
            
            email.send()
            
            return Response({
                'message': f'Documento enviado exitosamente a {len(recipient_emails)} destinatario(s)',
                'recipients': recipient_emails,
                'subject': subject,
                'document': document.name
            }, status=status.HTTP_200_OK)
            
        except Exception as e:
            import traceback
            
            error_full = traceback.format_exc()
            
            print("=" * 80)
            print("ERROR COMPLETO AL ENVIAR CORREO:")
            print("=" * 80)
            print(error_full)
            print("=" * 80)
            
            return Response(
                {
                    'error': f'Error al enviar el correo: {str(e)}',
                    'details': error_full,
                    'type': type(e).__name__
                }, 
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    @extend_schema(
        request=DocumentBulkDownloadSerializer,
        responses={
            200: OpenApiResponse(
                response=OpenApiTypes.BINARY,
                description="Archivo ZIP generado exitosamente"
            ),
            400: OpenApiResponse(
                description="Error en la validación de datos",
                examples={
                    "application/json": {
                        "document_ids": ["Este campo es requerido."],
                        "document_ids": ["Debe proporcionar al menos un ID de documento."]
                    }
                }
            ),
            403: OpenApiResponse(
                description="Permisos insuficientes o acceso denegado a algunos documentos",
                examples={
                    "application/json": {
                        "error": "No tienes permisos para descargar documentos",
                        "unauthorized_documents": [3, 5, 7]
                    }
                }
            ),
            404: OpenApiResponse(
                description="Algunos documentos no fueron encontrados",
                examples={
                    "application/json": {
                        "error": "Algunos documentos no fueron encontrados",
                        "not_found": [10, 12]
                    }
                }
            ),
        },
        description="Descargar múltiples documentos como un archivo ZIP comprimido",
        methods=["POST"]
    )
    
    @action(detail=False, methods=['post'], permission_classes=[CanDownload])
    def download_bulk(self, request):
        """
        Descargar múltiples documentos como un archivo ZIP
        """
        
        serializer = DocumentBulkDownloadSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        
        document_ids = serializer.validated_data['document_ids']
        zip_name = serializer.validated_data.get('zip_name', 'documentos')
        
        base_queryset = self.get_queryset()
        accessible_docs = base_queryset.filter(id__in=document_ids)
        accessible_ids = set(accessible_docs.values_list('id', flat=True))
        requested_ids = set(document_ids)
        
        if not accessible_docs.exists():
            return Response(
                {'error': 'No tienes acceso a ninguno de los documentos solicitados'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        unauthorized_ids = requested_ids - accessible_ids
        if unauthorized_ids:
            return Response(
                {
                    'error': 'No tienes acceso a algunos documentos',
                    'unauthorized_documents': list(unauthorized_ids)
                },
                status=status.HTTP_403_FORBIDDEN
            )
        
        docs_without_files = accessible_docs.filter(file__isnull=True) | accessible_docs.filter(file='')
        if docs_without_files.exists():
            missing_ids = list(docs_without_files.values_list('id', flat=True))
            return Response(
                {
                    'error': 'Algunos documentos no tienen archivos adjuntos',
                    'documents_without_files': missing_ids
                },
                status=status.HTTP_400_BAD_REQUEST
            )
        
        try:
            zip_buffer = io.BytesIO()
            
            with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
                for document in accessible_docs:
                    try:
                        original_filename = os.path.basename(document.file.name)
                        file_extension = os.path.splitext(original_filename)[1]
                        
                        safe_name = f"{document.id}_{document.name}"
                        safe_name = ''.join(c for c in safe_name if c.isalnum() or c in (' ', '.', '_')).rstrip()
                        safe_name += file_extension
                        
                        with document.file.open('rb') as file:
                            file_content = file.read()
                            zip_file.writestr(safe_name, file_content)
                        
                        metadata = f"""
Nombre: {document.name}
Descripción: {document.description or 'Sin descripción'}
Tipo: {document.file_type or 'No especificado'}
Tamaño: {document.file_size} bytes
Fecha de creación: {document.created_at}
Creado por: {document.created_by.get_full_name() if document.created_by else 'Desconocido'}
Público: {'Sí' if document.is_public else 'No'}
                        """.strip()
                        
                        zip_file.writestr(f"metadata_{document.id}.txt", metadata)
                        
                    except Exception as e:
                        print(f"Error procesando documento {document.id}: {str(e)}")
                        continue
            
            zip_buffer.seek(0, 2)  # Ir al final del archivo
            file_size = zip_buffer.tell()  # Obtener tamaño
            zip_buffer.seek(0)  # Volver al inicio
            
            response = HttpResponse(zip_buffer.getvalue(), content_type='application/zip')
            response['Content-Disposition'] = f'attachment; filename="{zip_name}.zip"'
            response['Content-Length'] = file_size
            
            response['X-Accel-Buffering'] = 'no'
            response['Cache-Control'] = 'no-cache'
            
            return response
            
        except Exception as e:
            return Response(
                {
                    'error': f'Error al crear el archivo ZIP: {str(e)}',
                    'details': str(e),
                    'traceback': traceback.format_exc()
                },
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
     
    @action(detail=True, methods=['get'])
    def versions(self, request, pk=None):
        document = self.get_object()
        versions = document.versions.all()
        serializer = DocumentVersionSerializer(versions, many=True, context={'request': request})
        return Response(serializer.data)

    @action(detail=True, methods=['post'])
    def create_version(self, request, pk=None):
        document = self.get_object()
        if not request.user.can_edit_documents and not request.user.is_staff:
            return Response({'error': 'No tienes permisos para editar documentos'}, status=status.HTTP_403_FORBIDDEN)

        data = request.data.copy()
        data['document'] = document.id
        serializer = DocumentVersionSerializer(data=data, context={'request': request})
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)


class DocumentVersionViewSet(viewsets.ModelViewSet):
    """ViewSet for DocumentVersion model with soft delete"""

    queryset = DocumentVersion.objects.all()
    serializer_class = DocumentVersionSerializer
    permission_classes = [DocumentPermissions]

    def get_queryset(self):
        user = self.request.user
        if user.is_staff or user.is_superuser:
            return DocumentVersion.objects.all()
        return DocumentVersion.objects.filter(
            Q(document__is_public=True) |
            Q(document__created_by=user) |
            Q(document__allowed_users=user)
        ).distinct()

    def perform_destroy(self, instance):
        """Override destroy to use soft delete"""
        instance.delete()

    @action(detail=True, methods=['post'])
    def restore(self, request, pk=None):
        """Restore a soft-deleted document version"""
        version = self.get_object()
        if version.deleted_at is None:
            return Response({'message': 'La versión no está eliminada'}, status=status.HTTP_400_BAD_REQUEST)
        version.restore()
        return Response({'message': 'Versión restaurada exitosamente'})

    @action(detail=False, methods=['get'])
    def deleted(self, request):
        """List soft-deleted versions"""
        deleted_versions = DocumentVersion.all_objects.dead()
        serializer = DocumentVersionSerializer(deleted_versions, many=True, context={'request': request})
        return Response(serializer.data)
