"""
Storage abstractions for object storage and vector databases.

This module provides interfaces that decouple the application from specific
storage implementations
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Dict, List, Optional, BinaryIO


@dataclass
class StorageObject:
    """
    Represents an object in storage.

    Attributes:
        key: Object key/path in storage
        size: Size in bytes
        last_modified: Last modification timestamp
        metadata: Additional object metadata
        content_type: MIME type of the object
    """

    key: str
    size: int
    last_modified: Optional[str] = None
    metadata: Optional[Dict[str, str]] = None
    content_type: Optional[str] = None


class BaseObjectStorage(ABC):
    """
    Abstract interface for object storage backends.

    Provides a unified API for storing and retrieving files regardless
    of the underlying storage system (S3, MinIO, local, etc.).

    All implementations must be thread-safe and handle their own
    connection pooling and retry logic.

    Example:
        storage = S3Storage(bucket="my-bucket", endpoint="s3.amazonaws.com")

        # Upload video
        with open("video.mp4", "rb") as f:
            storage.upload(f, "videos/video123.mp4")

        # Download video
        with open("downloaded.mp4", "wb") as f:
            storage.download("videos/video123.mp4", f)
    """

    @abstractmethod
    def upload(
        self,
        file: BinaryIO,
        key: str,
        metadata: Optional[Dict[str, str]] = None,
        content_type: Optional[str] = None,
    ) -> str:
        """
        Upload a file to storage.

        Args:
            file: File-like object to upload
            key: Storage key/path for the object
            metadata: Optional metadata to attach to object
            content_type: MIME type of the file

        Returns:
            The key where the object was stored

        Raises:
            StorageError: If upload fails
        """

    @abstractmethod
    def download(self, key: str, file: BinaryIO) -> None:
        """
        Download a file from storage.

        Args:
            key: Storage key/path of the object
            file: File-like object to write downloaded data to

        Raises:
            StorageError: If download fails
            KeyError: If object doesn't exist
        """

    @abstractmethod
    def delete(self, key: str) -> bool:
        """
        Delete an object from storage.

        Args:
            key: Storage key/path of the object to delete

        Returns:
            True if object was deleted, False if it didn't exist

        Raises:
            StorageError: If deletion fails
        """

    @abstractmethod
    def exists(self, key: str) -> bool:
        """
        Check if an object exists in storage.

        Args:
            key: Storage key/path to check

        Returns:
            True if object exists, False otherwise
        """

    @abstractmethod
    def list(self, prefix: str = "") -> List[StorageObject]:
        """
        List objects in storage with optional prefix filter.

        Args:
            prefix: Optional prefix to filter objects (e.g., "videos/")

        Returns:
            List of StorageObject instances

        Raises:
            StorageError: If listing fails
        """

    @abstractmethod
    def get_url(self, key: str, expires_in: int = 3600) -> str:
        """
        Generate a presigned URL for accessing an object.

        Args:
            key: Storage key/path of the object
            expires_in: URL expiration time in seconds (default 1 hour)

        Returns:
            Presigned URL string

        Raises:
            StorageError: If URL generation fails
            KeyError: If object doesn't exist
        """

    def upload_from_path(self, local_path: str, key: str, **kwargs) -> str:
        """
        Convenience method to upload from a local file path.

        Args:
            local_path: Path to local file
            key: Storage key/path for the object
            **kwargs: Additional arguments passed to upload()

        Returns:
            The key where the object was stored
        """
        with open(local_path, "rb") as f:
            return self.upload(f, key, **kwargs)

    def download_to_path(self, key: str, local_path: str) -> None:
        """
        Convenience method to download to a local file path.

        Args:
            key: Storage key/path of the object
            local_path: Path where file should be saved
        """
        with open(local_path, "wb") as f:
            self.download(key, f)
