"""
Media services for photo upload, management, and CloudFlare Images integration.
"""

import logging
import mimetypes
import os
from io import BytesIO
from typing import Optional, Dict, Any, List
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import InMemoryUploadedFile, TemporaryUploadedFile
from django.db import transaction
from django.db.models import Model

import requests
from PIL import Image

from apps.media.models import Photo

logger = logging.getLogger(__name__)


class CloudFlareError(Exception):
    """Base exception for CloudFlare API errors."""
    pass


class CloudFlareService:
    """
    Service for interacting with CloudFlare Images API.
    
    Provides image upload, deletion, and URL generation with automatic
    fallback to mock mode when CloudFlare credentials are not configured.
    """
    
    def __init__(self):
        self.account_id = settings.CLOUDFLARE_ACCOUNT_ID
        self.api_token = settings.CLOUDFLARE_IMAGE_TOKEN
        self.delivery_hash = settings.CLOUDFLARE_IMAGE_HASH
        
        # Enable mock mode if CloudFlare is not configured
        self.mock_mode = not all([self.account_id, self.api_token, self.delivery_hash])
        
        if self.mock_mode:
            logger.warning("CloudFlare Images not configured - using mock mode")
        
        self.base_url = f"https://api.cloudflare.com/client/v4/accounts/{self.account_id}/images/v1"
        self.headers = {"Authorization": f"Bearer {self.api_token}"}
    
    def upload_image(
        self,
        file: InMemoryUploadedFile | TemporaryUploadedFile,
        metadata: Optional[Dict[str, str]] = None
    ) -> Dict[str, Any]:
        """
        Upload an image to CloudFlare Images.
        
        Args:
            file: The uploaded file object
            metadata: Optional metadata dictionary
            
        Returns:
            Dict containing:
                - id: CloudFlare image ID
                - url: CDN URL for the image
                - variants: Available image variants
                
        Raises:
            CloudFlareError: If upload fails
        """
        if self.mock_mode:
            return self._mock_upload(file, metadata)
        
        try:
            # Prepare the file for upload
            file.seek(0)  # Reset file pointer
            
            # Prepare multipart form data
            files = {
                'file': (file.name, file.read(), file.content_type)
            }
            
            # Add metadata if provided
            data = {}
            if metadata:
                data['metadata'] = str(metadata)
            
            # Make API request
            response = requests.post(
                self.base_url,
                headers=self.headers,
                files=files,
                data=data,
                timeout=30
            )
            
            response.raise_for_status()
            result = response.json()
            
            if not result.get('success'):
                raise CloudFlareError(f"Upload failed: {result.get('errors', [])}")
            
            image_data = result['result']
            
            return {
                'id': image_data['id'],
                'url': self._get_cdn_url(image_data['id']),
                'variants': image_data.get('variants', []),
                'uploaded': image_data.get('uploaded'),
            }
            
        except requests.exceptions.RequestException as e:
            logger.error(f"CloudFlare upload failed: {str(e)}")
            raise CloudFlareError(f"Failed to upload image: {str(e)}")
    
    def delete_image(self, image_id: str) -> bool:
        """
        Delete an image from CloudFlare Images.
        
        Args:
            image_id: The CloudFlare image ID
            
        Returns:
            True if deletion was successful
            
        Raises:
            CloudFlareError: If deletion fails
        """
        if self.mock_mode:
            return self._mock_delete(image_id)
        
        try:
            url = f"{self.base_url}/{image_id}"
            response = requests.delete(
                url,
                headers=self.headers,
                timeout=30
            )
            
            response.raise_for_status()
            result = response.json()
            
            return result.get('success', False)
            
        except requests.exceptions.RequestException as e:
            logger.error(f"CloudFlare deletion failed: {str(e)}")
            raise CloudFlareError(f"Failed to delete image: {str(e)}")
    
    def get_image_url(self, image_id: str, variant: str = "public") -> str:
        """
        Generate a CloudFlare CDN URL for an image.
        
        Args:
            image_id: The CloudFlare image ID
            variant: Image variant (public, thumbnail, banner, etc.)
            
        Returns:
            CDN URL for the image
        """
        if self.mock_mode:
            return self._mock_url(image_id, variant)
        
        return self._get_cdn_url(image_id, variant)
    
    def get_image_variants(self, image_id: str) -> List[str]:
        """
        Get available variants for an image.
        
        Args:
            image_id: The CloudFlare image ID
            
        Returns:
            List of available variant names
        """
        if self.mock_mode:
            return ['public', 'thumbnail', 'banner']
        
        try:
            url = f"{self.base_url}/{image_id}"
            response = requests.get(
                url,
                headers=self.headers,
                timeout=30
            )
            
            response.raise_for_status()
            result = response.json()
            
            if result.get('success'):
                return list(result['result'].get('variants', []))
            
            return []
            
        except requests.exceptions.RequestException as e:
            logger.error(f"Failed to get variants: {str(e)}")
            return []
    
    def _get_cdn_url(self, image_id: str, variant: str = "public") -> str:
        """Generate CloudFlare CDN URL."""
        return f"https://imagedelivery.net/{self.delivery_hash}/{image_id}/{variant}"
    
    # Mock methods for development without CloudFlare
    
    def _mock_upload(self, file, metadata) -> Dict[str, Any]:
        """Mock upload for development."""
        import uuid
        mock_id = str(uuid.uuid4())
        
        logger.info(f"[MOCK] Uploaded image: {file.name} -> {mock_id}")
        
        return {
            'id': mock_id,
            'url': self._mock_url(mock_id),
            'variants': ['public', 'thumbnail', 'banner'],
            'uploaded': 'mock',
        }
    
    def _mock_delete(self, image_id: str) -> bool:
        """Mock deletion for development."""
        logger.info(f"[MOCK] Deleted image: {image_id}")
        return True
    
    def _mock_url(self, image_id: str, variant: str = "public") -> str:
        """Generate mock URL for development."""
        return f"https://placehold.co/800x600/png?text={image_id[:8]}"


class PhotoService:
    """
    Service for managing Photo objects with CloudFlare integration.
    
    Handles photo creation, attachment to entities, moderation,
    and gallery management.
    """
    
    def __init__(self):
        self.cloudflare = CloudFlareService()
    
    def create_photo(
        self,
        file: InMemoryUploadedFile | TemporaryUploadedFile,
        user,
        entity: Optional[Model] = None,
        photo_type: str = "gallery",
        title: str = "",
        description: str = "",
        credit: str = "",
        is_visible: bool = True,
    ) -> Photo:
        """
        Create a new photo with CloudFlare upload.
        
        Args:
            file: Uploaded file object
            user: User uploading the photo
            entity: Optional entity to attach photo to
            photo_type: Type of photo (main, gallery, banner, etc.)
            title: Photo title
            description: Photo description
            credit: Photo credit/attribution
            is_visible: Whether photo is visible
            
        Returns:
            Created Photo instance
            
        Raises:
            ValidationError: If validation fails
            CloudFlareError: If upload fails
        """
        # Get image dimensions
        dimensions = self._get_image_dimensions(file)
        
        # Upload to CloudFlare
        upload_result = self.cloudflare.upload_image(
            file,
            metadata={
                'uploaded_by': str(user.id),
                'photo_type': photo_type,
            }
        )
        
        # Create Photo instance
        with transaction.atomic():
            photo = Photo.objects.create(
                cloudflare_image_id=upload_result['id'],
                cloudflare_url=upload_result['url'],
                uploaded_by=user,
                photo_type=photo_type,
                title=title or file.name,
                description=description,
                credit=credit,
                width=dimensions['width'],
                height=dimensions['height'],
                file_size=file.size,
                mime_type=file.content_type,
                is_visible=is_visible,
                moderation_status='pending',
            )
            
            # Attach to entity if provided
            if entity:
                self.attach_to_entity(photo, entity)
            
            logger.info(f"Photo created: {photo.id} by user {user.id}")
            
            # Trigger async post-processing
            try:
                from apps.media.tasks import process_uploaded_image
                process_uploaded_image.delay(photo.id)
            except Exception as e:
                # Don't fail the upload if async task fails to queue
                logger.warning(f"Failed to queue photo processing task: {str(e)}")
            
            return photo
    
    def attach_to_entity(self, photo: Photo, entity: Model) -> None:
        """
        Attach a photo to an entity.
        
        Args:
            photo: Photo instance
            entity: Entity to attach to (Park, Ride, Company, etc.)
        """
        content_type = ContentType.objects.get_for_model(entity)
        photo.content_type = content_type
        photo.object_id = entity.pk
        photo.save(update_fields=['content_type', 'object_id'])
        
        logger.info(f"Photo {photo.id} attached to {content_type.model} {entity.pk}")
    
    def detach_from_entity(self, photo: Photo) -> None:
        """
        Detach a photo from its entity.
        
        Args:
            photo: Photo instance
        """
        photo.content_type = None
        photo.object_id = None
        photo.save(update_fields=['content_type', 'object_id'])
        
        logger.info(f"Photo {photo.id} detached from entity")
    
    def moderate_photo(
        self,
        photo: Photo,
        status: str,
        moderator,
        notes: str = ""
    ) -> Photo:
        """
        Moderate a photo (approve/reject/flag).
        
        Args:
            photo: Photo instance
            status: New status (approved, rejected, flagged)
            moderator: User performing moderation
            notes: Moderation notes
            
        Returns:
            Updated Photo instance
        """
        with transaction.atomic():
            photo.moderation_status = status
            photo.moderated_by = moderator
            photo.moderation_notes = notes
            
            if status == 'approved':
                photo.approve()
            elif status == 'rejected':
                photo.reject()
            elif status == 'flagged':
                photo.flag()
            
            photo.save()
            
            logger.info(
                f"Photo {photo.id} moderated: {status} by user {moderator.id}"
            )
            
            return photo
    
    def reorder_photos(
        self,
        entity: Model,
        photo_ids: List[int],
        photo_type: Optional[str] = None
    ) -> None:
        """
        Reorder photos for an entity.
        
        Args:
            entity: Entity whose photos to reorder
            photo_ids: List of photo IDs in desired order
            photo_type: Optional photo type filter
        """
        content_type = ContentType.objects.get_for_model(entity)
        
        with transaction.atomic():
            for order, photo_id in enumerate(photo_ids):
                filters = {
                    'id': photo_id,
                    'content_type': content_type,
                    'object_id': entity.pk,
                }
                
                if photo_type:
                    filters['photo_type'] = photo_type
                
                Photo.objects.filter(**filters).update(display_order=order)
        
        logger.info(f"Reordered {len(photo_ids)} photos for {content_type.model} {entity.pk}")
    
    def get_entity_photos(
        self,
        entity: Model,
        photo_type: Optional[str] = None,
        approved_only: bool = True
    ) -> List[Photo]:
        """
        Get photos for an entity.
        
        Args:
            entity: Entity to get photos for
            photo_type: Optional photo type filter
            approved_only: Whether to return only approved photos
            
        Returns:
            List of Photo instances ordered by display_order
        """
        content_type = ContentType.objects.get_for_model(entity)
        
        queryset = Photo.objects.filter(
            content_type=content_type,
            object_id=entity.pk,
        )
        
        if photo_type:
            queryset = queryset.filter(photo_type=photo_type)
        
        if approved_only:
            queryset = queryset.approved()
        
        return list(queryset.order_by('display_order', '-created_at'))
    
    def delete_photo(self, photo: Photo, delete_from_cloudflare: bool = True) -> None:
        """
        Delete a photo.
        
        Args:
            photo: Photo instance to delete
            delete_from_cloudflare: Whether to also delete from CloudFlare
        """
        cloudflare_id = photo.cloudflare_image_id
        
        with transaction.atomic():
            photo.delete()
        
        # Delete from CloudFlare after DB deletion succeeds
        if delete_from_cloudflare and cloudflare_id:
            try:
                self.cloudflare.delete_image(cloudflare_id)
            except CloudFlareError as e:
                logger.error(f"Failed to delete from CloudFlare: {str(e)}")
                # Don't raise - photo is already deleted from DB
        
        logger.info(f"Photo deleted: {cloudflare_id}")
    
    def _get_image_dimensions(
        self,
        file: InMemoryUploadedFile | TemporaryUploadedFile
    ) -> Dict[str, int]:
        """
        Extract image dimensions from uploaded file.
        
        Args:
            file: Uploaded file object
            
        Returns:
            Dict with 'width' and 'height' keys
        """
        try:
            file.seek(0)
            image = Image.open(file)
            width, height = image.size
            file.seek(0)  # Reset for later use
            
            return {'width': width, 'height': height}
        except Exception as e:
            logger.warning(f"Failed to get image dimensions: {str(e)}")
            return {'width': 0, 'height': 0}
