"""
Storage adapter interfaces for image upload operations.

This module defines the abstract base class that all storage implementations
must implement. This follows the Dependency Inversion Principle, allowing
high-level modules to depend on abstractions rather than concrete implementations.
"""

from abc import ABC, abstractmethod
from typing import BinaryIO, Dict, Any, Optional


class ImageStorageAdapter(ABC):
    """
    Abstract base class for image storage backends.
    
    This interface allows the application to work with different storage
    implementations (filesystem, Cloudinary, S3, Azure, etc.) without changing
    business logic. All storage backends must implement these methods to ensure
    consistent behavior across the application.
    
    The interface follows the Liskov Substitution Principle - any implementation
    can be substituted without breaking functionality.
    """
    
    @abstractmethod
    def upload(self, file: BinaryIO, path: str, **kwargs) -> str:
        """
        Upload a file to storage.
        
        This method uploads a binary file to the storage backend and returns
        a unique identifier that can be used to retrieve the file later.
        
        Args:
            file: Binary file object to upload (e.g., uploaded image file)
            path: Logical path/folder for organization (e.g., "reviews/property")
            **kwargs: Implementation-specific options (transformations, metadata, etc.)
            
        Returns:
            str: Storage identifier that can be used with other methods.
                 For filesystem: relative file path (e.g., "reviews/property/image.jpg")
                 For Cloudinary: public_id (e.g., "panjabiz/reviews/property/abc123")
                 
        Raises:
            StorageUploadError: If upload fails due to network, auth, or other issues
            StorageQuotaError: If storage quota would be exceeded
            
        Example:
            >>> storage = CloudinaryStorage(config)
            >>> identifier = storage.upload(image_file, "reviews/property")
            >>> print(identifier)  # "panjabiz/reviews/property/review-abc123-1704067200"
        """
        pass
    
    @abstractmethod
    def get_url(self, identifier: str, **kwargs) -> str:
        """
        Generate a URL for accessing the stored file.
        
        This method generates a publicly accessible HTTPS URL for the stored file.
        The URL should be suitable for use in web applications and API responses.
        
        Args:
            identifier: Storage identifier returned by upload()
            **kwargs: Transformation options (width, height, format, quality, etc.)
                     Implementation-specific - filesystem may ignore these,
                     Cloudinary can apply transformations on-the-fly
            
        Returns:
            str: Absolute HTTPS URL to access the file
                 For filesystem: URL path that needs request.build_absolute_uri()
                 For Cloudinary: Full CDN URL with optional transformations
                 
        Raises:
            StorageNotFoundError: If identifier doesn't exist in storage
            
        Example:
            >>> url = storage.get_url("panjabiz/reviews/property/abc123")
            >>> print(url)  # "https://res.cloudinary.com/.../abc123.jpg"
            
            >>> # With transformations
            >>> url = storage.get_url("abc123", transformation={"width": 400, "height": 400})
            >>> print(url)  # "https://res.cloudinary.com/.../w_400,h_400/abc123.jpg"
        """
        pass
    
    @abstractmethod
    def delete(self, identifier: str) -> bool:
        """
        Delete a file from storage.
        
        This method removes a file from the storage backend. It should be
        idempotent - calling it multiple times with the same identifier
        should not raise an error.
        
        Args:
            identifier: Storage identifier returned by upload()
            
        Returns:
            bool: True if file was deleted, False if file didn't exist
            
        Raises:
            StorageDeleteError: If deletion fails due to network or permission issues
                               (but not if file doesn't exist)
            
        Example:
            >>> deleted = storage.delete("panjabiz/reviews/property/abc123")
            >>> print(deleted)  # True if file existed and was deleted
        """
        pass
    
    @abstractmethod
    def exists(self, identifier: str) -> bool:
        """
        Check if a file exists in storage.
        
        This method checks whether a file with the given identifier exists
        in the storage backend without downloading or accessing the file content.
        
        Args:
            identifier: Storage identifier returned by upload()
            
        Returns:
            bool: True if file exists, False otherwise
            
        Example:
            >>> exists = storage.exists("panjabiz/reviews/property/abc123")
            >>> print(exists)  # True or False
        """
        pass
    
    @abstractmethod
    def get_metadata(self, identifier: str) -> Dict[str, Any]:
        """
        Retrieve metadata for a stored file.
        
        This method returns metadata about the stored file such as size,
        format, dimensions, upload date, etc. The exact metadata available
        depends on the storage backend implementation.
        
        Args:
            identifier: Storage identifier returned by upload()
            
        Returns:
            dict: Metadata dictionary with backend-specific keys.
                  Common keys should include:
                  - 'bytes': File size in bytes (int)
                  - 'format': File format/extension (str)
                  - 'width': Image width in pixels (int, if applicable)
                  - 'height': Image height in pixels (int, if applicable)
                  - 'created_at' or 'uploaded_at': Upload timestamp
                  
        Raises:
            StorageNotFoundError: If identifier doesn't exist in storage
            
        Example:
            >>> metadata = storage.get_metadata("panjabiz/reviews/property/abc123")
            >>> print(metadata)
            {
                'bytes': 245760,
                'format': 'jpg',
                'width': 1200,
                'height': 800,
                'created_at': '2024-01-01T12:00:00Z'
            }
        """
        pass