"""cortex/api/deprecation.py — Shared HTTP deprecation utilities.

Provides a decorator and helper for adding RFC 8594 deprecation headers to
legacy API endpoints.  Consistent with the pattern already used in
``cortex/api/routes/brain_graph.py`` (Issue #431).

Usage::

    from cortex.api.deprecation import deprecated_route

    @router.post("/api/v1/old-path", ...)
    @deprecated_route(canonical="/api/v1/new-path")
    async def old_endpoint(response: Response, ...):
        ...

The decorator injects three response headers:
  ``Deprecation: true``           — RFC 8594 §3
  ``Sunset: 2026-06-01``         — RFC 8594 §4
  ``Link: <canonical>; rel="successor-version"``

It also emits a WARNING log once (per canonical path, throttled per process
lifetime to avoid log spam in high-traffic deployments).

Issues: #297 (duplicate endpoint consolidation), #301 (singular/plural).
"""

from __future__ import annotations

import functools
import logging
from typing import Callable, Optional, Set

logger = logging.getLogger(__name__)

# Sunset date for all legacy paths in this batch.
DEPRECATION_SUNSET: str = "2026-06-01"

# Module-level set so we warn once per path per process, not on every call.
_warned_paths: Set[str] = set()


def add_deprecation_headers(response, canonical_path: str) -> None:
    """Inject RFC 8594 deprecation + sunset + link headers into *response*.

    Safe to call directly from an endpoint body when the decorator pattern
    cannot be applied (e.g. inline handlers).

    Args:
        response:       A FastAPI ``Response`` object available via DI.
        canonical_path: The preferred replacement path (full path, no host).
    """
    response.headers["Deprecation"] = "true"
    response.headers["Sunset"] = DEPRECATION_SUNSET
    response.headers["Link"] = f'<{canonical_path}>; rel="successor-version"'

    if canonical_path not in _warned_paths:
        _warned_paths.add(canonical_path)
        logger.warning(
            "Deprecated endpoint called. Canonical path: %s  Sunset: %s",
            canonical_path,
            DEPRECATION_SUNSET,
        )


def deprecated_route(canonical: str) -> Callable:
    """Decorator that marks a FastAPI endpoint as deprecated.

    Wraps the endpoint so that every response receives RFC 8594 deprecation
    headers and a one-time WARNING log is emitted per process.

    The wrapped function **must** accept a ``response: Response`` keyword
    argument (standard FastAPI DI pattern).

    Args:
        canonical: The canonical (replacement) path consumers should migrate to.

    Returns:
        A decorator that wraps the async endpoint function.

    Example::

        @router.post("/api/v1/old-path", deprecated=True)
        @deprecated_route(canonical="/api/v1/new-path")
        async def old_endpoint(response: Response, ...):
            ...
    """
    def decorator(fn: Callable) -> Callable:
        @functools.wraps(fn)
        async def wrapper(*args, **kwargs):
            response = kwargs.get("response")
            if response is not None:
                add_deprecation_headers(response, canonical)
            else:
                # Log warning even if we can't inject headers (shouldn't happen)
                if canonical not in _warned_paths:
                    _warned_paths.add(canonical)
                    logger.warning(
                        "Deprecated endpoint called (no response object found). "
                        "Canonical path: %s  Sunset: %s",
                        canonical,
                        DEPRECATION_SUNSET,
                    )
            return await fn(*args, **kwargs)
        return wrapper
    return decorator
