"""
Reusable serializer mixins for common functionality.

This module provides mixins that can be used across different serializers
to share common functionality like image handling, validation, and storage operations.
"""

from typing import Optional

from rest_framework import serializers

from ..storage import StorageFactory
from ..storage.exceptions import StorageError
from ..validators.image_validator import ImageValidator


class ReviewImageMixin:
    """
    Mixin for handling review image uploads with storage abstraction.
    
    This mixin provides common functionality for review image handling that
    can be used across different review serializers (Property, Product, Service,
    Job, Company reviews). It encapsulates storage operations, validation,
    and URL generation in a reusable way.
    
    Usage:
        class PropertyReviewSerializer(ReviewImageMixin, serializers.ModelSerializer):
            # Your serializer implementation
            pass
    
    The mixin expects the serializer to have:
    - A 'review_image' field in the model
    - A 'review_image_url' SerializerMethodField (optional)
    
    Configuration:
    - Override `get_image_folder_path()` to customize the storage folder
    - Override `get_image_validator()` to customize validation rules
    """
    
    def __init__(self, *args, **kwargs):
        """
        Initialize mixin with storage backend and image validator.
        
        This method should be called by the inheriting serializer's __init__.
        If the serializer already has an __init__, make sure to call super().__init__.
        """
        super().__init__(*args, **kwargs)
        
        # Initialize storage backend and validator if not already set
        if not hasattr(self, 'storage_backend'):
            self.storage_backend = StorageFactory.get_storage_backend()
        
        if not hasattr(self, 'image_validator'):
            self.image_validator = self.get_image_validator()
    
    def get_image_validator(self) -> ImageValidator:
        """
        Get image validator instance with default configuration.
        
        Override this method in subclasses to customize validation rules
        for specific review types.
        
        Returns:
            ImageValidator: Configured validator instance
        """
        return ImageValidator(
            max_size_bytes=5 * 1024 * 1024,  # 5MB
            allowed_types=['image/jpeg', 'image/jpg', 'image/png', 'image/webp']
        )
    
    def get_image_folder_path(self) -> str:
        """
        Get the folder path for storing images.
        
        Override this method in subclasses to customize the storage path
        for different review types.
        
        Returns:
            str: Folder path (e.g., "reviews/property", "reviews/product")
        """
        return "reviews/generic"
    
    def validate_review_image(self, value):
        """
        Validate review image using ImageValidator.
        
        This method provides enhanced validation with better error messages.
        It can be used directly as a field validator in serializers.
        
        Args:
            value: Uploaded file object
            
        Returns:
            The validated file object
            
        Raises:
            serializers.ValidationError: If validation fails
        """
        if not value:
            return value
        
        try:
            self.image_validator.validate(value)
            return value
        except serializers.ValidationError:
            # Re-raise validation errors from ImageValidator
            raise
        except Exception as e:
            raise serializers.ValidationError(f"Image validation failed: {str(e)}")
    
    def get_review_image_url(self, obj):
        """
        Get review image URL using storage backend.
        
        This method works with both filesystem and Cloudinary storage,
        returning the appropriate URL for the configured backend.
        
        Args:
            obj: Model instance with review_image field
            
        Returns:
            str or None: Image URL or None if no image
        """
        if not obj.review_image:
            return None
        
        try:
            # Use storage backend to get URL
            url = self.storage_backend.get_url(str(obj.review_image))
            
            # For filesystem storage, we need to build absolute URI
            if StorageFactory.is_filesystem_backend():
                request = self.context.get("request")
                return request.build_absolute_uri(url) if request else url
            
            # For Cloudinary, URL is already absolute
            return url
            
        except Exception:
            # Fallback to legacy behavior if storage backend fails
            request = self.context.get("request")
            url = obj.review_image.url if hasattr(obj.review_image, 'url') else str(obj.review_image)
            return request.build_absolute_uri(url) if request else url
    
    def _handle_image_upload(self, validated_data: dict) -> Optional[str]:
        """
        Handle image upload using storage backend.
        
        This is a helper method for create/update operations that handles
        the image upload process and error handling.
        
        Args:
            validated_data: Dictionary containing validated data including 'review_image'
            
        Returns:
            str or None: Storage identifier for uploaded image, or None if no image
            
        Raises:
            serializers.ValidationError: If upload fails
        """
        review_image = validated_data.get("review_image")
        
        if not review_image:
            return None
        
        try:
            # Upload using storage backend
            folder_path = self.get_image_folder_path()
            identifier = self.storage_backend.upload(review_image, folder_path)
            return identifier
            
        except StorageError as e:
            raise serializers.ValidationError({
                "review_image": f"Failed to upload image: {str(e)}"
            })
        except Exception as e:
            raise serializers.ValidationError({
                "review_image": f"Unexpected error during image upload: {str(e)}"
            })
    
    def _handle_image_delete(self, identifier: str) -> None:
        """
        Handle image deletion from storage.
        
        This is a helper method for update operations that handles
        deletion of old images when a new image is uploaded.
        
        Args:
            identifier: Storage identifier of image to delete
        """
        if not identifier:
            return
        
        try:
            self.storage_backend.delete(identifier)
        except Exception:
            # Log but don't fail the operation if deletion fails
            # The new image upload was successful, so we don't want to
            # roll back the entire operation due to cleanup failure
            pass
    
    def handle_image_create(self, validated_data: dict) -> dict:
        """
        Handle image upload during create operations.
        
        This method should be called from the serializer's create() method
        to handle image upload and update the validated_data.
        
        Args:
            validated_data: Dictionary containing validated data
            
        Returns:
            dict: Updated validated_data with image identifier
            
        Example:
            def create(self, validated_data):
                validated_data = self.handle_image_create(validated_data)
                return super().create(validated_data)
        """
        identifier = self._handle_image_upload(validated_data)
        if identifier:
            validated_data["review_image"] = identifier
        
        return validated_data
    
    def handle_image_update(self, instance, validated_data: dict) -> dict:
        """
        Handle image upload during update operations.
        
        This method should be called from the serializer's update() method
        to handle image upload, deletion of old images, and update the validated_data.
        
        Args:
            instance: Model instance being updated
            validated_data: Dictionary containing validated data
            
        Returns:
            dict: Updated validated_data with image identifier
            
        Example:
            def update(self, instance, validated_data):
                validated_data = self.handle_image_update(instance, validated_data)
                return super().update(instance, validated_data)
        """
        review_image = validated_data.get("review_image")
        
        if review_image:
            # Get old image identifier for cleanup
            old_image_identifier = str(instance.review_image) if instance.review_image else None
            
            # Upload new image
            identifier = self._handle_image_upload(validated_data)
            if identifier:
                validated_data["review_image"] = identifier
                
                # Delete old image if upload was successful
                if old_image_identifier:
                    self._handle_image_delete(old_image_identifier)
        
        return validated_data