"""
Factory functions for creating storage backends.

Provides convenient functions for creating storage backends from
configuration or environment variables.
"""

import os
from pathlib import Path

from shared.storage.base import StorageBackend, StorageConfig


def create_storage_backend(config: StorageConfig) -> StorageBackend:
    """Create a storage backend from configuration.

    Args:
        config: Storage configuration.

    Returns:
        A configured storage backend.

    Raises:
        ValueError: If configuration is invalid.
    """
    if config.backend_type == "local":
        if config.base_path is None:
            raise ValueError("base_path is required for local storage backend")

        from shared.storage.local import LocalStorageBackend

        return LocalStorageBackend(base_path=config.base_path)

    elif config.backend_type == "azure_blob":
        if config.connection_string is None:
            raise ValueError(
                "connection_string is required for Azure blob storage backend"
            )
        if config.container_name is None:
            raise ValueError(
                "container_name is required for Azure blob storage backend"
            )

        # Import here to allow lazy loading of Azure SDK
        from azure.storage.blob import BlobServiceClient  # noqa: F401

        from shared.storage.azure import AzureBlobStorageBackend

        return AzureBlobStorageBackend(
            connection_string=config.connection_string,
            container_name=config.container_name,
        )

    elif config.backend_type == "s3":
        if config.bucket_name is None:
            raise ValueError("bucket_name is required for S3 storage backend")

        # Import here to allow lazy loading of boto3
        import boto3  # noqa: F401

        from shared.storage.s3 import S3StorageBackend

        return S3StorageBackend(
            bucket_name=config.bucket_name,
            region_name=config.region_name,
            access_key_id=config.access_key_id,
            secret_access_key=config.secret_access_key,
            endpoint_url=config.endpoint_url,
        )

    else:
        raise ValueError(f"Unknown storage backend type: {config.backend_type}")


def get_default_storage_config() -> StorageConfig:
    """Get storage configuration from environment variables.

    Environment variables:
        STORAGE_BACKEND: Backend type ("local", "azure_blob", or "s3"), defaults to "local".
        STORAGE_BASE_PATH: Base path for local storage.
        AZURE_STORAGE_CONNECTION_STRING: Azure connection string.
        AZURE_STORAGE_CONTAINER: Azure container name.
        AWS_S3_BUCKET: S3 bucket name.
        AWS_REGION: AWS region name.
        AWS_ACCESS_KEY_ID: AWS access key ID.
        AWS_SECRET_ACCESS_KEY: AWS secret access key.
        AWS_ENDPOINT_URL: Custom endpoint URL for S3-compatible services.

    Returns:
        StorageConfig from environment.
    """
    backend_type = os.environ.get("STORAGE_BACKEND", "local")

    if backend_type == "local":
        base_path_str = os.environ.get("STORAGE_BASE_PATH")
        # Expand ~ to home directory
        base_path = Path(os.path.expanduser(base_path_str)) if base_path_str else None

        return StorageConfig(
            backend_type="local",
            base_path=base_path,
        )

    elif backend_type == "azure_blob":
        return StorageConfig(
            backend_type="azure_blob",
            connection_string=os.environ.get("AZURE_STORAGE_CONNECTION_STRING"),
            container_name=os.environ.get("AZURE_STORAGE_CONTAINER"),
        )

    elif backend_type == "s3":
        return StorageConfig(
            backend_type="s3",
            bucket_name=os.environ.get("AWS_S3_BUCKET"),
            region_name=os.environ.get("AWS_REGION"),
            access_key_id=os.environ.get("AWS_ACCESS_KEY_ID"),
            secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY"),
            endpoint_url=os.environ.get("AWS_ENDPOINT_URL"),
        )

    else:
        return StorageConfig(backend_type=backend_type)


def create_storage_backend_from_env() -> StorageBackend:
    """Create a storage backend from environment variables.

    Environment variables:
        STORAGE_BACKEND: Backend type ("local", "azure_blob", or "s3"), defaults to "local".
        STORAGE_BASE_PATH: Base path for local storage.
        AZURE_STORAGE_CONNECTION_STRING: Azure connection string.
        AZURE_STORAGE_CONTAINER: Azure container name.
        AWS_S3_BUCKET: S3 bucket name.
        AWS_REGION: AWS region name.
        AWS_ACCESS_KEY_ID: AWS access key ID.
        AWS_SECRET_ACCESS_KEY: AWS secret access key.
        AWS_ENDPOINT_URL: Custom endpoint URL for S3-compatible services.

    Returns:
        A configured storage backend.

    Raises:
        ValueError: If required environment variables are missing or empty.
    """
    backend_type = os.environ.get("STORAGE_BACKEND", "local").strip()

    if backend_type == "local":
        base_path = os.environ.get("STORAGE_BASE_PATH", "").strip()
        if not base_path:
            raise ValueError(
                "STORAGE_BASE_PATH environment variable is required and cannot be empty"
            )

        # Expand ~ to home directory
        base_path_expanded = os.path.expanduser(base_path)

        from shared.storage.local import LocalStorageBackend

        return LocalStorageBackend(base_path=Path(base_path_expanded))

    elif backend_type == "azure_blob":
        connection_string = os.environ.get(
            "AZURE_STORAGE_CONNECTION_STRING", ""
        ).strip()
        if not connection_string:
            raise ValueError(
                "AZURE_STORAGE_CONNECTION_STRING environment variable is required "
                "and cannot be empty"
            )

        container_name = os.environ.get("AZURE_STORAGE_CONTAINER", "").strip()
        if not container_name:
            raise ValueError(
                "AZURE_STORAGE_CONTAINER environment variable is required "
                "and cannot be empty"
            )

        # Import here to allow lazy loading of Azure SDK
        from azure.storage.blob import BlobServiceClient  # noqa: F401

        from shared.storage.azure import AzureBlobStorageBackend

        return AzureBlobStorageBackend(
            connection_string=connection_string,
            container_name=container_name,
        )

    elif backend_type == "s3":
        bucket_name = os.environ.get("AWS_S3_BUCKET", "").strip()
        if not bucket_name:
            raise ValueError(
                "AWS_S3_BUCKET environment variable is required and cannot be empty"
            )

        # Import here to allow lazy loading of boto3
        import boto3  # noqa: F401

        from shared.storage.s3 import S3StorageBackend

        return S3StorageBackend(
            bucket_name=bucket_name,
            region_name=os.environ.get("AWS_REGION", "").strip() or None,
            access_key_id=os.environ.get("AWS_ACCESS_KEY_ID", "").strip() or None,
            secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY", "").strip()
            or None,
            endpoint_url=os.environ.get("AWS_ENDPOINT_URL", "").strip() or None,
        )

    else:
        raise ValueError(f"Unknown storage backend type: {backend_type}")


def create_storage_backend_from_file(config_path: Path | str) -> StorageBackend:
    """Create a storage backend from a configuration file.

    Args:
        config_path: Path to YAML configuration file.

    Returns:
        A configured storage backend.

    Raises:
        FileNotFoundError: If config file doesn't exist.
        ValueError: If configuration is invalid.
    """
    from shared.storage.config_loader import load_storage_config

    file_config = load_storage_config(config_path)

    if file_config.backend_type == "local":
        if file_config.local is None:
            raise ValueError("local configuration section is required")

        from shared.storage.local import LocalStorageBackend

        return LocalStorageBackend(base_path=file_config.local.base_path)

    elif file_config.backend_type == "azure_blob":
        if file_config.azure is None:
            raise ValueError("azure configuration section is required")

        # Import here to allow lazy loading of Azure SDK
        from azure.storage.blob import BlobServiceClient  # noqa: F401

        from shared.storage.azure import AzureBlobStorageBackend

        return AzureBlobStorageBackend(
            connection_string=file_config.azure.connection_string,
            container_name=file_config.azure.container_name,
            create_container=file_config.azure.create_container,
        )

    elif file_config.backend_type == "s3":
        if file_config.s3 is None:
            raise ValueError("s3 configuration section is required")

        # Import here to allow lazy loading of boto3
        import boto3  # noqa: F401

        from shared.storage.s3 import S3StorageBackend

        return S3StorageBackend(
            bucket_name=file_config.s3.bucket_name,
            region_name=file_config.s3.region_name,
            access_key_id=file_config.s3.access_key_id,
            secret_access_key=file_config.s3.secret_access_key,
            endpoint_url=file_config.s3.endpoint_url,
            create_bucket=file_config.s3.create_bucket,
        )

    else:
        raise ValueError(f"Unknown storage backend type: {file_config.backend_type}")


def get_storage_backend(config_path: Path | str | None = None) -> StorageBackend:
    """Get storage backend with fallback chain.

    Priority:
    1. Config file (if provided)
    2. Environment variables

    Args:
        config_path: Optional path to config file.

    Returns:
        A configured storage backend.

    Raises:
        ValueError: If configuration is invalid.
        FileNotFoundError: If specified config file doesn't exist.
    """
    if config_path:
        return create_storage_backend_from_file(config_path)

    # Fall back to environment variables
    return create_storage_backend_from_env()
