"""Core Context Processors

This module contains context processors that add common variables
to template contexts across the Adtlas project.
"""

import logging
from typing import Dict, Any

from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.cache import cache
from django.http import HttpRequest
from django.urls import reverse
from django.utils import timezone

from .constants import (
    APP_NAME, APP_VERSION, APP_DESCRIPTION,
    DEFAULT_PAGE_SIZE, CACHE_KEY_SETTINGS, CACHE_KEY_USER_PERMISSIONS,
    CACHE_KEY_USER_ROLES, NOTIFICATION_TYPE_CHOICES
)
from .models import Setting
from .utils import format_datetime, get_client_ip


User = get_user_model()
logger = logging.getLogger(__name__)


# ============================================================================
# Application Context Processor
# ============================================================================

def app_context(request: HttpRequest) -> Dict[str, Any]:
    """Add application-wide context variables.
    
    Args:
        request: HTTP request object
        
    Returns:
        Dictionary of context variables
    """
    return {
        'app_name': APP_NAME,
        'app_version': APP_VERSION,
        'app_description': APP_DESCRIPTION,
        'current_year': timezone.now().year,
        'debug_mode': settings.DEBUG,
        'environment': getattr(settings, 'ENVIRONMENT', 'development'),
    }


# ============================================================================
# Settings Context Processor
# ============================================================================

def settings_context(request: HttpRequest) -> Dict[str, Any]:
    """Add application settings to context.
    
    Args:
        request: HTTP request object
        
    Returns:
        Dictionary of context variables
    """
    # Try to get settings from cache first
    app_settings = cache.get(CACHE_KEY_SETTINGS)
    
    if app_settings is None:
        try:
            # Get public settings from database
            settings_queryset = Setting.objects.filter(is_public=True)
            app_settings = {}
            
            for setting in settings_queryset:
                app_settings[setting.key] = setting.get_value()
            
            # Add default settings if not in database
            default_settings = {
                'site_name': APP_NAME,
                'site_description': APP_DESCRIPTION,
                'default_page_size': DEFAULT_PAGE_SIZE,
                'enable_notifications': True,
                'enable_dark_mode': True,
                'enable_breadcrumbs': True,
                'enable_search': True,
                'maintenance_mode': False,
            }
            
            for key, value in default_settings.items():
                if key not in app_settings:
                    app_settings[key] = value
            
            # Cache settings for 5 minutes
            cache.set(CACHE_KEY_SETTINGS, app_settings, 300)
            
        except Exception as e:
            logger.error(f"Failed to load settings: {e}")
            app_settings = {
                'site_name': APP_NAME,
                'site_description': APP_DESCRIPTION,
                'default_page_size': DEFAULT_PAGE_SIZE,
            }
    
    return {
        'app_settings': app_settings,
        'site_name': app_settings.get('site_name', APP_NAME),
        'site_description': app_settings.get('site_description', APP_DESCRIPTION),
    }


# ============================================================================
# User Context Processor
# ============================================================================

def user_context(request: HttpRequest) -> Dict[str, Any]:
    """Add user-specific context variables.
    
    Args:
        request: HTTP request object
        
    Returns:
        Dictionary of context variables
    """
    context = {
        'user_permissions': [],
        'user_roles': [],
        'user_menu': [],
        'unread_notifications': 0,
        'user_avatar_url': None,
        'user_full_name': '',
        'user_initials': '',
        'is_admin': False,
        'is_manager': False,
    }
    
    if not request.user.is_authenticated:
        return context
    
    user = request.user
    
    # Basic user info
    context.update({
        'user_full_name': user.get_full_name() if hasattr(user, 'get_full_name') else f"{user.first_name} {user.last_name}".strip(),
        'user_initials': get_user_initials(user),
        'is_admin': user.is_staff or user.is_superuser,
        'is_manager': user.groups.filter(name='Manager').exists() if hasattr(user, 'groups') else False,
    })
    
    # User avatar
    if hasattr(user, 'avatar') and user.avatar:
        context['user_avatar_url'] = user.avatar.url
    
    # User permissions (cached)
    cache_key = CACHE_KEY_USER_PERMISSIONS.format(user_id=user.id)
    user_permissions = cache.get(cache_key)
    
    if user_permissions is None:
        try:
            user_permissions = list(user.get_all_permissions())
            cache.set(cache_key, user_permissions, 300)  # Cache for 5 minutes
        except Exception as e:
            logger.error(f"Failed to get user permissions: {e}")
            user_permissions = []
    
    context['user_permissions'] = user_permissions
    
    # User roles (cached)
    cache_key = CACHE_KEY_USER_ROLES.format(user_id=user.id)
    user_roles = cache.get(cache_key)
    
    if user_roles is None:
        try:
            if hasattr(user, 'roles'):
                user_roles = [role.name for role in user.roles.all()]
            elif hasattr(user, 'groups'):
                user_roles = [group.name for group in user.groups.all()]
            else:
                user_roles = []
            
            cache.set(cache_key, user_roles, 300)  # Cache for 5 minutes
        except Exception as e:
            logger.error(f"Failed to get user roles: {e}")
            user_roles = []
    
    context['user_roles'] = user_roles
    
    # User menu
    context['user_menu'] = get_user_menu(user)
    
    # Unread notifications count
    try:
        if hasattr(user, 'notifications'):
            context['unread_notifications'] = user.notifications.filter(is_read=False).count()
    except Exception as e:
        logger.error(f"Failed to get unread notifications count: {e}")
    
    return context


# ============================================================================
# Request Context Processor
# ============================================================================

def request_context(request: HttpRequest) -> Dict[str, Any]:
    """Add request-specific context variables.
    
    Args:
        request: HTTP request object
        
    Returns:
        Dictionary of context variables
    """
    return {
        'request_path': request.path,
        'request_method': request.method,
        'is_ajax': getattr(request, 'is_ajax', False),
        'is_mobile': getattr(request, 'is_mobile', False),
        'client_ip': get_client_ip(request),
        'user_agent': request.META.get('HTTP_USER_AGENT', ''),
        'referer': request.META.get('HTTP_REFERER', ''),
        'current_url': request.build_absolute_uri(),
        'query_string': request.META.get('QUERY_STRING', ''),
    }


# ============================================================================
# Navigation Context Processor
# ============================================================================

def navigation_context(request: HttpRequest) -> Dict[str, Any]:
    """Add navigation context variables.
    
    Args:
        request: HTTP request object
        
    Returns:
        Dictionary of context variables
    """
    # Build breadcrumbs
    breadcrumbs = build_breadcrumbs(request)
    
    # Main navigation menu
    main_menu = get_main_menu(request.user if request.user.is_authenticated else None)
    
    # Admin menu (for staff users)
    admin_menu = []
    if request.user.is_authenticated and (request.user.is_staff or request.user.is_superuser):
        admin_menu = get_admin_menu()
    
    return {
        'breadcrumbs': breadcrumbs,
        'main_menu': main_menu,
        'admin_menu': admin_menu,
        'current_section': get_current_section(request.path),
    }


# ============================================================================
# Form Context Processor
# ============================================================================

def form_context(request: HttpRequest) -> Dict[str, Any]:
    """Add form-related context variables.
    
    Args:
        request: HTTP request object
        
    Returns:
        Dictionary of context variables
    """
    return {
        'csrf_token': request.META.get('CSRF_COOKIE'),
        'form_errors': getattr(request, 'form_errors', {}),
        'form_data': getattr(request, 'form_data', {}),
        'notification_types': dict(NOTIFICATION_TYPE_CHOICES),
    }


# ============================================================================
# Performance Context Processor
# ============================================================================

def performance_context(request: HttpRequest) -> Dict[str, Any]:
    """Add performance-related context variables.
    
    Args:
        request: HTTP request object
        
    Returns:
        Dictionary of context variables
    """
    context = {
        'page_load_time': None,
        'db_queries': None,
        'cache_hits': None,
    }
    
    # Only add performance data in debug mode
    if settings.DEBUG:
        # Page load time
        if hasattr(request, 'start_time'):
            import time
            context['page_load_time'] = round((time.time() - request.start_time) * 1000, 2)  # in milliseconds
        
        # Database queries count
        from django.db import connection
        context['db_queries'] = len(connection.queries)
    
    return context


# ============================================================================
# Utility Functions
# ============================================================================

def get_user_initials(user) -> str:
    """Get user initials.
    
    Args:
        user: User instance
        
    Returns:
        User initials string
    """
    if user.first_name and user.last_name:
        return f"{user.first_name[0]}{user.last_name[0]}".upper()
    elif user.first_name:
        return user.first_name[0].upper()
    elif user.last_name:
        return user.last_name[0].upper()
    elif user.username:
        return user.username[0].upper()
    else:
        return 'U'


def get_user_menu(user) -> list:
    """Get user-specific menu items.
    
    Args:
        user: User instance
        
    Returns:
        List of menu items
    """
    menu = [
        {
            'title': 'Profile',
            'url': reverse('accounts:profile'),
            'icon': 'fas fa-user',
            'active': False,
        },
        {
            'title': 'Settings',
            'url': reverse('accounts:settings') if 'accounts:settings' in [url.name for url in reverse('accounts:profile')] else '#',
            'icon': 'fas fa-cog',
            'active': False,
        },
        {
            'title': 'Logout',
            'url': reverse('accounts:logout'),
            'icon': 'fas fa-sign-out-alt',
            'active': False,
        },
    ]
    
    return menu


def get_main_menu(user) -> list:
    """Get main navigation menu.
    
    Args:
        user: User instance or None
        
    Returns:
        List of menu items
    """
    menu = [
        {
            'title': 'Dashboard',
            'url': reverse('core:dashboard') if user else '/',
            'icon': 'fas fa-tachometer-alt',
            'active': False,
            'permission': None,
        },
    ]
    
    # Add menu items based on user permissions
    if user and user.is_authenticated:
        # Add user management for staff
        if user.is_staff or user.has_perm('accounts.view_user'):
            menu.append({
                'title': 'Users',
                'url': reverse('accounts:user_list'),
                'icon': 'fas fa-users',
                'active': False,
                'permission': 'accounts.view_user',
            })
        
        # Add admin menu for superusers
        if user.is_superuser:
            menu.append({
                'title': 'Administration',
                'url': '/admin/',
                'icon': 'fas fa-cogs',
                'active': False,
                'permission': None,
            })
    
    return menu


def get_admin_menu() -> list:
    """Get admin menu items.
    
    Returns:
        List of admin menu items
    """
    return [
        {
            'title': 'Django Admin',
            'url': '/admin/',
            'icon': 'fas fa-cogs',
            'active': False,
        },
        {
            'title': 'System Settings',
            'url': reverse('core:settings') if 'core:settings' in [url.name for url in []] else '#',
            'icon': 'fas fa-sliders-h',
            'active': False,
        },
        {
            'title': 'Activity Logs',
            'url': reverse('core:activity_logs') if 'core:activity_logs' in [url.name for url in []] else '#',
            'icon': 'fas fa-list-alt',
            'active': False,
        },
    ]


def build_breadcrumbs(request: HttpRequest) -> list:
    """Build breadcrumbs for current page.
    
    Args:
        request: HTTP request object
        
    Returns:
        List of breadcrumb items
    """
    breadcrumbs = [
        {
            'title': 'Home',
            'url': '/',
            'active': False,
        }
    ]
    
    # Parse URL path to build breadcrumbs
    path_parts = [part for part in request.path.split('/') if part]
    
    if path_parts:
        current_path = ''
        
        for i, part in enumerate(path_parts):
            current_path += f'/{part}'
            
            # Skip numeric IDs in breadcrumbs
            if part.isdigit():
                continue
            
            # Format part name
            title = part.replace('_', ' ').replace('-', ' ').title()
            
            # Check if this is the last part
            is_active = (i == len(path_parts) - 1)
            
            breadcrumbs.append({
                'title': title,
                'url': current_path if not is_active else None,
                'active': is_active,
            })
    
    return breadcrumbs


def get_current_section(path: str) -> str:
    """Get current section from URL path.
    
    Args:
        path: URL path
        
    Returns:
        Current section name
    """
    path_parts = [part for part in path.split('/') if part]
    
    if not path_parts:
        return 'home'
    
    # Return first path part as section
    return path_parts[0]


def get_page_title(request: HttpRequest) -> str:
    """Get page title based on current URL.
    
    Args:
        request: HTTP request object
        
    Returns:
        Page title string
    """
    # Try to get title from view
    if hasattr(request, 'resolver_match') and request.resolver_match:
        view_name = request.resolver_match.view_name
        
        # Map view names to titles
        title_map = {
            'core:dashboard': 'Dashboard',
            'accounts:login': 'Login',
            'accounts:logout': 'Logout',
            'accounts:profile': 'Profile',
            'accounts:user_list': 'Users',
        }
        
        if view_name in title_map:
            return title_map[view_name]
    
    # Fallback to path-based title
    path_parts = [part for part in request.path.split('/') if part and not part.isdigit()]
    
    if not path_parts:
        return 'Home'
    
    return path_parts[-1].replace('_', ' ').replace('-', ' ').title()


# ============================================================================
# Combined Context Processor
# ============================================================================

def core_context(request: HttpRequest) -> Dict[str, Any]:
    """Combined context processor for all core context variables.
    
    Args:
        request: HTTP request object
        
    Returns:
        Dictionary of all context variables
    """
    context = {}
    
    # Combine all context processors
    context.update(app_context(request))
    context.update(settings_context(request))
    context.update(user_context(request))
    context.update(request_context(request))
    context.update(navigation_context(request))
    context.update(form_context(request))
    
    # Add performance context only in debug mode
    if settings.DEBUG:
        context.update(performance_context(request))
    
    # Add page title
    context['page_title'] = get_page_title(request)
    
    return context