from django.conf import settings
from django.contrib.auth import logout
from django.utils import timezone
from datetime import timedelta
from django.shortcuts import redirect
from django.contrib import messages
from django.urls import reverse
from django.http import Http404, HttpResponseForbidden
from django.core.exceptions import PermissionDenied
from .permissions import has_financial_access, can_assign_tickets, can_delete_customers
import logging

logger = logging.getLogger(__name__)

class CustomerOnlyMiddleware:
    """Middleware to restrict certain views to customers only"""

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        return response

class DepartmentPermissionMiddleware:
    """Middleware to enforce department-based permissions"""

    def __init__(self, get_response):
        self.get_response = get_response
        # Define URL patterns that require specific permissions
        self.protected_urls = {
            'financial': [
                '/billing/', '/payments/', '/customers/invoices/',
                '/customers/payments/', '/customers/balance/', '/expenditure/'
            ],
            'hr': [
                '/hr/employees/', '/hr/payslip/', '/hr/deduction/', '/hr/bonus/',
                '/hr/department/', '/hr/generate-payslips/'
            ],
            'settings': [
                '/settings/system/', '/settings/dashboard/', '/admin/'
            ],
            'ticket_assignment': [
                '/tickets/assign/', '/tickets/schedule/'
            ],
            'customer_management': [
                '/customers/add/', '/customers/create/', '/customers/.*/edit/',
                '/customers/.*/delete/'
            ]
        }

    def __call__(self, request):
        response = self.get_response(request)
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        """Process view before it's called"""
        try:
            # Only check permissions for authenticated users
            if request.user.is_authenticated:
                result = self._check_url_permissions(request)
                if result:  # If _check_url_permissions returns a response, return it
                    return result

            # Continue with normal processing
            return None

        except Exception as e:
            logger.error(f"Middleware process_view error: {str(e)}")
            return None

    def _check_url_permissions(self, request):
        """Check if user has permission to access the current URL"""
        try:
            # Skip permission checks for certain URLs
            skip_urls = [
                '/admin/',
                '/accounts/login/',
                '/accounts/logout/',
                '/accounts/employee-login/',
                '/accounts/customer-login/',
                '/static/',
                '/media/',
                '/api/',
            ]

            current_path = request.path_info

            # Skip if URL should be skipped
            for skip_url in skip_urls:
                if current_path.startswith(skip_url):
                    return

            # Skip for superusers
            if request.user.is_superuser:
                return

            # Check permissions based on user type and department
            if hasattr(request.user, 'employee_profile'):
                if not self._has_url_permission(request.user, current_path):
                    # Don't call self.get_response again - this causes recursion!
                    return HttpResponseForbidden("You don't have permission to access this page.")

        except Exception as e:
            # Log the error but don't block the request
            logger.error(f"Permission check error: {str(e)}")
            pass

    def _has_url_permission(self, user, path):
        """Helper to check permissions for a given user and path"""
        from .permissions import get_user_permissions

        user_permissions = get_user_permissions(user)

        # Check financial URLs (excluding expenditure for marketing)
        financial_urls = [url for url in self.protected_urls['financial'] if not url.startswith('/expenditure/')]
        if any(url in path for url in financial_urls):
            if 'view_billing' not in user_permissions and 'manage_billing' not in user_permissions:
                raise PermissionDenied("Access to financial modules requires billing permissions. Contact your department manager.")
        
        # Check expenditure URLs specifically
        if '/expenditure/' in path:
            if ('view_expense' not in user_permissions and 'add_expense' not in user_permissions and 
                'view_billing' not in user_permissions and 'manage_billing' not in user_permissions):
                raise PermissionDenied("Access to expense management requires appropriate department permissions.")

        # Check HR URLs
        if any(url in path for url in self.protected_urls['hr']):
            if 'view_employees' not in user_permissions and 'manage_employees' not in user_permissions:
                raise PermissionDenied("Access to HR modules requires employee management permissions. Contact system administrator.")

        # Check settings URLs
        if any(url in path for url in self.protected_urls['settings']):
            if not user.is_superuser:
                raise PermissionDenied("System settings are restricted to administrators only.")

        # Check ticket assignment URLs
        if any(url in path for url in self.protected_urls['ticket_assignment']):
            if 'assign_tickets' not in user_permissions:
                raise PermissionDenied("Ticket assignment requires technician or management privileges.")

        # Check customer management URLs
        if any(url in path for url in self.protected_urls['customer_management']):
            if path.endswith('/delete/') and 'delete_customers' not in user_permissions:
                raise PermissionDenied("Customer deletion requires management approval.")
            elif '/add/' in path or '/create/' in path:
                if 'add_customers' not in user_permissions and 'add_customer' not in user_permissions:
                    raise PermissionDenied("Adding customers requires appropriate department permissions.")
        
        return True # If no PermissionDenied was raised, access is granted.

    def _check_financial_access(self, request):
        """Check if user has access to financial URLs"""
        path = request.path
        if any(url in path for url in self.financial_urls):
            if not has_financial_access(request.user):
                raise PermissionDenied("Financial access required")

    def _check_ticket_assignment_access(self, request):
        """Check if user can assign tickets"""
        path = request.path
        if any(url in path for url in self.ticket_assignment_urls):
            if not can_assign_tickets(request.user):
                raise PermissionDenied("Ticket assignment access required")

    def _check_customer_delete_access(self, request):
        """Check if user can delete customers"""
        path = request.path
        if any(url in path for url in self.customer_delete_urls) and request.method in ['POST', 'DELETE']:
            if not can_delete_customers(request.user):
                raise PermissionDenied("Customer deletion access required")

class SessionTimeoutMiddleware:
    """Middleware to handle different session timeouts for different user types"""

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        if request.user.is_authenticated:
            # Get appropriate timeout based on user type
            if hasattr(request.user, 'employee_profile') and request.user.employee_profile:
                timeout = getattr(settings, 'EMPLOYEE_SESSION_TIMEOUT', 3600 * 8)
            elif hasattr(request.user, 'customer_profile') and request.user.customer_profile:
                timeout = getattr(settings, 'CUSTOMER_SESSION_TIMEOUT', 3600 * 2)
            elif request.user.is_superuser or request.user.is_staff:
                timeout = getattr(settings, 'ADMIN_SESSION_TIMEOUT', 3600 * 4)
            else:
                timeout = getattr(settings, 'SESSION_COOKIE_AGE', 3600)

            # Skip timeout check for logout URLs
            if not request.path.startswith('/logout') and not request.path.startswith('/accounts/logout'):
                # Check if session should expire
                last_activity = request.session.get('last_activity')
                if last_activity:
                    elapsed = timezone.now().timestamp() - last_activity
                    if elapsed > timeout:
                        # Store user type before forced logout
                        if hasattr(request.user, 'customer_profile') and request.user.customer_profile:
                            request.session['logout_user_type'] = 'customer'
                        else:
                            request.session['logout_user_type'] = 'employee'
                        logout(request)
                        messages.info(request, 'Your session has expired. Please log in again.')
                        return self.get_response(request)

                # Update last activity only if not logging out
                request.session['last_activity'] = timezone.now().timestamp()

        response = self.get_response(request)
        return response


class ActivityLoggingMiddleware:
    """Middleware to log user activity for security and audit purposes"""

    def __init__(self, get_response):
        self.get_response = get_response
        self.sensitive_paths = [
            '/admin/',
            '/accounts/',
            '/customers/',
            '/billing/',
            '/hr/',
            '/settings/'
        ]

    def __call__(self, request):
        # Log activity before processing request
        if request.user.is_authenticated and self.should_log(request):
            self.log_activity(request)

        response = self.get_response(request)
        return response

    def should_log(self, request):
        """Determine if this request should be logged"""
        # Log sensitive paths
        if any(request.path.startswith(path) for path in self.sensitive_paths):
            return True

        # Log POST requests (form submissions, data changes)
        if request.method in ['POST', 'PUT', 'DELETE']:
            return True

        # Don't log static files, heartbeat, or frequent AJAX calls
        if any(request.path.startswith(path) for path in ['/static/', '/media/', '/api/heartbeat/']):
            return False

        return False

    def log_activity(self, request):
        """Log user activity"""
        try:
            UserActivityLog.objects.create(
                user=request.user,
                action=f"{request.method} {request.path}",
                ip_address=self.get_client_ip(request),
                user_agent=request.META.get('HTTP_USER_AGENT', '')[:500],
                additional_data={
                    'method': request.method,
                    'path': request.path,
                    'query_params': dict(request.GET),
                    'session_key': request.session.session_key
                }
            )
        except Exception:
            # Don't let logging errors break the application
            pass

    def get_client_ip(self, request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip