from rest_framework import viewsets, filters, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from django.db import transaction, models
from django.db.models import Q
from drf_spectacular.utils import extend_schema, extend_schema_view
from igny8_core.api.base import SiteSectorModelViewSet
from igny8_core.api.pagination import CustomPageNumberPagination
from igny8_core.api.response import success_response, error_response
from igny8_core.api.throttles import DebugScopedRateThrottle
from igny8_core.api.permissions import IsAuthenticatedAndActive, IsViewerOrAbove, IsEditorOrAbove
from .models import Tasks, Images, Content
from .serializers import (
    TasksSerializer, 
    ImagesSerializer, 
    ContentSerializer,
    ContentTaxonomySerializer,
)
from igny8_core.business.content.models import ContentTaxonomy  # ContentAttribute model exists but serializer removed in Stage 1
from igny8_core.business.content.services.content_generation_service import ContentGenerationService
from igny8_core.business.content.services.validation_service import ContentValidationService
from igny8_core.business.content.services.metadata_mapping_service import MetadataMappingService
from igny8_core.business.billing.exceptions import InsufficientCreditsError



@extend_schema_view(
    list=extend_schema(tags=['Writer']),
    create=extend_schema(tags=['Writer']),
    retrieve=extend_schema(tags=['Writer']),
    update=extend_schema(tags=['Writer']),
    partial_update=extend_schema(tags=['Writer']),
    destroy=extend_schema(tags=['Writer']),
)
class TasksViewSet(SiteSectorModelViewSet):
    """
    ViewSet for managing tasks with CRUD operations
    Unified API Standard v1.0 compliant
    Stage 1 Refactored - removed deprecated filters
    """
    queryset = Tasks.objects.select_related('cluster', 'site', 'sector')
    serializer_class = TasksSerializer
    permission_classes = [IsAuthenticatedAndActive, IsViewerOrAbove]
    pagination_class = CustomPageNumberPagination  # Explicitly use custom pagination
    throttle_scope = 'writer'
    throttle_classes = [DebugScopedRateThrottle]
    
    # DRF filtering configuration
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    
    # Search configuration
    search_fields = ['title', 'keywords']
    
    # Ordering configuration
    ordering_fields = ['title', 'created_at', 'status']
    ordering = ['-created_at']  # Default ordering (newest first)
    
    # Filter configuration - Stage 1: removed entity_type, cluster_role
    filterset_fields = ['status', 'cluster_id', 'content_type', 'content_structure']
    
    def perform_create(self, serializer):
        """Require explicit site_id and sector_id - no defaults."""
        user = getattr(self.request, 'user', None)
        
        try:
            query_params = getattr(self.request, 'query_params', None)
            if query_params is None:
                query_params = getattr(self.request, 'GET', {})
        except AttributeError:
            query_params = {}
        
        site_id = serializer.validated_data.get('site_id') or query_params.get('site_id')
        sector_id = serializer.validated_data.get('sector_id') or query_params.get('sector_id')
        
        from igny8_core.auth.models import Site, Sector
        from rest_framework.exceptions import ValidationError
        
        # Site ID is REQUIRED
        if not site_id:
            raise ValidationError("site_id is required. Please select a site.")
        
        try:
            site = Site.objects.get(id=site_id)
        except Site.DoesNotExist:
            raise ValidationError(f"Site with id {site_id} does not exist")
        
        # Sector ID is REQUIRED
        if not sector_id:
            raise ValidationError("sector_id is required. Please select a sector.")
        
        try:
            sector = Sector.objects.get(id=sector_id)
            if sector.site_id != site_id:
                raise ValidationError(f"Sector does not belong to the selected site")
        except Sector.DoesNotExist:
            raise ValidationError(f"Sector with id {sector_id} does not exist")
        
        serializer.validated_data.pop('site_id', None)
        serializer.validated_data.pop('sector_id', None)
        
        account = getattr(self.request, 'account', None)
        if not account and user and user.is_authenticated and user.account:
            account = user.account
        if not account:
            account = site.account
        
        serializer.save(account=account, site=site, sector=sector)
    
    @action(detail=False, methods=['POST'], url_path='bulk_delete', url_name='bulk_delete')
    def bulk_delete(self, request):
        """Bulk delete tasks"""
        ids = request.data.get('ids', [])
        if not ids:
            return error_response(
                error='No IDs provided',
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request
            )
        
        queryset = self.get_queryset()
        deleted_count, _ = queryset.filter(id__in=ids).delete()
        
        return success_response(data={'deleted_count': deleted_count}, request=request)
    
    @action(detail=False, methods=['post'], url_path='bulk_update', url_name='bulk_update')
    def bulk_update(self, request):
        """Bulk update task status"""
        ids = request.data.get('ids', [])
        status_value = request.data.get('status')
        
        if not ids:
            return error_response(
                error='No IDs provided',
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request
            )
        if not status_value:
            return error_response(
                error='No status provided',
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request
            )
        
        queryset = self.get_queryset()
        updated_count = queryset.filter(id__in=ids).update(status=status_value)
        
        return success_response(data={'updated_count': updated_count}, request=request)
    
    @action(detail=False, methods=['post'], url_path='auto_generate_content', url_name='auto_generate_content')
    def auto_generate_content(self, request):
        """Auto-generate content for tasks using ContentGenerationService"""
        import logging
        
        logger = logging.getLogger(__name__)
        
        try:
            ids = request.data.get('ids', [])
            if not ids:
                return error_response(
                    error='No IDs provided',
                    status_code=status.HTTP_400_BAD_REQUEST,
                    request=request
                )
            
            if len(ids) > 10:
                return error_response(
                    error='Maximum 10 tasks allowed for content generation',
                    status_code=status.HTTP_400_BAD_REQUEST,
                    request=request
                )
            
            # Get account
            account = getattr(request, 'account', None)
            if not account:
                return error_response(
                    error='Account is required',
                    status_code=status.HTTP_400_BAD_REQUEST,
                    request=request
                )
            
            # Validate task IDs exist
            queryset = self.get_queryset()
            existing_tasks = queryset.filter(id__in=ids, account=account)
            existing_count = existing_tasks.count()
            
            if existing_count == 0:
                return error_response(
                    error=f'No tasks found for the provided IDs: {ids}',
                    status_code=status.HTTP_404_NOT_FOUND,
                    request=request
                )
            
            # Use service to generate content
            service = ContentGenerationService()
            try:
                result = service.generate_content(ids, account)
                
                if result.get('success'):
                    if 'task_id' in result:
                        # Async task queued
                        return success_response(
                            data={'task_id': result['task_id']},
                            message=result.get('message', 'Content generation started'),
                            request=request
                        )
                    else:
                        # Synchronous execution
                        return success_response(
                            data=result,
                            message='Content generated successfully',
                            request=request
                        )
                else:
                    return error_response(
                        error=result.get('error', 'Content generation failed'),
                        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                        request=request
                    )
            except InsufficientCreditsError as e:
                return error_response(
                    error=str(e),
                    status_code=status.HTTP_402_PAYMENT_REQUIRED,
                    request=request
                )
            except Exception as e:
                logger.error(f"Error in auto_generate_content: {str(e)}", exc_info=True)
                return error_response(
                    error=f'Content generation failed: {str(e)}',
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    request=request
                )
        
        except Exception as e:
            logger.error(f"Unexpected error in auto_generate_content: {str(e)}", exc_info=True)
            return error_response(
                error=f'Unexpected error: {str(e)}',
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                request=request
            )


@extend_schema_view(
    list=extend_schema(tags=['Writer']),
    create=extend_schema(tags=['Writer']),
    retrieve=extend_schema(tags=['Writer']),
    update=extend_schema(tags=['Writer']),
    partial_update=extend_schema(tags=['Writer']),
    destroy=extend_schema(tags=['Writer']),
)
class ImagesViewSet(SiteSectorModelViewSet):
    """
    ViewSet for managing content images
    Unified API Standard v1.0 compliant
    """
    queryset = Images.objects.all()
    serializer_class = ImagesSerializer
    permission_classes = [IsAuthenticatedAndActive, IsViewerOrAbove]
    pagination_class = CustomPageNumberPagination
    throttle_scope = 'writer'
    throttle_classes = [DebugScopedRateThrottle]
    
    filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
    ordering_fields = ['created_at', 'position', 'id']
    ordering = ['-id']  # Sort by ID descending (newest first)
    filterset_fields = ['task_id', 'content_id', 'image_type', 'status']
    
    def perform_create(self, serializer):
        """Override to automatically set account, site, and sector"""
        from rest_framework.exceptions import ValidationError
        
        # Get site and sector from request (set by middleware) or user's active context
        site = getattr(self.request, 'site', None)
        sector = getattr(self.request, 'sector', None)

        if not site:
            # Fallback to user's active site if not set by middleware
            user = getattr(self.request, 'user', None)
            if user and user.is_authenticated and hasattr(user, 'active_site'):
                site = user.active_site

        if not sector and site:
            # Fallback to default sector for the site if not set by middleware
            from igny8_core.auth.models import Sector
            sector = site.sectors.filter(is_default=True).first()

        # Site and sector are required - raise ValidationError if not available
        # Use dict format for ValidationError to ensure proper error structure
        if not site:
            raise ValidationError({"site": ["Site is required for image creation. Please select a site."]})
        if not sector:
            raise ValidationError({"sector": ["Sector is required for image creation. Please select a sector."]})

        # Add site and sector to validated_data so base class can validate access
        serializer.validated_data['site'] = site
        serializer.validated_data['sector'] = sector
        
        # Call parent to set account and validate access
        super().perform_create(serializer)
    
    @action(detail=True, methods=['get'], url_path='file', url_name='image_file')
    def serve_image_file(self, request, pk=None):
        """
        Serve image file from local path via URL
        GET /api/v1/writer/images/{id}/file/
        """
        import os
        from django.http import FileResponse, Http404
        from django.conf import settings
        
        try:
            # Get image directly without account filtering for file serving
            # This allows public access to image files
            try:
                image = Images.objects.get(pk=pk)
            except Images.DoesNotExist:
                return error_response(
                    error='Image not found',
                    status_code=status.HTTP_404_NOT_FOUND,
                    request=request
                )
            
            # Check if image has a local path
            if not image.image_path:
                return error_response(
                    error='No local file path available for this image',
                    status_code=status.HTTP_404_NOT_FOUND,
                    request=request
                )
            
            file_path = image.image_path
            
            # Verify file exists at the saved path
            if not os.path.exists(file_path):
                logger.error(f"[serve_image_file] Image {pk} - File not found at saved path: {file_path}")
                return error_response(
                    error=f'Image file not found at: {file_path}',
                    status_code=status.HTTP_404_NOT_FOUND,
                    request=request
                )
            
            # Check if file is readable
            if not os.access(file_path, os.R_OK):
                return error_response(
                    error='Image file is not readable',
                    status_code=status.HTTP_403_FORBIDDEN,
                    request=request
                )
            
            # Determine content type from file extension
            import mimetypes
            content_type, _ = mimetypes.guess_type(file_path)
            if not content_type:
                content_type = 'image/png'  # Default to PNG
            
            # Serve the file
            try:
                return FileResponse(
                    open(file_path, 'rb'),
                    content_type=content_type,
                    filename=os.path.basename(file_path)
                )
            except Exception as e:
                return error_response(
                    error=f'Failed to serve file: {str(e)}',
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    request=request
                )
                
        except Images.DoesNotExist:
            return error_response(
                error='Image not found',
                status_code=status.HTTP_404_NOT_FOUND,
                request=request
            )
        except Exception as e:
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"Error serving image file: {str(e)}", exc_info=True)
            return error_response(
                error=f'Failed to serve image: {str(e)}',
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                request=request
            )
    
    @action(detail=False, methods=['post'], url_path='auto_generate', url_name='auto_generate_images')
    def auto_generate_images(self, request):
        """Auto-generate images for tasks using AI"""
        task_ids = request.data.get('task_ids', [])
        if not task_ids:
            return error_response(
                error='No task IDs provided',
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request
            )
        
        if len(task_ids) > 10:
            return error_response(
                error='Maximum 10 tasks allowed for image generation',
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request
            )
        
        # Get account
        account = getattr(request, 'account', None)
        account_id = account.id if account else None
        
        # Try to queue Celery task, fall back to synchronous if Celery not available
        try:
            from igny8_core.ai.tasks import run_ai_task
            from kombu.exceptions import OperationalError as KombuOperationalError
            
            if hasattr(run_ai_task, 'delay'):
                # Celery is available - queue async task
                task = run_ai_task.delay(
                    function_name='generate_images',
                    payload={'ids': task_ids},
                    account_id=account_id
                )
                return success_response(
                    data={'task_id': str(task.id)},
                    message='Image generation started',
                    request=request
                )
            else:
                # Celery not available - execute synchronously
                result = run_ai_task(
                    function_name='generate_images',
                    payload={'ids': task_ids},
                    account_id=account_id
                )
                if result.get('success'):
                    return success_response(
                        data={'images_created': result.get('count', 0)},
                        message=result.get('message', 'Image generation completed'),
                        request=request
                    )
                else:
                    return error_response(
                        error=result.get('error', 'Image generation failed'),
                        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                        request=request
                    )
        except KombuOperationalError as e:
            return error_response(
                error='Task queue unavailable. Please try again.',
                status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
                request=request
            )
        except ImportError:
            # Tasks module not available
            return error_response(
                error='Image generation task not available',
                status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
                request=request
            )
        except Exception as e:
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"Error queuing image generation task: {str(e)}", exc_info=True)
            return error_response(
                error=f'Failed to start image generation: {str(e)}',
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                request=request
            )
    
    @action(detail=False, methods=['post'], url_path='bulk_update', url_name='bulk_update')
    def bulk_update(self, request):
        """Bulk update image status by content_id or image IDs
        Updates all images for a content record (featured + 1-6 in-article images)
        """
        from django.db.models import Q
        from .models import Content
        
        content_id = request.data.get('content_id')
        image_ids = request.data.get('ids', [])
        status_value = request.data.get('status')
        
        if not status_value:
            return error_response(
                error='No status provided',
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request
            )
        
        queryset = self.get_queryset()
        
        # Update by content_id if provided, otherwise by image IDs
        if content_id:
            try:
                # Get the content object to also update images linked directly to content
                content = Content.objects.get(id=content_id)
                
                # Update images linked directly to content (all images: featured + in-article)
                # Note: task field was removed in refactor - images now link directly to content
                updated_count = queryset.filter(content=content).update(status=status_value)
            except Content.DoesNotExist:
                return error_response(
                    error='Content not found',
                    status_code=status.HTTP_404_NOT_FOUND,
                    request=request
                )
        elif image_ids:
            updated_count = queryset.filter(id__in=image_ids).update(status=status_value)
        else:
            return error_response(
                error='Either content_id or ids must be provided',
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request
            )
        
        return success_response(data={'updated_count': updated_count}, request=request)
    
    @action(detail=False, methods=['get'], url_path='content_images', url_name='content_images')
    def content_images(self, request):
        """Get images grouped by content - one row per content with featured and in-article images"""
        from .serializers import ContentImagesGroupSerializer, ContentImageSerializer
        
        account = getattr(request, 'account', None)
        
        # Get site_id and sector_id from query parameters
        site_id = request.query_params.get('site_id')
        sector_id = request.query_params.get('sector_id')
        
        # Get all content that has images (either directly or via task)
        # First, get content with direct image links
        queryset = Content.objects.filter(images__isnull=False)
        if account:
            queryset = queryset.filter(account=account)
        
        # Apply site/sector filtering if provided
        if site_id:
            try:
                queryset = queryset.filter(site_id=int(site_id))
            except (ValueError, TypeError):
                pass
        
        if sector_id:
            try:
                queryset = queryset.filter(sector_id=int(sector_id))
            except (ValueError, TypeError):
                pass
        
        # Task field removed in Stage 1 - images are now only linked to content directly
        # All images must be linked via content, not task
        
        # Build grouped response
        grouped_data = []
        content_ids = set(queryset.values_list('id', flat=True).distinct())
        
        for content_id in content_ids:
            try:
                content = Content.objects.get(id=content_id)
                
                # Get images linked directly to content
                content_images = Images.objects.filter(content=content).order_by('position')
                
                # Get featured image
                featured_image = content_images.filter(image_type='featured').first()
                
                # Get in-article images (sorted by position)
                in_article_images = list(content_images.filter(image_type='in_article').order_by('position'))
                
                # Determine overall status
                all_images = list(content_images)
                if not all_images:
                    overall_status = 'pending'
                elif all(img.status == 'generated' for img in all_images):
                    overall_status = 'complete'
                elif any(img.status == 'failed' for img in all_images):
                    overall_status = 'failed'
                elif any(img.status == 'generated' for img in all_images):
                    overall_status = 'partial'
                else:
                    overall_status = 'pending'
                
                # Create serializer instances with request context for proper URL generation
                featured_serializer = ContentImageSerializer(featured_image, context={'request': request}) if featured_image else None
                in_article_serializers = [ContentImageSerializer(img, context={'request': request}) for img in in_article_images]
                
                grouped_data.append({
                    'content_id': content.id,
                    'content_title': content.title or content.meta_title or f"Content #{content.id}",
                    'content_status': content.status,  # Add content status
                    'featured_image': featured_serializer.data if featured_serializer else None,
                    'in_article_images': [s.data for s in in_article_serializers],
                    'overall_status': overall_status,
                })
            except Content.DoesNotExist:
                continue
        
        # Sort by content title
        grouped_data.sort(key=lambda x: x['content_title'])
        
        return success_response(
            data={
                'count': len(grouped_data),
                'results': grouped_data
            },
            request=request
        )
    
    @action(detail=False, methods=['post'], url_path='generate_images', url_name='generate_images')
    def generate_images(self, request):
        """Generate images from prompts - queues Celery task for sequential processing"""
        from igny8_core.ai.tasks import process_image_generation_queue
        
        account = getattr(request, 'account', None)
        image_ids = request.data.get('ids', [])
        content_id = request.data.get('content_id')
        
        if not image_ids:
            return error_response(
                error='No image IDs provided',
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request
            )
        
        account_id = account.id if account else None
        
        # Queue Celery task
        try:
            if hasattr(process_image_generation_queue, 'delay'):
                task = process_image_generation_queue.delay(
                    image_ids=image_ids,
                    account_id=account_id,
                    content_id=content_id
                )
                return success_response(
                    data={'task_id': str(task.id)},
                    message='Image generation started',
                    request=request
                )
            else:
                # Fallback to synchronous execution (for testing)
                result = process_image_generation_queue(
                    image_ids=image_ids,
                    account_id=account_id,
                    content_id=content_id
                )
                return success_response(data=result, request=request)
        except Exception as e:
            logger.error(f"[generate_images] Error: {str(e)}", exc_info=True)
            return error_response(
                error=str(e),
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                request=request
            )
    
    @action(detail=True, methods=['get'], url_path='validation', url_name='validation')
    def validation(self, request, pk=None):
        """
        Stage 3: Get validation checklist for content.
        
        GET /api/v1/writer/content/{id}/validation/
        Returns aggregated validation checklist for Writer UI.
        """
        content = self.get_object()
        validation_service = ContentValidationService()
        
        errors = validation_service.validate_content(content)
        publish_errors = validation_service.validate_for_publish(content)
        
        return success_response(
            data={
                'content_id': content.id,
                'is_valid': len(errors) == 0,
                'ready_to_publish': len(publish_errors) == 0,
                'validation_errors': errors,
                'publish_errors': publish_errors,
                'metadata': {
                    'has_entity_type': bool(content.content_type),
                    'entity_type': content.content_type,
                    'has_cluster_mapping': self._has_cluster_mapping(content),
                    'has_taxonomy_mapping': self._has_taxonomy_mapping(content),
                }
            },
            request=request
        )
    
    @action(detail=True, methods=['post'], url_path='validate', url_name='validate')
    def validate(self, request, pk=None):
        """
        Stage 3: Re-run validators and return actionable errors.
        
        POST /api/v1/writer/content/{id}/validate/
        Re-validates content and returns structured errors.
        """
        content = self.get_object()
        validation_service = ContentValidationService()
        
        # Persist metadata mappings if task exists
        # Metadata is now persisted directly on content - no task linkage needed
            # mapping_service = MetadataMappingService()  # DEPRECATED
            # mapping_service.persist_task_metadata_to_content(content)  # DEPRECATED
        
        errors = validation_service.validate_for_publish(content)
        
        return success_response(
            data={
                'content_id': content.id,
                'is_valid': len(errors) == 0,
                'errors': errors,
            },
            request=request
        )
    
    def _has_cluster_mapping(self, content):
        """Helper to check if content has cluster mapping"""
        from igny8_core.business.content.models import ContentClusterMap
        return ContentClusterMap.objects.filter(content=content).exists()
    
    def _has_taxonomy_mapping(self, content):
        """Helper to check if content has taxonomy mapping"""
        from igny8_core.business.content.models import ContentTaxonomyMap
        return ContentTaxonomyMap.objects.filter(content=content).exists()
    
@extend_schema_view(
    list=extend_schema(tags=['Writer']),
    create=extend_schema(tags=['Writer']),
    retrieve=extend_schema(tags=['Writer']),
    update=extend_schema(tags=['Writer']),
    partial_update=extend_schema(tags=['Writer']),
    destroy=extend_schema(tags=['Writer']),
)
class ContentViewSet(SiteSectorModelViewSet):
    """
    ViewSet for managing content with new unified structure
    Unified API Standard v1.0 compliant
    Stage 1 Refactored - removed deprecated fields
    """
    queryset = Content.objects.select_related('cluster', 'site', 'sector').prefetch_related('taxonomy_terms')
    serializer_class = ContentSerializer
    permission_classes = [IsAuthenticatedAndActive, IsViewerOrAbove]
    pagination_class = CustomPageNumberPagination
    throttle_scope = 'writer'
    throttle_classes = [DebugScopedRateThrottle]
    
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    search_fields = ['title', 'content_html', 'external_url']
    ordering_fields = ['created_at', 'updated_at', 'status']
    ordering = ['-created_at']
    # Stage 1: removed task_id, entity_type, content_format, cluster_role, sync_status, external_type
    filterset_fields = [
        'cluster_id',
        'status', 
        'content_type',
        'content_structure',
        'source',
    ]
    
    def perform_create(self, serializer):
        """Override to automatically set account"""
        account = getattr(self.request, 'account', None)
        if account:
            serializer.save(account=account)
        else:
            serializer.save()
    
    @action(detail=False, methods=['POST'], url_path='bulk_delete', url_name='bulk_delete')
    def bulk_delete(self, request):
        """Bulk delete content"""
        ids = request.data.get('ids', [])
        if not ids:
            return error_response(
                error='No IDs provided',
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request
            )
        
        queryset = self.get_queryset()
        deleted_count, _ = queryset.filter(id__in=ids).delete()
        
        return success_response(data={'deleted_count': deleted_count}, request=request)
    
    @action(detail=True, methods=['post'], url_path='publish', url_name='publish', permission_classes=[IsAuthenticatedAndActive, IsEditorOrAbove])
    def publish(self, request, pk=None):
        """
        STAGE 3: Publish content to WordPress site via Celery task.
        Mirrors the automated publishing flow for manual publishing from Review page.
        
        POST /api/v1/writer/content/{id}/publish/
        {
            "site_integration_id": 1  // Optional - defaults to finding WordPress integration for content's site
        }
        """
        from igny8_core.business.integration.models import SiteIntegration
        from igny8_core.tasks.wordpress_publishing import publish_content_to_wordpress
        import logging
        
        logger = logging.getLogger(__name__)
        
        content = self.get_object()
        
        # STAGE 3: Prevent duplicate publishing
        if content.external_id:
            return error_response(
                error='Content already published. Use WordPress to update or unpublish first.',
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request,
                errors={'external_id': [f'Already published with ID: {content.external_id}']}
            )
        
        # Get site integration (use content's site if not specified)
        site_integration_id = request.data.get('site_integration_id')
        
        if not site_integration_id:
            # Find WordPress integration for this site
            site_integrations = SiteIntegration.objects.filter(
                site=content.site,
                platform='wordpress',
                is_active=True
            )
            
            if not site_integrations.exists():
                return error_response(
                    error='No active WordPress integration found for this site',
                    status_code=status.HTTP_400_BAD_REQUEST,
                    request=request,
                    errors={'site_integration': ['WordPress integration is required to publish']}
                )
            
            site_integration = site_integrations.first()
        else:
            try:
                site_integration = SiteIntegration.objects.get(
                    id=site_integration_id,
                    site=content.site,
                    platform='wordpress'
                )
            except SiteIntegration.DoesNotExist:
                return error_response(
                    error=f'WordPress integration with id {site_integration_id} not found for this site',
                    status_code=status.HTTP_404_NOT_FOUND,
                    request=request
                )
        
        # OPTIMISTIC UPDATE: Set status to published immediately for better UX
        # The Celery task will update external_id and external_url when WordPress responds
        content.status = 'published'
        content.save(update_fields=['status', 'updated_at'])
        
        # Queue publishing task (same as automated flow)
        try:
            result = publish_content_to_wordpress.delay(
                content_id=content.id,
                site_integration_id=site_integration.id
            )
            
            logger.info(f"[ContentViewSet.publish] Queued Celery task {result.id} for content {content.id}, status set to 'published'")
            
            return success_response(
                data={
                    'content_id': content.id,
                    'task_id': result.id,
                    'status': 'published',
                    'message': 'Publishing queued - content will be published to WordPress shortly'
                },
                message='Content status updated to published and queued for WordPress',
                request=request,
                status_code=status.HTTP_202_ACCEPTED
            )
            
        except Exception as e:
            logger.error(f"[ContentViewSet.publish] Error queuing publish task: {str(e)}", exc_info=True)
            # Revert status on error
            content.status = 'review'
            content.save(update_fields=['status', 'updated_at'])
            return error_response(
                error=f"Failed to queue publishing task: {str(e)}",
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                request=request
            )
    
    @action(detail=True, methods=['get'], url_path='wordpress_status', url_name='wordpress_status')
    def wordpress_status(self, request, pk=None):
        """
        Get WordPress post status for published content.
        Calls WordPress REST API to get current status.
        
        GET /api/v1/writer/content/{id}/wordpress_status/
        Returns: {
            'wordpress_status': 'publish'|'draft'|'pending'|null,
            'external_id': 123,
            'external_url': 'https://...',
            'last_checked': '2025-11-30T...'
        }
        """
        import requests
        from django.utils import timezone
        from igny8_core.business.integration.models import SiteIntegration
        import logging
        
        logger = logging.getLogger(__name__)
        content = self.get_object()
        
        if not content.external_id:
            return success_response(
                data={
                    'wordpress_status': None,
                    'external_id': None,
                    'external_url': None,
                    'message': 'Content not published to WordPress yet'
                },
                request=request
            )
        
        # Get WordPress integration for this content's site
        try:
            site_integration = SiteIntegration.objects.filter(
                site=content.site,
                platform='wordpress',
                is_active=True
            ).first()
            
            if not site_integration:
                return error_response(
                    error='No active WordPress integration found',
                    status_code=status.HTTP_404_NOT_FOUND,
                    request=request
                )
            
            # Call WordPress REST API to get post status
            wordpress_url = f"{site_integration.site_url}/wp-json/igny8/v1/post-status/{content.external_id}/"
            headers = {
                'X-IGNY8-API-KEY': site_integration.api_key,
            }
            
            response = requests.get(wordpress_url, headers=headers, timeout=10)
            
            if response.status_code == 200:
                wp_data = response.json().get('data', {})
                return success_response(
                    data={
                        'wordpress_status': wp_data.get('post_status'),
                        'external_id': content.external_id,
                        'external_url': content.external_url,
                        'post_title': wp_data.get('post_title'),
                        'post_modified': wp_data.get('post_modified'),
                        'last_checked': timezone.now().isoformat()
                    },
                    request=request
                )
            else:
                logger.error(f"WordPress API error: {response.status_code} - {response.text}")
                return error_response(
                    error=f'Failed to get WordPress status: {response.status_code}',
                    status_code=status.HTTP_502_BAD_GATEWAY,
                    request=request
                )
                
        except requests.RequestException as e:
            logger.error(f"Request to WordPress failed: {str(e)}")
            return error_response(
                error=f'Connection to WordPress failed: {str(e)}',
                status_code=status.HTTP_502_BAD_GATEWAY,
                request=request
            )
        except Exception as e:
            logger.error(f"Error getting WordPress status: {str(e)}", exc_info=True)
            return error_response(
                error=str(e),
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                request=request
            )
    
    @action(detail=True, methods=['post'], url_path='unpublish', url_name='unpublish', permission_classes=[IsAuthenticatedAndActive, IsEditorOrAbove])
    def unpublish(self, request, pk=None):
        """
        STAGE 3: Unpublish content - clear external references and revert to draft.
        Note: This does NOT delete the WordPress post, only clears the link.
        
        POST /api/v1/writer/content/{id}/unpublish/
        """
        content = self.get_object()
        
        if not content.external_id:
            return error_response(
                error='Content is not published',
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request
            )
        
        # Store the old values for response
        old_external_id = content.external_id
        old_external_url = content.external_url
        
        # Clear external references and revert status
        content.external_id = None
        content.external_url = None
        content.status = 'draft'
        content.save(update_fields=['external_id', 'external_url', 'status', 'updated_at'])
        
        return success_response(
            data={
                'content_id': content.id,
                'status': content.status,
                'was_external_id': old_external_id,
                'was_external_url': old_external_url,
            },
            message='Content unpublished successfully. WordPress post was not deleted.',
            request=request
        )
    
    @action(detail=False, methods=['post'], url_path='generate_image_prompts', url_name='generate_image_prompts')
    def generate_image_prompts(self, request):
        """Generate image prompts for content records - same pattern as other AI functions"""
        from igny8_core.ai.tasks import run_ai_task
        
        account = getattr(request, 'account', None)
        ids = request.data.get('ids', [])
        
        if not ids:
            return error_response(
                error='No IDs provided',
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request
            )
        
        account_id = account.id if account else None
        
        # Queue Celery task
        try:
            if hasattr(run_ai_task, 'delay'):
                task = run_ai_task.delay(
                    function_name='generate_image_prompts',
                    payload={'ids': ids},
                    account_id=account_id
                )
                return success_response(
                    data={'task_id': str(task.id)},
                    message='Image prompt generation started',
                    request=request
                )
            else:
                # Fallback to synchronous execution
                result = run_ai_task(
                    function_name='generate_image_prompts',
                    payload={'ids': ids},
                    account_id=account_id
                )
                if result.get('success'):
                    return success_response(
                        data={'prompts_created': result.get('count', 0)},
                        message='Image prompts generated successfully',
                        request=request
                    )
                else:
                    return error_response(
                        error=result.get('error', 'Image prompt generation failed'),
                        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                        request=request
                    )
        except Exception as e:
            return error_response(
                error=str(e),
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                request=request
            )
    
    @action(detail=True, methods=['get'], url_path='validation', url_name='validation')
    def validation(self, request, pk=None):
        """
        Stage 3: Get validation checklist for content.
        
        GET /api/v1/writer/content/{id}/validation/
        Returns aggregated validation checklist for Writer UI.
        """
        content = self.get_object()
        validation_service = ContentValidationService()
        
        errors = validation_service.validate_content(content)
        publish_errors = validation_service.validate_for_publish(content)
        
        return success_response(
            data={
                'content_id': content.id,
                'is_valid': len(errors) == 0,
                'ready_to_publish': len(publish_errors) == 0,
                'validation_errors': errors,
                'publish_errors': publish_errors,
                'metadata': {
                    'has_entity_type': bool(content.content_type),
                    'entity_type': content.content_type,
                    'has_cluster_mapping': self._has_cluster_mapping(content),
                    'has_taxonomy_mapping': self._has_taxonomy_mapping(content),
                }
            },
            request=request
        )
    
    @action(detail=True, methods=['post'], url_path='validate', url_name='validate')
    def validate(self, request, pk=None):
        """
        Stage 3: Re-run validators and return actionable errors.
        
        POST /api/v1/writer/content/{id}/validate/
        Re-validates content and returns structured errors.
        """
        content = self.get_object()
        validation_service = ContentValidationService()
        
        # Persist metadata mappings if task exists
        # Metadata is now persisted directly on content - no task linkage needed
            # mapping_service = MetadataMappingService()  # DEPRECATED
            # mapping_service.persist_task_metadata_to_content(content)  # DEPRECATED
        
        errors = validation_service.validate_for_publish(content)
        
        return success_response(
            data={
                'content_id': content.id,
                'is_valid': len(errors) == 0,
                'errors': errors,
            },
            request=request
        )
    
    def _has_cluster_mapping(self, content):
        """Helper to check if content has cluster mapping"""
        from igny8_core.business.content.models import ContentClusterMap
        return ContentClusterMap.objects.filter(content=content).exists()
    
    def _has_taxonomy_mapping(self, content):
        """Helper to check if content has taxonomy mapping"""
        from igny8_core.business.content.models import ContentTaxonomyMap
        return ContentTaxonomyMap.objects.filter(content=content).exists()
    
    @action(detail=False, methods=['post'], url_path='generate_product', url_name='generate_product')
    def generate_product(self, request):
        """
        Generate product content (Phase 8).
        
        POST /api/v1/writer/content/generate_product/
        {
            "name": "Product Name",
            "description": "Product description",
            "features": ["Feature 1", "Feature 2"],
            "target_audience": "Target audience",
            "primary_keyword": "Primary keyword",
            "site_id": 1,  // optional
            "sector_id": 1  // optional
        }
        """
        from igny8_core.business.content.services.content_generation_service import ContentGenerationService
        from igny8_core.auth.models import Site, Sector
        
        account = getattr(request, 'account', None)
        if not account:
            return error_response(
                error='Account not found',
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request
            )
        
        product_data = request.data
        site_id = product_data.get('site_id')
        sector_id = product_data.get('sector_id')
        
        site = None
        sector = None
        
        if site_id:
            try:
                site = Site.objects.get(id=site_id, account=account)
            except Site.DoesNotExist:
                return error_response(
                    error='Site not found',
                    status_code=status.HTTP_404_NOT_FOUND,
                    request=request
                )
        
        if sector_id:
            try:
                sector = Sector.objects.get(id=sector_id, account=account)
            except Sector.DoesNotExist:
                return error_response(
                    error='Sector not found',
                    status_code=status.HTTP_404_NOT_FOUND,
                    request=request
                )
        
        service = ContentGenerationService()
        
        try:
            result = service.generate_product_content(
                product_data=product_data,
                account=account,
                site=site,
                sector=sector
            )
            
            if result.get('success'):
                return success_response(
                    data={'task_id': result.get('task_id')},
                    message=result.get('message', 'Product content generation started'),
                    request=request
                )
            else:
                return error_response(
                    error=result.get('error', 'Product content generation failed'),
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    request=request
                )
        except Exception as e:
            return error_response(
                error=str(e),
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                request=request
            )
    
    @action(detail=True, methods=['get'], url_path='validation', url_name='validation')
    def validation(self, request, pk=None):
        """
        Stage 3: Get validation checklist for content.
        
        GET /api/v1/writer/content/{id}/validation/
        Returns aggregated validation checklist for Writer UI.
        """
        content = self.get_object()
        validation_service = ContentValidationService()
        
        errors = validation_service.validate_content(content)
        publish_errors = validation_service.validate_for_publish(content)
        
        return success_response(
            data={
                'content_id': content.id,
                'is_valid': len(errors) == 0,
                'ready_to_publish': len(publish_errors) == 0,
                'validation_errors': errors,
                'publish_errors': publish_errors,
                'metadata': {
                    'has_entity_type': bool(content.content_type),
                    'entity_type': content.content_type,
                    'has_cluster_mapping': self._has_cluster_mapping(content),
                    'has_taxonomy_mapping': self._has_taxonomy_mapping(content),
                }
            },
            request=request
        )
    
    @action(detail=True, methods=['post'], url_path='validate', url_name='validate')
    def validate(self, request, pk=None):
        """
        Stage 3: Re-run validators and return actionable errors.
        
        POST /api/v1/writer/content/{id}/validate/
        Re-validates content and returns structured errors.
        """
        content = self.get_object()
        validation_service = ContentValidationService()
        
        # Persist metadata mappings if task exists
        # Metadata is now persisted directly on content - no task linkage needed
            # mapping_service = MetadataMappingService()  # DEPRECATED
            # mapping_service.persist_task_metadata_to_content(content)  # DEPRECATED
        
        errors = validation_service.validate_for_publish(content)
        
        return success_response(
            data={
                'content_id': content.id,
                'is_valid': len(errors) == 0,
                'errors': errors,
            },
            request=request
        )
    
    def _has_cluster_mapping(self, content):
        """Helper to check if content has cluster mapping"""
        from igny8_core.business.content.models import ContentClusterMap
        return ContentClusterMap.objects.filter(content=content).exists()
    
    def _has_taxonomy_mapping(self, content):
        """Helper to check if content has taxonomy mapping"""
        from igny8_core.business.content.models import ContentTaxonomyMap
        return ContentTaxonomyMap.objects.filter(content=content).exists()
    
    @action(detail=False, methods=['post'], url_path='generate_service', url_name='generate_service')
    def generate_service(self, request):
        """
        Generate service page content (Phase 8).
        
        POST /api/v1/writer/content/generate_service/
        {
            "name": "Service Name",
            "description": "Service description",
            "benefits": ["Benefit 1", "Benefit 2"],
            "target_audience": "Target audience",
            "primary_keyword": "Primary keyword",
            "site_id": 1,  // optional
            "sector_id": 1  // optional
        }
        """
        from igny8_core.business.content.services.content_generation_service import ContentGenerationService
        from igny8_core.auth.models import Site, Sector
        
        account = getattr(request, 'account', None)
        if not account:
            return error_response(
                error='Account not found',
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request
            )
        
        service_data = request.data
        site_id = service_data.get('site_id')
        sector_id = service_data.get('sector_id')
        
        site = None
        sector = None
        
        if site_id:
            try:
                site = Site.objects.get(id=site_id, account=account)
            except Site.DoesNotExist:
                return error_response(
                    error='Site not found',
                    status_code=status.HTTP_404_NOT_FOUND,
                    request=request
                )
        
        if sector_id:
            try:
                sector = Sector.objects.get(id=sector_id, account=account)
            except Sector.DoesNotExist:
                return error_response(
                    error='Sector not found',
                    status_code=status.HTTP_404_NOT_FOUND,
                    request=request
                )
        
        service = ContentGenerationService()
        
        try:
            result = service.generate_service_page(
                service_data=service_data,
                account=account,
                site=site,
                sector=sector
            )
            
            if result.get('success'):
                return success_response(
                    data={'task_id': result.get('task_id')},
                    message=result.get('message', 'Service page generation started'),
                    request=request
                )
            else:
                return error_response(
                    error=result.get('error', 'Service page generation failed'),
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    request=request
                )
        except Exception as e:
            return error_response(
                error=str(e),
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                request=request
            )
    
    @action(detail=True, methods=['get'], url_path='validation', url_name='validation')
    def validation(self, request, pk=None):
        """
        Stage 3: Get validation checklist for content.
        
        GET /api/v1/writer/content/{id}/validation/
        Returns aggregated validation checklist for Writer UI.
        """
        content = self.get_object()
        validation_service = ContentValidationService()
        
        errors = validation_service.validate_content(content)
        publish_errors = validation_service.validate_for_publish(content)
        
        return success_response(
            data={
                'content_id': content.id,
                'is_valid': len(errors) == 0,
                'ready_to_publish': len(publish_errors) == 0,
                'validation_errors': errors,
                'publish_errors': publish_errors,
                'metadata': {
                    'has_entity_type': bool(content.content_type),
                    'entity_type': content.content_type,
                    'has_cluster_mapping': self._has_cluster_mapping(content),
                    'has_taxonomy_mapping': self._has_taxonomy_mapping(content),
                }
            },
            request=request
        )
    
    @action(detail=True, methods=['post'], url_path='validate', url_name='validate')
    def validate(self, request, pk=None):
        """
        Stage 3: Re-run validators and return actionable errors.
        
        POST /api/v1/writer/content/{id}/validate/
        Re-validates content and returns structured errors.
        """
        content = self.get_object()
        validation_service = ContentValidationService()
        
        # Persist metadata mappings if task exists
        # Metadata is now persisted directly on content - no task linkage needed
            # mapping_service = MetadataMappingService()  # DEPRECATED
            # mapping_service.persist_task_metadata_to_content(content)  # DEPRECATED
        
        errors = validation_service.validate_for_publish(content)
        
        return success_response(
            data={
                'content_id': content.id,
                'is_valid': len(errors) == 0,
                'errors': errors,
            },
            request=request
        )
    
    def _has_cluster_mapping(self, content):
        """Helper to check if content has cluster mapping"""
        from igny8_core.business.content.models import ContentClusterMap
        return ContentClusterMap.objects.filter(content=content).exists()
    
    def _has_taxonomy_mapping(self, content):
        """Helper to check if content has taxonomy mapping"""
        from igny8_core.business.content.models import ContentTaxonomyMap
        return ContentTaxonomyMap.objects.filter(content=content).exists()
    
    @action(detail=False, methods=['post'], url_path='generate_taxonomy', url_name='generate_taxonomy')
    def generate_taxonomy(self, request):
        """
        Generate taxonomy page content (Phase 8).
        
        POST /api/v1/writer/content/generate_taxonomy/
        {
            "name": "Taxonomy Name",
            "description": "Taxonomy description",
            "items": ["Item 1", "Item 2"],
            "primary_keyword": "Primary keyword",
            "site_id": 1,  // optional
            "sector_id": 1  // optional
        }
        """
        from igny8_core.business.content.services.content_generation_service import ContentGenerationService
        from igny8_core.auth.models import Site, Sector
        
        account = getattr(request, 'account', None)
        if not account:
            return error_response(
                error='Account not found',
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request
            )
        
        taxonomy_data = request.data
        site_id = taxonomy_data.get('site_id')
        sector_id = taxonomy_data.get('sector_id')
        
        site = None
        sector = None
        
        if site_id:
            try:
                site = Site.objects.get(id=site_id, account=account)
            except Site.DoesNotExist:
                return error_response(
                    error='Site not found',
                    status_code=status.HTTP_404_NOT_FOUND,
                    request=request
                )
        
        if sector_id:
            try:
                sector = Sector.objects.get(id=sector_id, account=account)
            except Sector.DoesNotExist:
                return error_response(
                    error='Sector not found',
                    status_code=status.HTTP_404_NOT_FOUND,
                    request=request
                )
        
        service = ContentGenerationService()
        
        try:
            result = service.generate_taxonomy(
                taxonomy_data=taxonomy_data,
                account=account,
                site=site,
                sector=sector
            )
            
            if result.get('success'):
                return success_response(
                    data={'task_id': result.get('task_id')},
                    message=result.get('message', 'Taxonomy generation started'),
                    request=request
                )
            else:
                return error_response(
                    error=result.get('error', 'Taxonomy generation failed'),
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    request=request
                )
        except Exception as e:
            return error_response(
                error=str(e),
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                request=request
            )
    
    @action(detail=True, methods=['get'], url_path='validation', url_name='validation')
    def validation(self, request, pk=None):
        """
        Stage 3: Get validation checklist for content.
        
        GET /api/v1/writer/content/{id}/validation/
        Returns aggregated validation checklist for Writer UI.
        """
        content = self.get_object()
        validation_service = ContentValidationService()
        
        errors = validation_service.validate_content(content)
        publish_errors = validation_service.validate_for_publish(content)
        
        return success_response(
            data={
                'content_id': content.id,
                'is_valid': len(errors) == 0,
                'ready_to_publish': len(publish_errors) == 0,
                'validation_errors': errors,
                'publish_errors': publish_errors,
                'metadata': {
                    'has_entity_type': bool(content.content_type),
                    'entity_type': content.content_type,
                    'has_cluster_mapping': self._has_cluster_mapping(content),
                    'has_taxonomy_mapping': self._has_taxonomy_mapping(content),
                }
            },
            request=request
        )
    
    @action(detail=True, methods=['post'], url_path='validate', url_name='validate')
    def validate(self, request, pk=None):
        """
        Stage 3: Re-run validators and return actionable errors.
        
        POST /api/v1/writer/content/{id}/validate/
        Re-validates content and returns structured errors.
        """
        content = self.get_object()
        validation_service = ContentValidationService()
        
        # Persist metadata mappings if task exists
        # Metadata is now persisted directly on content - no task linkage needed
            # mapping_service = MetadataMappingService()  # DEPRECATED
            # mapping_service.persist_task_metadata_to_content(content)  # DEPRECATED
        
        errors = validation_service.validate_for_publish(content)
        
        return success_response(
            data={
                'content_id': content.id,
                'is_valid': len(errors) == 0,
                'errors': errors,
            },
            request=request
        )
    
    def _has_cluster_mapping(self, content):
        """Helper to check if content has cluster mapping"""
        from igny8_core.business.content.models import ContentClusterMap
        return ContentClusterMap.objects.filter(content=content).exists()
    
    def _has_taxonomy_mapping(self, content):
        """Helper to check if content has taxonomy mapping"""
        # Check new M2M relationship
        return content.taxonomy_terms.exists()


@extend_schema_view(
    list=extend_schema(tags=['Writer']),
    create=extend_schema(tags=['Writer']),
    retrieve=extend_schema(tags=['Writer']),
    update=extend_schema(tags=['Writer']),
    partial_update=extend_schema(tags=['Writer']),
    destroy=extend_schema(tags=['Writer']),
)
class ContentTaxonomyViewSet(SiteSectorModelViewSet):
    """
    ViewSet for managing content taxonomies (categories, tags, product attributes)
    Unified API Standard v1.0 compliant
    """
    queryset = ContentTaxonomy.objects.select_related('parent', 'site', 'sector').prefetch_related('clusters', 'contents')
    serializer_class = ContentTaxonomySerializer
    permission_classes = [IsAuthenticatedAndActive, IsViewerOrAbove]
    pagination_class = CustomPageNumberPagination
    throttle_scope = 'writer'
    throttle_classes = [DebugScopedRateThrottle]
    
    # DRF filtering configuration
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    
    # Search configuration
    search_fields = ['name', 'slug', 'description', 'external_taxonomy']
    
    # Ordering configuration
    ordering_fields = ['name', 'taxonomy_type', 'count', 'created_at']
    ordering = ['taxonomy_type', 'name']
    
    # Filter configuration
    # Removed "parent" to avoid non-model field in filterset (breaks drf-spectacular)
    filterset_fields = ['taxonomy_type', 'sync_status', 'external_id', 'external_taxonomy']
    
    def perform_create(self, serializer):
        """Create taxonomy with site/sector context"""
        user = getattr(self.request, 'user', None)
        
        try:
            query_params = getattr(self.request, 'query_params', None)
            if query_params is None:
                query_params = getattr(self.request, 'GET', {})
        except AttributeError:
            query_params = {}
        
        site_id = serializer.validated_data.get('site_id') or query_params.get('site_id')
        sector_id = serializer.validated_data.get('sector_id') or query_params.get('sector_id')
        
        from igny8_core.auth.models import Site, Sector
        from rest_framework.exceptions import ValidationError
        
        if not site_id:
            raise ValidationError("site_id is required")
        
        try:
            site = Site.objects.get(id=site_id)
        except Site.DoesNotExist:
            raise ValidationError(f"Site with id {site_id} does not exist")
        
        if not sector_id:
            raise ValidationError("sector_id is required")
        
        try:
            sector = Sector.objects.get(id=sector_id)
            if sector.site_id != site_id:
                raise ValidationError(f"Sector does not belong to the selected site")
        except Sector.DoesNotExist:
            raise ValidationError(f"Sector with id {sector_id} does not exist")
        
        serializer.validated_data.pop('site_id', None)
        serializer.validated_data.pop('sector_id', None)
        
        account = getattr(self.request, 'account', None)
        if not account and user and user.is_authenticated and user.account:
            account = user.account
        if not account:
            account = site.account
        
        serializer.save(account=account, site=site, sector=sector)
    
    @action(detail=True, methods=['post'], permission_classes=[IsAuthenticatedAndActive, IsEditorOrAbove])
    def map_to_cluster(self, request, pk=None):
        """Map taxonomy to semantic cluster"""
        taxonomy = self.get_object()
        cluster_id = request.data.get('cluster_id')
        
        if not cluster_id:
            return error_response(
                error="cluster_id is required",
                status_code=status.HTTP_400_BAD_REQUEST,
                request=request
            )
        
        from igny8_core.business.planning.models import Clusters
        try:
            cluster = Clusters.objects.get(id=cluster_id, site=taxonomy.site)
            taxonomy.clusters.add(cluster)
            
            return success_response(
                data={'message': f'Taxonomy "{taxonomy.name}" mapped to cluster "{cluster.name}"'},
                message="Taxonomy mapped to cluster successfully",
                request=request
            )
        except Clusters.DoesNotExist:
            return error_response(
                error=f"Cluster with id {cluster_id} not found",
                status_code=status.HTTP_404_NOT_FOUND,
                request=request
            )
    
    @action(detail=True, methods=['get'])
    def contents(self, request, pk=None):
        """Get all content associated with this taxonomy"""
        taxonomy = self.get_object()
        contents = taxonomy.contents.all()
        
        serializer = ContentSerializer(contents, many=True, context={'request': request})
        
        return success_response(
            data=serializer.data,
            message=f"Found {contents.count()} content items for taxonomy '{taxonomy.name}'",
            request=request
        )


# ContentAttributeViewSet temporarily disabled - ContentAttributeSerializer was removed in Stage 1
# TODO: Re-implement or remove completely based on Stage 1 architecture decisions


