"""
API views для учебных материалов.
"""
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from django.db import models
from django.http import FileResponse, Http404
from .models import Material, MaterialFolder, MaterialTag, MaterialAccess, StorageQuota
from .serializers import (
    MaterialSerializer,
    MaterialListSerializer,
    MaterialCreateSerializer,
    MaterialFolderSerializer,
    MaterialTagSerializer,
    StorageQuotaSerializer,
    MaterialAccessSerializer
)
from .permissions import IsMaterialOwnerOrShared, IsMaterialOwner
from apps.subscriptions.permissions import RequiresActiveSubscription
from config.throttling import UploadRateThrottle


# Кастомная пагинация для материалов с поддержкой page_size из запроса
from rest_framework.pagination import PageNumberPagination


class MaterialPagination(PageNumberPagination):
    page_size = 20  # Размер страницы по умолчанию
    page_size_query_param = 'page_size'  # Параметр для изменения размера страницы
    max_page_size = 100  # Максимальный размер страницы


class MaterialViewSet(viewsets.ModelViewSet):
    """
    ViewSet для управления материалами.
    
    list: Список материалов
    create: Создать материал
    retrieve: Получить материал
    update: Обновить материал
    destroy: Удалить материал
    download: Скачать файл
    share: Предоставить доступ
    """
    
    permission_classes = [IsAuthenticated, RequiresActiveSubscription]
    pagination_class = MaterialPagination
    
    def get_throttles(self):
        """Применяем throttling только для создания (загрузки файлов)."""
        if self.action == 'create':
            return [UploadRateThrottle()]
        return super().get_throttles()
    
    def get_queryset(self):
        """Получение материалов."""
        user = self.request.user
        
        # Материалы пользователя и доступные ему.
        # Студент (client) видит только: свои, явно расшаренные (shared_with) и публичные.
        # Не показываем все материалы ментора с access_type='clients' — только shared_with.
        queryset = Material.objects.filter(
            models.Q(owner=user) |
            models.Q(shared_with__id=user.id) |
            models.Q(access_type='public')
        ).filter(
            is_deleted=False
        )
        
        queryset = queryset.distinct().select_related(
            'owner',
            'folder'
        ).prefetch_related('tags', 'shared_with')
        
        # Фильтр по папке
        folder_id = self.request.query_params.get('folder')
        if folder_id:
            queryset = queryset.filter(folder_id=folder_id)
        
        # Фильтр по типу
        material_type = self.request.query_params.get('type')
        if material_type:
            queryset = queryset.filter(material_type=material_type)
        
        # Фильтр по тегам
        tags = self.request.query_params.getlist('tags')
        if tags:
            queryset = queryset.filter(tags__id__in=tags).distinct()
        
        # Поиск
        search = self.request.query_params.get('search')
        if search:
            queryset = queryset.filter(
                models.Q(title__icontains=search) |
                models.Q(description__icontains=search) |
                models.Q(file_name__icontains=search)
            )
        
        # Оптимизация: для списка используем only() для ограничения полей
        if self.action == 'list':
            queryset = queryset.only(
                'id', 'title', 'description', 'file', 'file_name', 'file_size', 'file_type',
                'url', 'material_type', 'owner_id', 'folder_id', 'access_type',
                'is_featured', 'views_count', 'downloads_count',
                'created_at', 'updated_at'
            )
        
        return queryset
    
    def get_serializer_class(self):
        """Выбор сериализатора."""
        if self.action == 'list':
            return MaterialListSerializer
        elif self.action == 'create':
            return MaterialCreateSerializer
        return MaterialSerializer
    
    def create(self, request, *args, **kwargs):
        """Создание материала."""
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # Сериализатор сам устанавливает owner в методе create
        material = serializer.save()
        
        response_serializer = MaterialSerializer(material)
        return Response(
            response_serializer.data,
            status=status.HTTP_201_CREATED
        )
    
    def retrieve(self, request, *args, **kwargs):
        """Получить материал."""
        material = self.get_object()
        
        # Проверяем доступ
        if not material.has_access(request.user):
            return Response(
                {'error': 'У вас нет доступа к этому материалу'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        # Увеличиваем счетчик просмотров
        material.increment_views()
        
        # Логируем доступ
        MaterialAccess.objects.create(
            material=material,
            user=request.user,
            action='view',
            ip_address=request.META.get('REMOTE_ADDR'),
            user_agent=request.META.get('HTTP_USER_AGENT', '')[:500]
        )
        
        serializer = self.get_serializer(material)
        return Response(serializer.data)
    
    def destroy(self, request, *args, **kwargs):
        """Удаление материала (мягкое)."""
        material = self.get_object()
        
        # Проверяем права
        if material.owner != request.user:
            return Response(
                {'error': 'Только владелец может удалить материал'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        # Сохраняем размер файла
        file_size = material.file_size
        
        # Мягкое удаление
        material.soft_delete()
        
        # Обновляем квоту
        if material.file:
            from .services import StorageService
            StorageService.remove_file_usage(request.user, file_size)
        
        return Response(status=status.HTTP_204_NO_CONTENT)
    
    @action(detail=True, methods=['get'])
    def download(self, request, pk=None):
        """
        Скачать файл.
        
        GET /api/materials/materials/{id}/download/
        """
        material = self.get_object()
        
        # Проверяем доступ
        if not material.has_access(request.user):
            return Response(
                {'error': 'У вас нет доступа к этому материалу'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        # Проверяем разрешено ли скачивание
        if not material.allow_download:
            return Response(
                {'error': 'Скачивание этого материала запрещено'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        # Проверяем наличие файла
        if not material.file:
            return Response(
                {'error': 'У материала нет файла'},
                status=status.HTTP_404_NOT_FOUND
            )
        
        # Увеличиваем счетчик скачиваний
        material.increment_downloads()
        
        # Логируем доступ
        MaterialAccess.objects.create(
            material=material,
            user=request.user,
            action='download',
            ip_address=request.META.get('REMOTE_ADDR'),
            user_agent=request.META.get('HTTP_USER_AGENT', '')[:500]
        )
        
        # Отдаем файл
        response = FileResponse(material.file.open('rb'))
        response['Content-Type'] = material.file_type or 'application/octet-stream'
        response['Content-Disposition'] = f'attachment; filename="{material.file_name}"'
        return response
    
    @action(detail=True, methods=['post'])
    def share(self, request, pk=None):
        """
        Предоставить доступ к материалу.
        
        POST /api/materials/materials/{id}/share/
        Body: {
            "user_ids": [1, 2, 3]
        }
        """
        material = self.get_object()
        
        # Проверяем права
        if material.owner != request.user:
            return Response(
                {'error': 'Только владелец может предоставить доступ'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        user_ids = request.data.get('user_ids', [])
        
        # Обновляем полный список пользователей, которым доступен материал.
        # Если список пустой — доступ будет только у владельца (shared_with очищается).
        from apps.users.models import User
        import logging
        logger = logging.getLogger(__name__)
        
        logger.info(f"[MaterialViewSet.share] Material ID: {material.id}, User IDs: {user_ids}, Owner: {material.owner.id}")
        
        # Конвертируем строки в числа, если нужно
        if user_ids and isinstance(user_ids[0], str):
            user_ids = [int(uid) for uid in user_ids]
        
        users = User.objects.filter(id__in=user_ids)
        logger.info(f"[MaterialViewSet.share] Found users: {[u.id for u in users]}")
        
        material.shared_with.set(users)
        material.save()  # Убеждаемся, что изменения сохранены
        
        # Проверяем, что пользователи действительно добавлены
        shared_user_ids = list(material.shared_with.values_list('id', flat=True))
        logger.info(f"[MaterialViewSet.share] Material shared_with after save: {shared_user_ids}")
        
        # Оптимизация: создаем одну запись о шаринге (в оригинале создавалось N одинаковых записей)
        # Сохраняем логику создания записи, но используем одну запись вместо N
        MaterialAccess.objects.create(
            material=material,
            user=request.user,
            action='share',
            ip_address=request.META.get('REMOTE_ADDR')
        )
        
        serializer = MaterialSerializer(material)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def my_materials(self, request):
        """
        Мои материалы.
        
        GET /api/materials/materials/my_materials/
        """
        materials = Material.objects.filter(
            owner=request.user,
            is_deleted=False
        ).select_related('owner', 'folder').prefetch_related('tags').only(
            'id', 'title', 'file', 'file_name', 'file_size', 'file_type',
            'url', 'material_type', 'owner_id', 'folder_id', 'access_type',
            'is_featured', 'views_count', 'downloads_count',
            'created_at', 'updated_at'
        )
        
        serializer = MaterialListSerializer(materials, many=True)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def shared_with_me(self, request):
        """
        Материалы, к которым предоставлен доступ.
        
        GET /api/materials/materials/shared_with_me/
        """
        materials = Material.objects.filter(
            shared_with=request.user,
            is_deleted=False
        ).select_related('owner', 'folder').prefetch_related('tags').only(
            'id', 'title', 'file', 'file_name', 'file_size', 'file_type',
            'url', 'material_type', 'owner_id', 'folder_id', 'access_type',
            'is_featured', 'views_count', 'downloads_count',
            'created_at', 'updated_at'
        )
        
        serializer = MaterialListSerializer(materials, many=True)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def featured(self, request):
        """
        Избранные материалы.
        
        GET /api/materials/materials/featured/
        """
        materials = Material.objects.filter(
            owner=request.user,
            is_featured=True,
            is_deleted=False
        ).select_related('owner', 'folder').prefetch_related('tags').only(
            'id', 'title', 'file', 'file_name', 'file_size', 'file_type',
            'url', 'material_type', 'owner_id', 'folder_id', 'access_type',
            'is_featured', 'views_count', 'downloads_count',
            'created_at', 'updated_at'
        )
        
        serializer = MaterialListSerializer(materials, many=True)
        return Response(serializer.data)


class MaterialFolderViewSet(viewsets.ModelViewSet):
    """
    ViewSet для управления папками материалов.
    """
    
    permission_classes = [IsAuthenticated]
    serializer_class = MaterialFolderSerializer
    
    def get_queryset(self):
        """Получение папок."""
        user = self.request.user
        
        queryset = MaterialFolder.objects.filter(
            models.Q(owner=user) |
            models.Q(shared_with=user)
        ).distinct().select_related('owner', 'parent')
        
        # Оптимизация: для списка используем only() для ограничения полей
        if self.action == 'list':
            queryset = queryset.only(
                'id', 'name', 'description', 'owner_id', 'parent_id',
                'color', 'icon', 'created_at', 'updated_at'
            )
        
        return queryset
    
    def perform_create(self, serializer):
        """Создание папки."""
        serializer.save(owner=self.request.user)


class MaterialTagViewSet(viewsets.ModelViewSet):
    """
    ViewSet для управления тегами материалов.
    """
    
    permission_classes = [IsAuthenticated]
    serializer_class = MaterialTagSerializer
    
    def get_queryset(self):
        """Получение тегов."""
        queryset = MaterialTag.objects.all()
        
        # Оптимизация: для списка используем only() для ограничения полей
        if self.action == 'list':
            queryset = queryset.only(
                'id', 'name', 'slug', 'color', 'created_at', 'updated_at'
            )
        
        return queryset


class StorageQuotaViewSet(viewsets.ReadOnlyModelViewSet):
    """
    ViewSet для просмотра квот хранилища.
    """
    
    permission_classes = [IsAuthenticated]
    serializer_class = StorageQuotaSerializer
    
    def get_queryset(self):
        """Получение квот."""
        queryset = StorageQuota.objects.filter(user=self.request.user).select_related('user')
        
        # Оптимизация: для списка используем only() для ограничения полей
        if self.action == 'list':
            queryset = queryset.only(
                'id', 'user_id', 'total_mb', 'used_mb', 'available_mb',
                'subscription_limit_mb', 'created_at', 'updated_at'
            )
        
        return queryset
    
    @action(detail=False, methods=['get'])
    def my_quota(self, request):
        """
        Моя квота.
        
        GET /api/materials/quotas/my_quota/
        """
        from .services import StorageService
        
        # Синхронизируем квоту с подпиской
        StorageService.sync_quota_with_subscription(request.user)
        
        quota, created = StorageQuota.objects.get_or_create(user=request.user)
        serializer = StorageQuotaSerializer(quota)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def stats(self, request):
        """
        Статистика использования хранилища.
        
        GET /api/materials/quotas/stats/
        """
        from .services import StorageService
        
        stats = StorageService.get_storage_stats(request.user)
        return Response(stats)
    
    @action(detail=False, methods=['post'])
    def check_limit(self, request):
        """
        Проверить лимит перед загрузкой файла.
        
        POST /api/materials/quotas/check_limit/
        Body: {"file_size": 1024000}  # размер в байтах
        """
        from .services import StorageService
        
        file_size = request.data.get('file_size', 0)
        
        if not file_size or file_size <= 0:
            return Response(
                {'error': 'Необходимо указать размер файла (file_size в байтах)'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        can_upload, error_message, warning_message = StorageService.check_storage_limit(
            request.user, file_size
        )
        
        if not can_upload:
            return Response({
                'can_upload': False,
                'message': error_message,
                'is_warning': False,
                'is_critical': True
            }, status=status.HTTP_400_BAD_REQUEST)
        
        # Определяем, является ли предупреждение критическим (>= 90%)
        quota, _ = StorageQuota.objects.get_or_create(user=request.user)
        used_percentage = quota.get_used_percentage()
        is_critical = used_percentage >= 90
        is_warning = used_percentage >= 80 and not is_critical
        
        return Response({
            'can_upload': True,
            'message': warning_message,
            'is_warning': is_warning,
            'is_critical': is_critical
        })
    
    @action(detail=False, methods=['post'])
    def recalculate(self, request):
        """
        Пересчитать использование.
        
        POST /api/materials/quotas/recalculate/
        """
        from .services import StorageService
        
        # Синхронизируем квоту с подпиской перед пересчетом
        StorageService.sync_quota_with_subscription(request.user)
        
        from .services import StorageService
        
        # Синхронизируем квоту с подпиской перед пересчетом
        StorageService.sync_quota_with_subscription(request.user)
        
        quota, created = StorageQuota.objects.get_or_create(user=request.user)
        quota.recalculate()
        serializer = StorageQuotaSerializer(quota)
        return Response(serializer.data)
    
    @action(detail=False, methods=['post'])
    def cleanup_old_files(self, request):
        """
        Очистить старые неиспользуемые файлы.
        
        POST /api/materials/quotas/cleanup_old_files/
        Body: {"days_old": 90}  # опционально, по умолчанию 90 дней
        """
        from .services import StorageService
        
        days_old = request.data.get('days_old', 90)
        
        if not isinstance(days_old, int) or days_old < 1:
            return Response(
                {'error': 'days_old должен быть положительным числом'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        result = StorageService.cleanup_old_unused_files(request.user, days_old=days_old)
        
        return Response({
            'success': True,
            'deleted_count': result['deleted_count'],
            'freed_space_mb': round(result['freed_space_mb'], 2),
            'message': f'Удалено {result["deleted_count"]} материалов, освобождено {result["freed_space_mb"]:.2f} МБ'
        })
