"""
FastAPI integration for storage system.

Provides helpers to integrate storage backends with FastAPI applications.
"""

import logging
from contextlib import asynccontextmanager
from typing import Optional

from fastapi import FastAPI, HTTPException, Query, Request
from fastapi.responses import StreamingResponse

from .base import FileNotFoundError, PermissionDeniedError, StorageBackend, StorageError
from .easy import easy_storage

logger = logging.getLogger(__name__)


def add_storage(
    app: FastAPI,
    backend: Optional[StorageBackend] = None,
    serve_files: bool = False,
    file_route_prefix: str = "/files",
) -> StorageBackend:
    """
    Add storage backend to FastAPI application.

    This function:
    - Stores backend in app.state.storage
    - Registers startup/shutdown hooks
    - Optionally mounts file serving route
    - Adds health check integration

    Args:
        app: FastAPI application instance
        backend: Storage backend instance (auto-detected if None)
        serve_files: If True, mount route to serve files (LocalBackend only)
        file_route_prefix: URL prefix for file serving (default: "/files")

    Returns:
        Storage backend instance

    Example:
        >>> from fastapi import FastAPI
        >>> from svc_infra.storage import add_storage, easy_storage
        >>>
        >>> app = FastAPI()
        >>>
        >>> # Auto-detect backend
        >>> storage = add_storage(app)
        >>>
        >>> # Explicit backend
        >>> backend = easy_storage(backend="s3", bucket="my-uploads")
        >>> storage = add_storage(app, backend)
        >>>
        >>> # With file serving (LocalBackend only)
        >>> backend = easy_storage(backend="local")
        >>> storage = add_storage(app, backend, serve_files=True)

    Note:
        File serving is only supported for LocalBackend. For S3/GCS,
        use presigned URLs instead.
    """
    # Auto-detect backend if not provided
    if backend is None:
        backend = easy_storage()

    # Store in app state
    app.state.storage = backend

    # Get existing lifespan or create new one
    existing_lifespan = getattr(app.router, "lifespan_context", None)

    @asynccontextmanager
    async def storage_lifespan(app: FastAPI):
        # Startup
        logger.info(f"Storage backend initialized: {backend.__class__.__name__}")

        # Test connection for S3 backend
        if hasattr(backend, "bucket"):
            try:
                # Try to list keys (limit 1 to minimize cost)
                await backend.list_keys(limit=1)
                logger.info(f"Successfully connected to storage: {backend.bucket}")
            except Exception as e:
                logger.error(f"Failed to connect to storage: {e}")
                # Don't fail startup, let health check catch it

        # Call existing lifespan if present
        if existing_lifespan is not None:
            async with existing_lifespan(app):
                yield
        else:
            yield

        # Shutdown
        logger.info("Storage backend shutdown")

    # Replace lifespan
    app.router.lifespan_context = storage_lifespan

    # Mount file serving route if requested (LocalBackend only)
    if serve_files:
        from .backends.local import LocalBackend

        if not isinstance(backend, LocalBackend):
            logger.warning(
                f"File serving only supported for LocalBackend, "
                f"got {backend.__class__.__name__}. Skipping route mount."
            )
        else:
            # Create file serving route
            @app.get(f"{file_route_prefix}/{{key:path}}")
            async def serve_file(
                key: str,
                expires: str = Query(..., description="Expiration timestamp"),
                signature: str = Query(..., description="HMAC signature"),
                download: bool = Query(False, description="Force download"),
            ):
                """
                Serve files from local storage with signature validation.

                Requires valid signature generated by LocalBackend.get_url().
                """
                # Verify signature
                if not backend.verify_url(key, expires, signature, download):
                    raise HTTPException(
                        status_code=403,
                        detail="Invalid or expired signature",
                    )

                # Get file
                try:
                    data = await backend.get(key)
                    metadata = await backend.get_metadata(key)

                    # Determine content disposition
                    if download:
                        filename = key.split("/")[-1]
                        content_disposition = f'attachment; filename="{filename}"'
                    else:
                        content_disposition = "inline"

                    # Return file
                    return StreamingResponse(
                        iter([data]),
                        media_type=metadata.get("content_type", "application/octet-stream"),
                        headers={
                            "Content-Disposition": content_disposition,
                            "Content-Length": str(len(data)),
                        },
                    )

                except FileNotFoundError:
                    raise HTTPException(status_code=404, detail="File not found")
                except PermissionDeniedError:
                    raise HTTPException(status_code=403, detail="Permission denied")
                except StorageError as e:
                    logger.error(f"Storage error serving file {key}: {e}")
                    raise HTTPException(status_code=500, detail="Storage error")

            logger.info(f"File serving enabled at {file_route_prefix}")

    return backend


def get_storage(request: Request) -> StorageBackend:
    """
    FastAPI dependency to inject storage backend.

    Use this in route handlers to access the storage backend.

    Example:
        >>> from fastapi import APIRouter, Depends, UploadFile
        >>> from svc_infra.storage import get_storage, StorageBackend
        >>>
        >>> router = APIRouter()
        >>>
        >>> @router.post("/upload")
        >>> async def upload_file(
        ...     file: UploadFile,
        ...     storage: StorageBackend = Depends(get_storage),
        ... ):
        ...     content = await file.read()
        ...     url = await storage.put(
        ...         key=f"uploads/{file.filename}",
        ...         data=content,
        ...         content_type=file.content_type or "application/octet-stream"
        ...     )
        ...     return {"url": url}

    Raises:
        RuntimeError: If storage not initialized with add_storage()
    """
    if not hasattr(request.app.state, "storage"):
        raise RuntimeError(
            "Storage not initialized. " "Call add_storage(app) during application setup."
        )

    return request.app.state.storage


async def health_check_storage(request: Request) -> dict:
    """
    Health check for storage backend.

    Returns storage status and basic statistics.

    Example:
        >>> from fastapi import FastAPI
        >>> from svc_infra.storage import add_storage, health_check_storage
        >>>
        >>> app = FastAPI()
        >>> add_storage(app)
        >>>
        >>> @app.get("/_health/storage")
        >>> async def storage_health(request: Request):
        ...     return await health_check_storage(request)

    Returns:
        Dict with status and backend information
    """
    try:
        storage = get_storage(request)

        # Get backend type
        backend_type = storage.__class__.__name__.replace("Backend", "").lower()

        # Try a simple operation
        await storage.list_keys(limit=1)

        return {
            "status": "healthy",
            "backend": backend_type,
        }

    except Exception as e:
        logger.error(f"Storage health check failed: {e}")
        return {
            "status": "unhealthy",
            "error": str(e),
        }


__all__ = [
    "add_storage",
    "get_storage",
    "health_check_storage",
]
