# app/core/frontend_detector.py
"""
Centralized Frontend Detection

Single source of truth for detecting which frontend type a request targets.
Handles both development (path-based) and production (domain-based) routing.

Detection priority:
1. Admin subdomain (admin.omsflow.lu)
2. Path-based admin/store (/admin/*, /store/*, /api/v1/admin/*)
3. Custom domain lookup (mybakery.lu -> STOREFRONT)
4. Store subdomain (orion.omsflow.lu -> STOREFRONT)
5. Storefront paths (/storefront/*, /api/v1/storefront/*)
6. Default to PLATFORM (marketing pages)

This module unifies frontend detection that was previously duplicated across:
- middleware/platform_context.py
- middleware/store_context.py
- middleware/context.py

All middleware and routes should use FrontendDetector for frontend detection.
"""

import logging

from app.modules.enums import FrontendType

logger = logging.getLogger(__name__)


class FrontendDetector:
    """
    Centralized frontend detection for dev and prod modes.

    Provides consistent detection of frontend type from request characteristics.
    All path/domain detection logic should be centralized here.
    """

    # Reserved subdomains (not store shops)
    RESERVED_SUBDOMAINS = frozenset({"www", "admin", "api", "store", "portal"})

    # Path patterns for each frontend type
    # Note: Order matters - more specific patterns should be checked first
    ADMIN_PATH_PREFIXES = ("/admin", "/api/v1/admin")
    STORE_PATH_PREFIXES = ("/store/", "/api/v1/store")  # Note: /store/ not /stores/
    STOREFRONT_PATH_PREFIXES = (
        "/storefront",
        "/api/v1/storefront",
    )
    MERCHANT_PATH_PREFIXES = ("/merchants", "/api/v1/merchants")
    PLATFORM_PATH_PREFIXES = ("/api/v1/platform",)

    @classmethod
    def detect(
        cls,
        host: str,
        path: str,
        has_store_context: bool = False,
    ) -> FrontendType:
        """
        Detect frontend type from request.

        Args:
            host: Request host header (e.g., "omsflow.lu", "orion.omsflow.lu", "localhost:8000")
            path: Request path (e.g., "/admin/stores", "/storefront/products")
            has_store_context: True if request.state.store is set (from middleware)

        Returns:
            FrontendType enum value
        """
        host = cls._strip_port(host)
        subdomain = cls._get_subdomain(host)

        logger.debug(
            "[FRONTEND_DETECTOR] Detecting frontend type",
            extra={
                "host": host,
                "path": path,
                "subdomain": subdomain,
                "has_store_context": has_store_context,
            },
        )

        # 1. Admin subdomain (admin.omsflow.lu)
        if subdomain == "admin":
            logger.debug("[FRONTEND_DETECTOR] Detected ADMIN from subdomain")
            return FrontendType.ADMIN

        # 2. Path-based detection (works for dev and prod)
        # Check in priority order
        if cls._matches_any(path, cls.ADMIN_PATH_PREFIXES):
            logger.debug("[FRONTEND_DETECTOR] Detected ADMIN from path")
            return FrontendType.ADMIN

        if cls._matches_any(path, cls.MERCHANT_PATH_PREFIXES):
            logger.debug("[FRONTEND_DETECTOR] Detected MERCHANT from path")
            return FrontendType.MERCHANT

        # Check storefront BEFORE store since /api/v1/storefront starts with /api/v1/store
        if cls._matches_any(path, cls.STOREFRONT_PATH_PREFIXES):
            logger.debug("[FRONTEND_DETECTOR] Detected STOREFRONT from path")
            return FrontendType.STOREFRONT

        if cls._matches_any(path, cls.STORE_PATH_PREFIXES):
            logger.debug("[FRONTEND_DETECTOR] Detected STORE from path")
            return FrontendType.STORE

        if cls._matches_any(path, cls.PLATFORM_PATH_PREFIXES):
            logger.debug("[FRONTEND_DETECTOR] Detected PLATFORM from path")
            return FrontendType.PLATFORM

        # 3. Store subdomain detection (orion.omsflow.lu)
        # If subdomain exists and is not reserved -> it's a store storefront
        if subdomain and subdomain not in cls.RESERVED_SUBDOMAINS:
            logger.debug(
                f"[FRONTEND_DETECTOR] Detected STOREFRONT from subdomain: {subdomain}"
            )
            return FrontendType.STOREFRONT

        # 4. Custom domain detection (handled by middleware setting store context)
        # If store is set but no storefront path -> still storefront
        if has_store_context:
            logger.debug(
                "[FRONTEND_DETECTOR] Detected STOREFRONT from store context"
            )
            return FrontendType.STOREFRONT

        # 5. Default: PLATFORM (marketing pages like /, /pricing, /about)
        logger.debug("[FRONTEND_DETECTOR] Defaulting to PLATFORM")
        return FrontendType.PLATFORM

    @classmethod
    def _strip_port(cls, host: str) -> str:
        """Remove port from host if present (e.g., localhost:8000 -> localhost)."""
        return host.split(":")[0] if ":" in host else host

    @classmethod
    def _get_subdomain(cls, host: str) -> str | None:
        """
        Extract subdomain from host (e.g., 'orion' from 'orion.omsflow.lu').

        Returns None for localhost, IP addresses, or root domains.
        Handles special case of admin.localhost for development.
        """
        if host in ("localhost", "127.0.0.1"):
            return None

        parts = host.split(".")

        # Handle localhost subdomains (e.g., admin.localhost)
        if len(parts) == 2 and parts[1] == "localhost":
            return parts[0].lower()

        if len(parts) >= 3:  # subdomain.domain.tld
            return parts[0].lower()

        return None

    @classmethod
    def _matches_any(cls, path: str, prefixes: tuple[str, ...]) -> bool:
        """Check if path starts with any of the given prefixes."""
        return any(path.startswith(prefix) for prefix in prefixes)

    # =========================================================================
    # Convenience methods for specific frontend types
    # =========================================================================

    @classmethod
    def is_admin(cls, host: str, path: str) -> bool:
        """Check if request targets admin frontend."""
        return cls.detect(host, path) == FrontendType.ADMIN

    @classmethod
    def is_store(cls, host: str, path: str) -> bool:
        """Check if request targets store dashboard frontend."""
        return cls.detect(host, path) == FrontendType.STORE

    @classmethod
    def is_storefront(
        cls,
        host: str,
        path: str,
        has_store_context: bool = False,
    ) -> bool:
        """Check if request targets storefront frontend."""
        return cls.detect(host, path, has_store_context) == FrontendType.STOREFRONT

    @classmethod
    def is_platform(cls, host: str, path: str) -> bool:
        """Check if request targets platform marketing frontend."""
        return cls.detect(host, path) == FrontendType.PLATFORM

    @classmethod
    def is_api_request(cls, path: str) -> bool:
        """Check if request is for API endpoints (any frontend's API)."""
        return path.startswith("/api/")
