"""
Filesystem storage adapter implementation.

This module provides a concrete implementation of the ImageStorageAdapter
interface using Django's FileSystemStorage backend. This adapter handles
local file storage operations while maintaining the same interface as
cloud storage providers.
"""

import os
import uuid
from datetime import datetime
from typing import BinaryIO, Dict, Any

from django.core.files.storage import FileSystemStorage
from django.conf import settings

from .adapters import ImageStorageAdapter
from .exceptions import (
    StorageUploadError,
    StorageDeleteError,
    StorageNotFoundError
)


class FilesystemStorageAdapter(ImageStorageAdapter):
    """
    Filesystem storage implementation using Django's FileSystemStorage.
    
    This adapter provides local file storage capabilities while implementing
    the same interface as cloud storage providers. It's useful for development,
    testing, and deployments where local storage is preferred.
    
    The adapter automatically generates unique filenames to prevent conflicts
    and organizes files in a logical directory structure.
    """
    
    def __init__(self, base_path: str = "review_images"):
        """
        Initialize filesystem storage adapter.
        
        Args:
            base_path: Base directory path for storing images relative to MEDIA_ROOT
        """
        self.storage = FileSystemStorage()
        self.base_path = base_path.strip('/')
    
    def upload(self, file: BinaryIO, path: str, **kwargs) -> str:
        """
        Upload a file to the filesystem.
        
        This method saves the file to the local filesystem using Django's
        FileSystemStorage. It generates a unique filename to prevent conflicts
        and organizes files in the specified path structure.
        
        Args:
            file: Binary file object to upload
            path: Logical path for organization (e.g., "reviews/property")
            **kwargs: Additional options (ignored for filesystem storage)
            
        Returns:
            str: Relative file path that can be used with other methods
            
        Raises:
            StorageUploadError: If upload fails due to filesystem issues
        """
        try:
            # Generate unique filename
            timestamp = int(datetime.now().timestamp())
            unique_id = str(uuid.uuid4())[:8]
            
            # Get original filename and extension
            original_name = getattr(file, 'name', 'image')
            if '.' in original_name:
                name_part, ext = original_name.rsplit('.', 1)
                filename = f"{name_part}-{unique_id}-{timestamp}.{ext}"
            else:
                filename = f"{original_name}-{unique_id}-{timestamp}"
            
            # Construct full path
            full_path = f"{self.base_path}/{path.strip('/')}/{filename}"
            
            # Save file using Django's storage
            saved_path = self.storage.save(full_path, file)
            
            return saved_path
            
        except Exception as e:
            raise StorageUploadError(
                f"Failed to upload file to filesystem: {str(e)}",
                retryable=True
            )
    
    def get_url(self, identifier: str, **kwargs) -> str:
        """
        Generate a URL for accessing the stored file.
        
        This method generates a URL path for the stored file. Note that this
        returns a relative URL that needs to be combined with the domain
        to create a full URL.
        
        Args:
            identifier: File path returned by upload()
            **kwargs: Transformation options (ignored for filesystem storage)
            
        Returns:
            str: URL path to access the file (relative to MEDIA_URL)
            
        Raises:
            StorageNotFoundError: If file doesn't exist
        """
        if not self.exists(identifier):
            raise StorageNotFoundError(f"File not found: {identifier}")
        
        try:
            return self.storage.url(identifier)
        except Exception as e:
            raise StorageNotFoundError(f"Failed to generate URL for {identifier}: {str(e)}")
    
    def delete(self, identifier: str) -> bool:
        """
        Delete a file from the filesystem.
        
        This method removes a file from the local filesystem. It's idempotent -
        attempting to delete a non-existent file returns False without error.
        
        Args:
            identifier: File path returned by upload()
            
        Returns:
            bool: True if file was deleted, False if file didn't exist
            
        Raises:
            StorageDeleteError: If deletion fails due to filesystem issues
        """
        try:
            if not self.storage.exists(identifier):
                return False
            
            self.storage.delete(identifier)
            return True
            
        except Exception as e:
            raise StorageDeleteError(f"Failed to delete file {identifier}: {str(e)}")
    
    def exists(self, identifier: str) -> bool:
        """
        Check if a file exists in the filesystem.
        
        Args:
            identifier: File path returned by upload()
            
        Returns:
            bool: True if file exists, False otherwise
        """
        try:
            return self.storage.exists(identifier)
        except Exception:
            return False
    
    def get_metadata(self, identifier: str) -> Dict[str, Any]:
        """
        Retrieve metadata for a stored file.
        
        This method returns filesystem metadata such as size, modification time,
        and other file attributes available from the OS.
        
        Args:
            identifier: File path returned by upload()
            
        Returns:
            dict: Metadata dictionary with file information
            
        Raises:
            StorageNotFoundError: If file doesn't exist
        """
        if not self.exists(identifier):
            raise StorageNotFoundError(f"File not found: {identifier}")
        
        try:
            # Get full filesystem path
            full_path = self.storage.path(identifier)
            stat_info = os.stat(full_path)
            
            # Extract filename and extension
            filename = os.path.basename(identifier)
            name_part, ext = filename.rsplit('.', 1) if '.' in filename else (filename, '')
            
            metadata = {
                'bytes': stat_info.st_size,
                'format': ext.lower() if ext else 'unknown',
                'created_at': datetime.fromtimestamp(stat_info.st_ctime).isoformat(),
                'modified_at': datetime.fromtimestamp(stat_info.st_mtime).isoformat(),
                'path': identifier,
                'filename': filename
            }
            
            # Try to get image dimensions if it's an image file
            if ext.lower() in ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp']:
                try:
                    from PIL import Image
                    with Image.open(full_path) as img:
                        metadata['width'] = img.width
                        metadata['height'] = img.height
                except Exception:
                    # If PIL fails, just skip dimensions
                    pass
            
            return metadata
            
        except Exception as e:
            raise StorageNotFoundError(f"Failed to get metadata for {identifier}: {str(e)}")