"""
API Versioning Middleware & Utilities.

استراتژی نسخه‌بندی API:
- URL-based versioning: /api/v1/, /api/v2/
- Deprecation headers: Warning, Sunset, Deprecation
- Version negotiation via Accept header (secondary)
"""
import datetime
import logging
from typing import Optional

from django.conf import settings
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.utils import timezone
from django.utils.deprecation import MiddlewareMixin

logger = logging.getLogger(__name__)


# =============================================================================
# Configuration
# =============================================================================

API_VERSIONS = getattr(settings, 'API_VERSIONS', {
    'v1': {
        'status': 'current',
        'released': '2024-01-01',
        'sunset': None,
    },
    'v2': {
        'status': 'development',
        'released': None,
        'sunset': None,
    },
})

# Default version when none specified
DEFAULT_API_VERSION = getattr(settings, 'DEFAULT_API_VERSION', 'v1')

# Deprecated endpoints: path prefix -> sunset info
DEPRECATED_ENDPOINTS = getattr(settings, 'DEPRECATED_ENDPOINTS', {
    # Example:
    # '/api/v1/old-endpoint/': {
    #     'sunset': '2025-06-01',
    #     'alternative': '/api/v2/new-endpoint/',
    #     'message': 'Use /api/v2/new-endpoint/ instead',
    # },
})


# =============================================================================
# Versioning Middleware
# =============================================================================

class APIVersioningMiddleware(MiddlewareMixin):
    """
    Middleware for API versioning support.

    Features:
    - Extracts version from URL path (/api/v1/...) or Accept header
    - Adds deprecation/sunset headers for deprecated versions or endpoints
    - Injects version info into request for downstream use
    - Returns 410 Gone for sunset endpoints
    """

    def process_request(self, request: HttpRequest) -> Optional[HttpResponse]:
        # Extract version from URL
        version = self._extract_version_from_url(request.path)

        # Fallback: check Accept header for version
        if not version:
            version = self._extract_version_from_accept(request)

        # Default if not found
        if not version:
            version = DEFAULT_API_VERSION

        # Attach to request
        request.api_version = version

        # Check if version is valid
        if version not in API_VERSIONS and request.path.startswith('/api/'):
            return JsonResponse(
                {
                    'error': 'unsupported_api_version',
                    'message': f'API version "{version}" is not supported.',
                    'supported_versions': list(API_VERSIONS.keys()),
                },
                status=400,
            )

        # Check if a sunset version
        version_info = API_VERSIONS.get(version, {})
        if version_info.get('status') == 'sunset':
            sunset_date = version_info.get('sunset')
            if sunset_date:
                sunset_dt = datetime.datetime.strptime(sunset_date, '%Y-%m-%d').date()
                if timezone.now().date() > sunset_dt:
                    return JsonResponse(
                        {
                            'error': 'api_version_sunset',
                            'message': f'API version "{version}" has been sunset as of {sunset_date}.',
                            'supported_versions': [
                                v for v, info in API_VERSIONS.items()
                                if info.get('status') in ('current', 'stable')
                            ],
                        },
                        status=410,
                    )

        # Check for deprecated specific endpoints
        for deprecated_path, dep_info in DEPRECATED_ENDPOINTS.items():
            if request.path.startswith(deprecated_path):
                sunset_date = dep_info.get('sunset')
                if sunset_date:
                    sunset_dt = datetime.datetime.strptime(sunset_date, '%Y-%m-%d').date()
                    if timezone.now().date() > sunset_dt:
                        return JsonResponse(
                            {
                                'error': 'endpoint_sunset',
                                'message': dep_info.get(
                                    'message', 'This endpoint has been removed.'
                                ),
                                'alternative': dep_info.get('alternative'),
                            },
                            status=410,
                        )

        return None

    def process_response(
        self, request: HttpRequest, response: HttpResponse
    ) -> HttpResponse:
        if not request.path.startswith('/api/'):
            return response

        version = getattr(request, 'api_version', DEFAULT_API_VERSION)
        version_info = API_VERSIONS.get(version, {})

        # Add version header
        response['X-API-Version'] = version

        # Add deprecation headers for deprecated versions
        if version_info.get('status') == 'deprecated':
            sunset_date = version_info.get('sunset')
            response['Deprecation'] = 'true'
            if sunset_date:
                response['Sunset'] = sunset_date
            response['Warning'] = (
                f'299 - "API version {version} is deprecated. '
                f'Please migrate to a newer version."'
            )
            link_header = version_info.get('migration_guide')
            if link_header:
                response['Link'] = f'<{link_header}>; rel="deprecation"'

        # Add deprecation headers for specific endpoints
        for deprecated_path, dep_info in DEPRECATED_ENDPOINTS.items():
            if request.path.startswith(deprecated_path):
                response['Deprecation'] = 'true'
                sunset_date = dep_info.get('sunset')
                if sunset_date:
                    response['Sunset'] = sunset_date
                message = dep_info.get('message', 'This endpoint is deprecated.')
                response['Warning'] = f'299 - "{message}"'
                alternative = dep_info.get('alternative')
                if alternative:
                    response['Link'] = (
                        f'<{alternative}>; rel="successor-version"'
                    )
                break

        return response

    @staticmethod
    def _extract_version_from_url(path: str) -> Optional[str]:
        """Extract API version from URL path like /api/v1/..."""
        parts = path.strip('/').split('/')
        if len(parts) >= 2 and parts[0] == 'api':
            candidate = parts[1]
            if candidate.startswith('v') and candidate[1:].isdigit():
                return candidate
        return None

    @staticmethod
    def _extract_version_from_accept(request: HttpRequest) -> Optional[str]:
        """
        Extract version from Accept header.

        Supports: application/vnd.nexapro.v1+json
        """
        accept = request.META.get('HTTP_ACCEPT', '')
        if 'vnd.nexapro.' in accept:
            try:
                version_part = accept.split('vnd.nexapro.')[1]
                version = version_part.split('+')[0]
                if version.startswith('v') and version[1:].isdigit():
                    return version
            except (IndexError, ValueError):
                pass
        return None


# =============================================================================
# Version Router
# =============================================================================

class VersionRouter:
    """
    Utility for routing to different view implementations based on API version.

    Usage in urls.py:
        router = VersionRouter()
        router.register('v1', UserViewSetV1)
        router.register('v2', UserViewSetV2)

        urlpatterns = [
            path('users/', router.as_view(), name='users'),
        ]
    """

    def __init__(self):
        self._version_map: dict = {}

    def register(self, version: str, view_class):
        """Register a view class for a specific API version."""
        self._version_map[version] = view_class
        return self

    def as_view(self, **initkwargs):
        """Return a view function that routes to the correct version."""
        version_map = self._version_map

        def dispatch(request, *args, **kwargs):
            version = getattr(request, 'api_version', DEFAULT_API_VERSION)
            view_class = version_map.get(version)

            if not view_class:
                # Fallback: use the latest available version
                available = sorted(version_map.keys(), reverse=True)
                for v in available:
                    v_info = API_VERSIONS.get(v, {})
                    if v_info.get('status') in ('current', 'stable'):
                        view_class = version_map[v]
                        break

                if not view_class and available:
                    view_class = version_map[available[0]]

            if not view_class:
                return JsonResponse(
                    {'error': 'No view available for this API version.'},
                    status=404,
                )

            view = view_class.as_view(**initkwargs)
            return view(request, *args, **kwargs)

        return dispatch


# =============================================================================
# Deprecation Decorator
# =============================================================================

def deprecated_endpoint(sunset_date: str, alternative: str = '', message: str = ''):
    """
    Decorator to mark a view as deprecated.

    Usage:
        @deprecated_endpoint(
            sunset_date='2025-06-01',
            alternative='/api/v2/users/',
            message='Use v2 endpoint instead'
        )
        class OldUserView(APIView):
            ...
    """

    def decorator(view_func_or_class):
        original_dispatch = None

        if hasattr(view_func_or_class, 'dispatch'):
            # Class-based view
            original_dispatch = view_func_or_class.dispatch

            def patched_dispatch(self, request, *args, **kwargs):
                response = original_dispatch(self, request, *args, **kwargs)
                _add_deprecation_headers(response, sunset_date, alternative, message)
                return response

            view_func_or_class.dispatch = patched_dispatch
            return view_func_or_class
        else:
            # Function-based view
            def wrapper(request, *args, **kwargs):
                response = view_func_or_class(request, *args, **kwargs)
                _add_deprecation_headers(response, sunset_date, alternative, message)
                return response

            wrapper.__name__ = view_func_or_class.__name__
            wrapper.__doc__ = view_func_or_class.__doc__
            return wrapper

    return decorator


def _add_deprecation_headers(
    response: HttpResponse,
    sunset_date: str,
    alternative: str,
    message: str,
):
    """Add deprecation-related headers to a response."""
    response['Deprecation'] = 'true'
    if sunset_date:
        response['Sunset'] = sunset_date
    warning_msg = message or 'This endpoint is deprecated.'
    response['Warning'] = f'299 - "{warning_msg}"'
    if alternative:
        response['Link'] = f'<{alternative}>; rel="successor-version"'
