from api.property_api.property.models.property import PropertyReviews

from rest_framework import serializers
from common.utils.review_images import validate_review_image_file
from common.storage import StorageFactory
from common.storage.exceptions import StorageError
from common.validators.image_validator import ImageValidator
from common.validators.video_validator import VideoValidator
import logging
import threading

logger = logging.getLogger('property_reviews_serializer')

class PropertyReviewsSerializer(serializers.ModelSerializer):
    # Override model CharField fields to accept File objects on write operations
    review_image = serializers.FileField(write_only=True, required=False, allow_null=True)
    review_video = serializers.FileField(write_only=True, required=False, allow_null=True)
    
    # Read-only fields for URL generation
    review_image_url = serializers.SerializerMethodField(read_only=True)
    review_video_url = serializers.SerializerMethodField(read_only=True)
    review_video_streaming_url = serializers.SerializerMethodField(read_only=True)
    review_video_thumbnail_url = serializers.SerializerMethodField(read_only=True)
    media_type = serializers.SerializerMethodField(read_only=True)

    def __init__(self, *args, **kwargs):
        """
        Initialize serializer with storage backend and validators.
        """
        super().__init__(*args, **kwargs)
        self.storage_backend = StorageFactory.get_storage_backend()
        self.image_validator = ImageValidator(
            max_size_bytes=5 * 1024 * 1024,  # 5MB
            allowed_types=['image/jpeg', 'image/jpg', 'image/png', 'image/webp']
        )
        self.video_validator = VideoValidator(
            max_size_bytes=100 * 1024 * 1024,  # 100MB
            allowed_types=['video/mp4', 'video/quicktime', 'video/webm'],
            max_duration_seconds=300  # 5 minutes
        )

    class Meta:
        model = PropertyReviews
        fields = [
            'id',
            'review_star',
            'message',
            'review_image',
            'review_image_url',
            'review_video',
            'review_video_url',
            'review_video_streaming_url',
            'review_video_thumbnail_url',
            'video_duration',
            'media_type',
            'property',
            'is_active',
            'created_at',
            'updated_at'
        ]
        read_only_fields = [
            'id', 
            'created_at',
            'updated_at',
            'is_active',
            'video_duration'
        ]

    def validate(self, attrs):
        """
        Ensure only image OR video, not both.
        """
        request_id = getattr(threading.current_thread(), 'request_id', 'unknown')
        user_id = getattr(self.context.get('request', {}).get('user', {}), 'id', 'unknown')
        
        review_image = attrs.get('review_image')
        review_video = attrs.get('review_video')
        
        logger.info(
            f"Validating review data - Image: {bool(review_image)}, Video: {bool(review_video)}",
            extra={
                'user_id': user_id,
                'request_id': request_id,
                'has_image': bool(review_image),
                'has_video': bool(review_video),
                'validation_stage': 'media_type_check'
            }
        )
        
        if review_image and review_video:
            logger.warning(
                f"Validation failed: Both image and video provided",
                extra={
                    'user_id': user_id,
                    'request_id': request_id,
                    'error_type': 'both_media_types_provided'
                }
            )
            raise serializers.ValidationError(
                "Cannot upload both image and video. Please choose one."
            )
        
        logger.info(
            f"Media type validation passed",
            extra={
                'user_id': user_id,
                'request_id': request_id,
                'validation_stage': 'media_type_check_passed'
            }
        )
        
        return attrs

    def validate_review_image(self, value):
        """
        Validate review image using the new ImageValidator.
        
        This method provides enhanced validation with better error messages
        while maintaining backward compatibility.
        """
        if not value:
            return value
        
        request_id = getattr(threading.current_thread(), 'request_id', 'unknown')
        user_id = getattr(self.context.get('request', {}).get('user', {}), 'id', 'unknown')
        
        file_name = getattr(value, 'name', 'unknown')
        file_size = getattr(value, 'size', 0)
        content_type = getattr(value, 'content_type', 'unknown')
        
        logger.info(
            f"Validating review image: {file_name} ({file_size} bytes, {content_type})",
            extra={
                'user_id': user_id,
                'request_id': request_id,
                'file_name': file_name,
                'file_size_bytes': file_size,
                'file_size_mb': round(file_size / (1024 * 1024), 4),
                'content_type': content_type,
                'validation_stage': 'image_validation'
            }
        )
        
        try:
            # Use new validator for better validation
            self.image_validator.validate(value)
            
            logger.info(
                f"Image validation passed: {file_name}",
                extra={
                    'user_id': user_id,
                    'request_id': request_id,
                    'file_name': file_name,
                    'validation_stage': 'image_validation_passed'
                }
            )
            
            return value
        except serializers.ValidationError as e:
            logger.error(
                f"Image validation failed: {file_name} - {str(e)}",
                extra={
                    'user_id': user_id,
                    'request_id': request_id,
                    'file_name': file_name,
                    'error_type': 'image_validation_failed',
                    'error_message': str(e)
                }
            )
            # Re-raise validation errors from ImageValidator
            raise
        except Exception as e:
            logger.warning(
                f"Image validation error, falling back to legacy validator: {str(e)}",
                extra={
                    'user_id': user_id,
                    'request_id': request_id,
                    'file_name': file_name,
                    'error_type': 'validator_fallback',
                    'error_message': str(e)
                }
            )
            # Fallback to legacy validation for any unexpected issues
            return validate_review_image_file(value)

    def validate_review_video(self, value):
        """
        Validate review video using the VideoValidator.
        """
        if not value:
            return value
        
        request_id = getattr(threading.current_thread(), 'request_id', 'unknown')
        user_id = getattr(self.context.get('request', {}).get('user', {}), 'id', 'unknown')
        
        file_name = getattr(value, 'name', 'unknown')
        file_size = getattr(value, 'size', 0)
        content_type = getattr(value, 'content_type', 'unknown')
        
        logger.info(
            f"Validating review video: {file_name} ({file_size} bytes, {content_type})",
            extra={
                'user_id': user_id,
                'request_id': request_id,
                'file_name': file_name,
                'file_size_bytes': file_size,
                'file_size_mb': round(file_size / (1024 * 1024), 4),
                'content_type': content_type,
                'validation_stage': 'video_validation'
            }
        )
        
        try:
            # Use video validator
            self.video_validator.validate(value)
            
            logger.info(
                f"Video validation passed: {file_name}",
                extra={
                    'user_id': user_id,
                    'request_id': request_id,
                    'file_name': file_name,
                    'validation_stage': 'video_validation_passed'
                }
            )
            
            return value
        except serializers.ValidationError as e:
            logger.error(
                f"Video validation failed: {file_name} - {str(e)}",
                extra={
                    'user_id': user_id,
                    'request_id': request_id,
                    'file_name': file_name,
                    'error_type': 'video_validation_failed',
                    'error_message': str(e)
                }
            )
            # Re-raise validation errors from VideoValidator
            raise
        except Exception as e:
            logger.error(
                f"Unexpected video validation error: {file_name} - {str(e)}",
                extra={
                    'user_id': user_id,
                    'request_id': request_id,
                    'file_name': file_name,
                    'error_type': 'video_validation_exception',
                    'error_message': str(e)
                }
            )
            raise serializers.ValidationError(f"Video validation failed: {str(e)}")

    def get_media_type(self, obj):
        """Return 'image', 'video', or None"""
        if obj.review_video:
            return 'video'
        elif obj.review_image:
            return 'image'
        return None

    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.
        """
        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 get_review_video_url(self, obj):
        """Get progressive video URL"""
        if not obj.review_video:
            return None
        
        try:
            return self.storage_backend.get_video_url(str(obj.review_video))
        except Exception:
            return None
    
    def get_review_video_streaming_url(self, obj):
        """Get HLS streaming URL"""
        if not obj.review_video:
            return None
        
        try:
            return self.storage_backend.get_streaming_url(str(obj.review_video))
        except Exception:
            return None
    
    def get_review_video_thumbnail_url(self, obj):
        """Get video thumbnail URL"""
        if not obj.review_video:
            return None
        
        try:
            return self.storage_backend.get_thumbnail_url(str(obj.review_video))
        except Exception:
            return None

    def create(self, validated_data):
        """
        Create property review with image or video upload using storage backend.
        """
        request = self.context.get("request")
        request_id = getattr(threading.current_thread(), 'request_id', 'unknown')
        user_id = getattr(request.user if request else None, 'id', 'unknown')
        
        review_image = validated_data.get("review_image")
        review_video = validated_data.get("review_video")
        
        logger.info(
            f"Creating property review - Image: {bool(review_image)}, Video: {bool(review_video)}",
            extra={
                'user_id': user_id,
                'request_id': request_id,
                'has_image': bool(review_image),
                'has_video': bool(review_video),
                'operation': 'create_review'
            }
        )
        
        # Handle image upload
        if review_image:
            file_name = getattr(review_image, 'name', 'unknown')
            file_size = getattr(review_image, 'size', 0)
            
            logger.info(
                f"Starting image upload: {file_name} ({file_size} bytes)",
                extra={
                    'user_id': user_id,
                    'request_id': request_id,
                    'file_name': file_name,
                    'file_size_bytes': file_size,
                    'upload_type': 'image',
                    'operation': 'upload_start'
                }
            )
            
            try:
                # Upload using storage backend
                identifier = self.storage_backend.upload(review_image, "reviews/property")
                validated_data["review_image"] = identifier
                
                logger.info(
                    f"Image upload successful: {file_name} -> {identifier}",
                    extra={
                        'user_id': user_id,
                        'request_id': request_id,
                        'file_name': file_name,
                        'storage_identifier': identifier,
                        'upload_type': 'image',
                        'operation': 'upload_success'
                    }
                )
                
            except StorageError as e:
                logger.error(
                    f"Image upload failed (StorageError): {file_name} - {str(e)}",
                    extra={
                        'user_id': user_id,
                        'request_id': request_id,
                        'file_name': file_name,
                        'error_type': 'storage_error',
                        'error_message': str(e),
                        'upload_type': 'image'
                    }
                )
                raise serializers.ValidationError({
                    "review_image": f"Failed to upload image: {str(e)}"
                })
            except Exception as e:
                logger.error(
                    f"Image upload failed (Unexpected): {file_name} - {str(e)}",
                    extra={
                        'user_id': user_id,
                        'request_id': request_id,
                        'file_name': file_name,
                        'error_type': 'unexpected_error',
                        'error_message': str(e),
                        'upload_type': 'image'
                    }
                )
                raise serializers.ValidationError({
                    "review_image": f"Unexpected error during image upload: {str(e)}"
                })
        
        # Handle video upload
        if review_video:
            file_name = getattr(review_video, 'name', 'unknown')
            file_size = getattr(review_video, 'size', 0)
            
            logger.info(
                f"Starting video upload: {file_name} ({file_size} bytes)",
                extra={
                    'user_id': user_id,
                    'request_id': request_id,
                    'file_name': file_name,
                    'file_size_bytes': file_size,
                    'upload_type': 'video',
                    'operation': 'upload_start'
                }
            )
            
            try:
                # Upload video using storage backend
                identifier = self.storage_backend.upload_video(review_video, "reviews/property")
                validated_data["review_video"] = identifier
                
                logger.info(
                    f"Video upload successful: {file_name} -> {identifier}",
                    extra={
                        'user_id': user_id,
                        'request_id': request_id,
                        'file_name': file_name,
                        'storage_identifier': identifier,
                        'upload_type': 'video',
                        'operation': 'upload_success'
                    }
                )
                
                # Get video duration from Cloudinary
                try:
                    metadata = self.storage_backend.get_video_metadata(identifier)
                    duration = int(metadata.get('duration', 0))
                    validated_data["video_duration"] = duration
                    
                    logger.info(
                        f"Video metadata retrieved: duration={duration}s",
                        extra={
                            'user_id': user_id,
                            'request_id': request_id,
                            'storage_identifier': identifier,
                            'video_duration': duration,
                            'operation': 'metadata_retrieval'
                        }
                    )
                except Exception as e:
                    logger.warning(
                        f"Failed to retrieve video metadata: {str(e)}",
                        extra={
                            'user_id': user_id,
                            'request_id': request_id,
                            'storage_identifier': identifier,
                            'error_message': str(e),
                            'operation': 'metadata_retrieval_failed'
                        }
                    )
                    validated_data["video_duration"] = 0
                
            except StorageError as e:
                logger.error(
                    f"Video upload failed (StorageError): {file_name} - {str(e)}",
                    extra={
                        'user_id': user_id,
                        'request_id': request_id,
                        'file_name': file_name,
                        'error_type': 'storage_error',
                        'error_message': str(e),
                        'upload_type': 'video'
                    }
                )
                raise serializers.ValidationError({
                    "review_video": f"Failed to upload video: {str(e)}"
                })
            except Exception as e:
                logger.error(
                    f"Video upload failed (Unexpected): {file_name} - {str(e)}",
                    extra={
                        'user_id': user_id,
                        'request_id': request_id,
                        'file_name': file_name,
                        'error_type': 'unexpected_error',
                        'error_message': str(e),
                        'upload_type': 'video'
                    }
                )
                raise serializers.ValidationError({
                    "review_video": f"Unexpected error during video upload: {str(e)}"
                })
        
        if request and hasattr(request, "user"):
            validated_data["created_by"] = request.user
            
        logger.info(
            f"Creating review record in database",
            extra={
                'user_id': user_id,
                'request_id': request_id,
                'operation': 'database_save'
            }
        )
            
        try:
            review = super().create(validated_data)
            
            logger.info(
                f"Property review created successfully: ID={review.id}",
                extra={
                    'user_id': user_id,
                    'request_id': request_id,
                    'review_id': review.id,
                    'operation': 'create_success'
                }
            )
            
            return review
        except Exception as e:
            logger.error(
                f"Database save failed: {str(e)}",
                extra={
                    'user_id': user_id,
                    'request_id': request_id,
                    'error_type': 'database_error',
                    'error_message': str(e),
                    'operation': 'database_save_failed'
                }
            )
            raise

    def update(self, instance, validated_data):
        """
        Update property review with image or video upload using storage backend.
        
        If a new image is provided, the old image/video is deleted from storage.
        If a new video is provided, the old video/image is deleted from storage.
        """
        review_image = validated_data.get("review_image")
        review_video = validated_data.get("review_video")
        old_image_identifier = str(instance.review_image) if instance.review_image else None
        old_video_identifier = str(instance.review_video) if instance.review_video else None
        
        # Handle image upload
        if review_image:
            try:
                # Upload new image using storage backend
                identifier = self.storage_backend.upload(review_image, "reviews/property")
                validated_data["review_image"] = identifier
                
                # Clear video fields if replacing video with image
                if old_video_identifier:
                    validated_data["review_video"] = None
                    validated_data["video_duration"] = None
                    try:
                        self.storage_backend.delete(old_video_identifier)
                    except Exception:
                        # Log but don't fail the update if old video deletion fails
                        pass
                
                # Delete old image if it exists and upload was successful
                if old_image_identifier:
                    try:
                        self.storage_backend.delete(old_image_identifier)
                    except Exception:
                        # Log but don't fail the update if old image deletion fails
                        pass
                
            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)}"
                })
        
        # Handle video upload
        if review_video:
            try:
                # Upload new video using storage backend
                identifier = self.storage_backend.upload_video(review_video, "reviews/property")
                validated_data["review_video"] = identifier
                
                # Get video duration from Cloudinary
                metadata = self.storage_backend.get_video_metadata(identifier)
                validated_data["video_duration"] = int(metadata.get('duration', 0))
                
                # Clear image field if replacing image with video
                if old_image_identifier:
                    validated_data["review_image"] = None
                    try:
                        self.storage_backend.delete(old_image_identifier)
                    except Exception:
                        # Log but don't fail the update if old image deletion fails
                        pass
                
                # Delete old video if it exists and upload was successful
                if old_video_identifier:
                    try:
                        self.storage_backend.delete(old_video_identifier)
                    except Exception:
                        # Log but don't fail the update if old video deletion fails
                        pass
                
            except StorageError as e:
                raise serializers.ValidationError({
                    "review_video": f"Failed to upload video: {str(e)}"
                })
            except Exception as e:
                raise serializers.ValidationError({
                    "review_video": f"Unexpected error during video upload: {str(e)}"
                })
        
        return super().update(instance, validated_data)
