# middleware/context_middleware.py
"""
Context Detection Middleware (Class-Based)

Detects the request context type (API, Admin, Vendor Dashboard, Shop, or Fallback)
and injects it into request.state for use by error handlers and other components.

MUST run AFTER vendor_context_middleware to have access to clean_path.
MUST run BEFORE theme_context_middleware (which needs context_type).

Class-based middleware provides:
- Better state management
- Easier testing
- More organized code
- Standard ASGI pattern
"""

import logging
from enum import Enum

from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware

logger = logging.getLogger(__name__)


class RequestContext(str, Enum):
    """Request context types for the application."""

    API = "api"
    ADMIN = "admin"
    VENDOR_DASHBOARD = "vendor"
    SHOP = "shop"
    FALLBACK = "fallback"


class ContextManager:
    """Manages context detection for multi-area application."""

    @staticmethod
    def detect_context(request: Request) -> RequestContext:
        """
        Detect the request context type.

        Priority order:
        1. API → /api/* paths (highest priority, always JSON)
        2. Admin → /admin/* paths or admin.* subdomain
        3. Vendor Dashboard → /vendor/* paths (vendor management area)
        4. Shop → Vendor storefront (custom domain, subdomain, or shop paths)
        5. Fallback → Unknown/generic context

        CRITICAL: Uses clean_path (if available) instead of original path.
        This ensures correct context detection for path-based routing.

        Args:
            request: FastAPI request object

        Returns:
            RequestContext enum value
        """
        # Use clean_path if available (extracted by vendor_context_middleware)
        # Falls back to original path if clean_path not set
        # This is critical for correct context detection with path-based routing
        path = getattr(request.state, "clean_path", request.url.path)

        host = request.headers.get("host", "")

        # Remove port from host if present
        if ":" in host:
            host = host.split(":")[0]

        logger.debug(
            "[CONTEXT] Detecting context",
            extra={
                "original_path": request.url.path,
                "clean_path": getattr(request.state, "clean_path", "NOT SET"),
                "path_to_check": path,
                "host": host,
            },
        )

        # 1. API context (highest priority)
        if path.startswith("/api/"):
            logger.debug("[CONTEXT] Detected as API", extra={"path": path})
            return RequestContext.API

        # 2. Admin context
        if ContextManager._is_admin_context(request, host, path):
            logger.debug(
                "[CONTEXT] Detected as ADMIN", extra={"path": path, "host": host}
            )
            return RequestContext.ADMIN

        # 3. Vendor Dashboard context (vendor management area)
        # Check both clean_path and original path for vendor dashboard
        original_path = request.url.path
        if ContextManager._is_vendor_dashboard_context(
            path
        ) or ContextManager._is_vendor_dashboard_context(original_path):
            logger.debug(
                "[CONTEXT] Detected as VENDOR_DASHBOARD",
                extra={"path": path, "original_path": original_path},
            )
            return RequestContext.VENDOR_DASHBOARD

        # 4. Shop context (vendor storefront)
        # Check if vendor context exists (set by vendor_context_middleware)
        if hasattr(request.state, "vendor") and request.state.vendor:
            # If we have a vendor and it's not admin or vendor dashboard, it's shop
            logger.debug(
                "[CONTEXT] Detected as SHOP (has vendor context)",
                extra={"vendor": request.state.vendor.name},
            )
            return RequestContext.SHOP

        # Also check shop-specific paths
        if path.startswith("/shop/"):
            logger.debug("[CONTEXT] Detected as SHOP (from path)", extra={"path": path})
            return RequestContext.SHOP

        # 5. Fallback for unknown contexts
        logger.debug("[CONTEXT] Detected as FALLBACK", extra={"path": path})
        return RequestContext.FALLBACK

    @staticmethod
    def _is_admin_context(request: Request, host: str, path: str) -> bool:
        """Check if request is in admin context."""
        # Admin subdomain (admin.platform.com)
        if host.startswith("admin."):
            return True

        # Admin path (/admin/*)
        if path.startswith("/admin"):
            return True

        return False

    @staticmethod
    def _is_vendor_dashboard_context(path: str) -> bool:
        """Check if request is in vendor dashboard context."""
        # Vendor dashboard paths (/vendor/{code}/*)
        # Note: This is the vendor management area, not the shop
        # Important: /vendors/{code}/shop/* should NOT match this
        if path.startswith("/vendor/") and not path.startswith("/vendors/"):
            return True

        return False


class ContextMiddleware(BaseHTTPMiddleware):
    """
    Middleware to detect and inject request context into request.state.

    Class-based middleware provides:
    - Better lifecycle management
    - Easier to test and extend
    - Standard ASGI pattern
    - Clear separation of concerns

    Runs SECOND in middleware chain (after vendor_context_middleware).
    Depends on:
        request.state.clean_path (set by vendor_context_middleware)
        request.state.vendor (set by vendor_context_middleware)

    Sets:
        request.state.context_type: RequestContext enum value
    """

    async def dispatch(self, request: Request, call_next):
        """
        Detect context and inject into request state.
        """
        # Detect context
        context_type = ContextManager.detect_context(request)

        # Inject into request state
        request.state.context_type = context_type

        # Log context detection with full details
        logger.debug(
            f"[CONTEXT_MIDDLEWARE] Context detected: {context_type.value}",
            extra={
                "path": request.url.path,
                "clean_path": getattr(request.state, "clean_path", "NOT SET"),
                "host": request.headers.get("host", ""),
                "context": context_type.value,
                "has_vendor": hasattr(request.state, "vendor")
                and request.state.vendor is not None,
            },
        )

        # Continue processing
        response = await call_next(request)
        return response


def get_request_context(request: Request) -> RequestContext:
    """
    Helper function to get current request context.

    Args:
        request: FastAPI request object

    Returns:
        RequestContext enum value (defaults to FALLBACK if not set)
    """
    return getattr(request.state, "context_type", RequestContext.FALLBACK)
