# app/modules/core/utils/page_context.py
"""
Module-driven page context builder.

This module provides a unified, dynamic context building system for all frontend
types. Instead of hardcoding module imports, it delegates context creation to
modules themselves through their registered context providers.

Architecture:
    Each module can register context providers in its definition.py:

        context_providers={
            FrontendType.PLATFORM: get_platform_context_contribution,
            FrontendType.STORE: get_store_context_contribution,
        }

    The context builder then:
    1. Determines which modules are enabled for the platform
    2. Calls each enabled module's context provider for the requested frontend
    3. Merges all contributions into a single context dict

Benefits:
    - No hardcoded module imports in this file
    - Modules are fully isolated - adding/removing a module doesn't require changes here
    - Main platform with no modules enabled loads nothing extra
    - Each module controls its own context contribution

Frontend Types:
    - PLATFORM: Marketing pages (homepage, pricing, signup)
    - ADMIN: Platform admin dashboard
    - STORE: Store/merchant dashboard
    - STOREFRONT: Customer-facing shop pages
"""

import logging
from typing import Any

from fastapi import Request
from sqlalchemy.orm import Session

from app.core.config import settings
from app.modules.enums import FrontendType
from app.utils.i18n import get_jinja2_globals

logger = logging.getLogger(__name__)


def get_context_for_frontend(
    frontend_type: FrontendType,
    request: Request,
    db: Session,
    **extra_context: Any,
) -> dict[str, Any]:
    """
    Build context dynamically for any frontend type.

    This is the main entry point for context building. It:
    1. Creates base context (request, i18n, platform info)
    2. Gets list of enabled modules for the platform
    3. Calls each module's context provider for this frontend type
    4. Merges all contributions

    Args:
        frontend_type: The frontend type (PLATFORM, ADMIN, STORE, STOREFRONT)
        request: FastAPI Request object
        db: Database session
        **extra_context: Additional context variables to include

    Returns:
        Dict of context variables for template rendering

    Example:
        context = get_context_for_frontend(
            FrontendType.PLATFORM,
            request,
            db,
            page_title="Home",
        )
    """
    # Import here to avoid circular imports
    from app.modules.registry import MODULES
    from app.modules.service import module_service

    # Get platform from middleware (may be None for unauthenticated contexts)
    platform = getattr(request.state, "platform", None)

    # Get language from request state (set by middleware)
    language = getattr(request.state, "language", "en")

    # Build base context (always present for all frontends)
    context = _build_base_context(request, platform, language)

    # Determine which modules are enabled for this platform
    if platform:
        enabled_module_codes = module_service.get_enabled_module_codes(db, platform.id)
        logger.info(
            f"[CONTEXT] Building {frontend_type.value} context for platform "
            f"'{platform.code}' with {len(enabled_module_codes)} enabled modules"
        )
    else:
        # No platform context - only core modules
        enabled_module_codes = {
            code for code, module in MODULES.items() if module.is_core
        }
        logger.info(
            f"[CONTEXT] Building {frontend_type.value} context (no platform) with "
            f"{len(enabled_module_codes)} core modules: {sorted(enabled_module_codes)}"
        )

    # Collect context from enabled modules that have providers for this frontend
    modules_with_providers = []
    modules_contributed = []

    for code in enabled_module_codes:
        module = MODULES.get(code)
        if module is None:
            continue

        if not module.has_context_provider(frontend_type):
            continue

        modules_with_providers.append(code)

        try:
            contribution = module.get_context_contribution(
                frontend_type, request, db, platform
            )
            if contribution:
                context.update(contribution)
                modules_contributed.append(f"{code}({len(contribution)} keys)")
                logger.debug(
                    f"[CONTEXT] Module '{code}' contributed: {list(contribution.keys())}"
                )
        except Exception as e:
            logger.warning(
                f"[CONTEXT] Module '{code}' context provider failed for "
                f"{frontend_type.value}: {e}"
            )

    if modules_contributed:
        logger.info(
            f"[CONTEXT] Context providers called: {', '.join(modules_contributed)}"
        )
    elif modules_with_providers:
        logger.info(
            f"[CONTEXT] {len(modules_with_providers)} modules have providers but none contributed"
        )

    # Pass enabled module codes to templates for conditional rendering
    context["enabled_modules"] = enabled_module_codes

    # Pass frontend type to templates (used by JS for logging, dev toolbar, etc.)
    context["frontend_type"] = frontend_type.value

    # For storefront, build nav menu structure from module declarations
    if frontend_type == FrontendType.STOREFRONT:
        from app.modules.core.services.menu_discovery_service import (
            menu_discovery_service,
        )

        platform_id = platform.id if platform else None
        sections = menu_discovery_service.get_menu_sections_for_frontend(
            db, FrontendType.STOREFRONT, platform_id
        )
        # Build dict of section_id -> list of enabled items for easy template access
        storefront_nav: dict[str, list] = {}
        for section in sections:
            enabled_items = [
                item for item in section.items if item.is_module_enabled
            ]
            if enabled_items:
                storefront_nav[section.id] = enabled_items
        context["storefront_nav"] = storefront_nav

    # Add any extra context passed by the caller
    if extra_context:
        context.update(extra_context)

    return context


def _build_base_context(
    request: Request,
    platform: Any,
    language: str,
) -> dict[str, Any]:
    """
    Build the base context that's present for all frontends.

    This includes:
    - request: FastAPI Request object
    - platform: Platform object (may be None)
    - i18n: Translation functions and language info
    - Basic platform settings from config

    Args:
        request: FastAPI Request object
        platform: Platform object or None
        language: Language code (e.g., "en", "fr")

    Returns:
        Base context dict
    """
    # Get i18n globals (_, t, current_language, etc.)
    i18n_globals = get_jinja2_globals(language)

    context = {
        "request": request,
        "platform": platform,
        "platform_name": settings.project_name,
        "main_domain": settings.main_domain,
    }

    # Add i18n globals
    context.update(i18n_globals)

    return context


# =============================================================================
# Convenience Functions (Wrappers for common frontend types)
# =============================================================================


def get_platform_context(
    request: Request,
    db: Session,
    **extra_context: Any,
) -> dict[str, Any]:
    """
    Build context for platform/marketing pages.

    This is a convenience wrapper for get_context_for_frontend(FrontendType.PLATFORM).

    Args:
        request: FastAPI Request object
        db: Database session
        **extra_context: Additional variables for template

    Returns:
        Context dict for platform pages
    """
    return get_context_for_frontend(
        FrontendType.PLATFORM,
        request,
        db,
        **extra_context,
    )


def get_admin_context(
    request: Request,
    db: Session,
    current_user: Any,
    **extra_context: Any,
) -> dict[str, Any]:
    """
    Build context for admin dashboard pages.

    Args:
        request: FastAPI Request object
        db: Database session
        current_user: Authenticated admin user
        **extra_context: Additional variables for template

    Returns:
        Context dict for admin pages
    """
    return get_context_for_frontend(
        FrontendType.ADMIN,
        request,
        db,
        user=current_user,
        **extra_context,
    )


def get_store_context(
    request: Request,
    db: Session,
    current_user: Any,
    store_code: str,
    **extra_context: Any,
) -> dict[str, Any]:
    """
    Build context for store dashboard pages.

    Args:
        request: FastAPI Request object
        db: Database session
        current_user: Authenticated store user
        store_code: Store subdomain/code
        **extra_context: Additional variables for template

    Returns:
        Context dict for store pages (includes user_permissions list)
    """
    # Resolve user permissions for the current store
    user_permissions = _resolve_store_user_permissions(db, current_user)

    return get_context_for_frontend(
        FrontendType.STORE,
        request,
        db,
        user=current_user,
        store_code=store_code,
        user_permissions=user_permissions,
        **extra_context,
    )


def _resolve_store_user_permissions(db: Session, current_user: Any) -> list[str]:
    """
    Resolve the permission list for a store user.

    Returns all permission IDs for the current user in their current store.
    Owners get all permissions. Members get permissions from their role.
    """
    from app.modules.tenancy.models import User

    store_id = getattr(current_user, "token_store_id", None)
    if not store_id:
        return []

    user_id = getattr(current_user, "user_id", None) or getattr(
        current_user, "id", None
    )
    if not user_id:
        return []

    user_model = db.query(User).filter(User.id == user_id).first()
    if not user_model:
        return []

    # Owners get all permissions
    if user_model.is_owner_of(store_id):
        from app.modules.tenancy.services.permission_discovery_service import (
            permission_discovery_service,
        )

        return list(permission_discovery_service.get_all_permission_ids())

    # Members get permissions from their store role
    for membership in user_model.store_memberships:
        if membership.store_id == store_id and membership.is_active:
            return membership.get_all_permissions()

    return []


def get_storefront_context(
    request: Request,
    db: Session | None = None,
    **extra_context: Any,
) -> dict[str, Any]:
    """
    Build context for storefront (customer shop) pages.

    Args:
        request: FastAPI Request object with store/theme in state
        db: Optional database session
        **extra_context: Additional variables for template

    Returns:
        Context dict for storefront pages
    """
    # Extract store and theme from middleware state
    store = getattr(request.state, "store", None)
    theme = getattr(request.state, "theme", None)
    clean_path = getattr(request.state, "clean_path", request.url.path)
    store_context = getattr(request.state, "store_context", None)

    # Get detection method from store_context
    access_method = (
        store_context.get("detection_method", "unknown")
        if store_context
        else "unknown"
    )

    # Calculate base URL for links
    # Dev path-based: /platforms/{code}/storefront/{store_code}/
    # Prod subdomain/custom domain: /
    base_url = "/"
    if access_method == "path" and store:
        platform = getattr(request.state, "platform", None)
        platform_original_path = getattr(request.state, "platform_original_path", None)
        # Use subdomain (lowercase, hyphens) for URL routing — store_code is for internal use
        store_slug = store.subdomain or store.store_code
        if platform and platform_original_path and platform_original_path.startswith("/platforms/"):
            base_url = f"/platforms/{platform.code}/storefront/{store_slug}/"
        else:
            full_prefix = (
                store_context.get("full_prefix", "/storefront/")
                if store_context
                else "/storefront/"
            )
            base_url = f"{full_prefix}{store_slug}/"

    # Read subscription info set by StorefrontAccessMiddleware
    subscription = getattr(request.state, "subscription", None)
    subscription_tier = getattr(request.state, "subscription_tier", None)

    # Build storefront-specific base context
    storefront_base = {
        "store": store,
        "theme": theme,
        "clean_path": clean_path,
        "access_method": access_method,
        "base_url": base_url,
        "subscription": subscription,
        "subscription_tier": subscription_tier,
        "tier_code": subscription_tier.code if subscription_tier else None,
    }

    # If no db session, return just the base context with safe defaults
    if db is None:
        context = _build_base_context(
            request,
            getattr(request.state, "platform", None),
            getattr(request.state, "language", "en"),
        )
        context.update(storefront_base)
        context["enabled_modules"] = set()
        context["storefront_nav"] = {}
        context.update(extra_context)
        return context

    # Full context with module contributions
    # (get_context_for_frontend computes enabled_modules and storefront_nav)
    return get_context_for_frontend(
        FrontendType.STOREFRONT,
        request,
        db,
        **storefront_base,
        **extra_context,
    )
