"""API versioning utilities and deprecation handling.

This module provides tools for managing API version deprecation:
- Deprecation middleware for entire API versions
- Deprecation decorator for individual endpoints
- RFC 8594 compliant deprecation headers
"""

import logging
from collections.abc import Callable
from datetime import datetime
from functools import wraps
from typing import Any

from fastapi import Request, Response
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.types import ASGIApp

logger = logging.getLogger(__name__)


class VersionDeprecationMiddleware(BaseHTTPMiddleware):
    """Middleware to add deprecation headers for deprecated API versions.

    Adds RFC 8594 compliant headers:
    - Deprecation: Indicates the version is deprecated
    - Sunset: Indicates when the version will be removed
    - Link: Points to migration documentation

    Usage in main.py:
        app.add_middleware(
            VersionDeprecationMiddleware,
            deprecated_versions={"v1": {"sunset": "2025-06-01", "link": "/docs/migration/v2"}},
        )
    """

    def __init__(
        self,
        app: ASGIApp,
        deprecated_versions: dict[str, dict[str, str]] | None = None,
    ) -> None:
        """Initialize the middleware.

        Args:
            app: The ASGI application.
            deprecated_versions: Dict mapping version prefixes to deprecation info.
                Each entry should have:
                - sunset: ISO date string when version will be removed (optional)
                - link: URL to migration documentation (optional)
                - message: Custom deprecation message (optional)

        Example:
            {
                "v1": {
                    "sunset": "2025-06-01",
                    "link": "https://api.example.com/docs/migration/v2",
                    "message": "Please migrate to API v2",
                }
            }
        """
        super().__init__(app)
        self.deprecated_versions = deprecated_versions or {}

    async def dispatch(
        self, request: Request, call_next: RequestResponseEndpoint
    ) -> Response:
        """Process the request and add deprecation headers if needed."""
        response = await call_next(request)

        path = request.url.path
        for version, info in self.deprecated_versions.items():
            if f"/api/{version}/" in path or path.endswith(f"/api/{version}"):
                self._add_deprecation_headers(response, version, info)
                self._log_deprecated_usage(request, version)
                break

        return response

    def _add_deprecation_headers(
        self, response: Response, version: str, info: dict[str, str]
    ) -> None:
        """Add RFC 8594 deprecation headers to the response."""
        response.headers["Deprecation"] = "true"

        if sunset := info.get("sunset"):
            # Convert to HTTP date format
            sunset_date = datetime.fromisoformat(sunset)
            response.headers["Sunset"] = sunset_date.strftime("%a, %d %b %Y %H:%M:%S GMT")

        if link := info.get("link"):
            response.headers["Link"] = f'<{link}>; rel="deprecation"'

        # Custom warning header
        message = info.get("message", f"API {version} is deprecated")
        response.headers["X-API-Deprecation-Warning"] = message

    def _log_deprecated_usage(self, request: Request, version: str) -> None:
        """Log usage of deprecated API version for monitoring."""
        logger.warning(
            "Deprecated API version accessed: %s %s %s",
            version,
            request.method,
            request.url.path,
        )


def deprecated(
    sunset: str | None = None,
    message: str | None = None,
    link: str | None = None,
) -> Callable[..., Any]:
    """Decorator to mark an endpoint as deprecated.

    Adds deprecation headers to responses from the decorated endpoint.
    Use this for deprecating individual endpoints within an active API version.

    Args:
        sunset: ISO date string when endpoint will be removed.
        message: Custom deprecation message.
        link: URL to migration documentation.

    Usage:
        @router.get("/old-endpoint")
        @deprecated(
            sunset="2025-06-01",
            message="Use /new-endpoint instead",
            link="/docs/migration",
        )
        async def old_endpoint():
            ...
    """

    def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
        @wraps(func)
        async def wrapper(*args: Any, **kwargs: Any) -> Any:
            result = await func(*args, **kwargs)

            # Find Response object in args (FastAPI injects it)
            response = None
            for arg in args:
                if isinstance(arg, Response):
                    response = arg
                    break
            for value in kwargs.values():
                if isinstance(value, Response):
                    response = value
                    break

            if response:
                response.headers["Deprecation"] = "true"
                if sunset:
                    sunset_date = datetime.fromisoformat(sunset)
                    response.headers["Sunset"] = sunset_date.strftime(
                        "%a, %d %b %Y %H:%M:%S GMT"
                    )
                if link:
                    response.headers["Link"] = f'<{link}>; rel="deprecation"'
                if message:
                    response.headers["X-API-Deprecation-Warning"] = message

            return result

        # Add deprecation info to OpenAPI schema
        wrapper.__doc__ = (
            f"{func.__doc__ or ''}\n\n"
            f"**DEPRECATED**"
            f"{f': {message}' if message else ''}"
            f"{f' (Sunset: {sunset})' if sunset else ''}"
        )

        return wrapper

    return decorator
