from functools import wraps
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import PermissionDenied
from django.contrib import messages
from django.shortcuts import redirect
from django.http import HttpResponseForbidden
from django.template import loader
from django.contrib.auth.models import Permission
from django.contrib.auth.decorators import user_passes_test
from hr.models import Department
from .models import GroupPermissionManager, DepartmentPermissionAssignment

def has_financial_access(user):
    """Check if user has access to financial information"""
    return has_permission(user, 'view_billing')


def can_assign_tickets(user):
    """Check if user can assign tickets to technicians"""
    return has_permission(user, 'assign_tickets')


def can_delete_customers(user):
    """Check if user can delete customers"""
    return has_permission(user, 'delete_customers')


def is_admin(user):
    """Check if user is an admin"""
    # Superusers are ALWAYS admins
    if user.is_superuser:
        return True

    return (hasattr(user, 'employee_profile') and 
            user.employee_profile and 
            user.employee_profile.department and 
            user.employee_profile.department.name == 'Management')


def get_user_permissions(user):
    """Get all permissions for a user based on their role and department"""
    permissions = set()

    if not user.is_authenticated:
        return permissions

    # Superusers get all permissions
    if user.is_superuser:
        all_permissions = GroupPermissionManager.objects.filter(is_active=True).values_list('name', flat=True)
        return set(all_permissions)

    # Get permissions from Django groups (this handles the hardcoded group permissions)
    user_groups = user.groups.all()
    for group in user_groups:
        group_permissions = group.permissions.all()
        for perm in group_permissions:
            # Add the permission codename to our set
            permissions.add(perm.codename)
            # Also add common permission patterns
            if perm.codename.startswith('add_'):
                permissions.add('create_' + perm.codename[4:])
            elif perm.codename.startswith('change_'):
                permissions.add('edit_' + perm.codename[7:])
            elif perm.codename.startswith('view_'):
                permissions.add('view_' + perm.codename[5:])
            elif perm.codename.startswith('delete_'):
                permissions.add('delete_' + perm.codename[7:])

    # Staff users get basic permissions
    if user.is_staff:
        permissions.update({
            'view_customers', 'view_tickets', 'view_calendar'
        })

    # Dynamic department-specific permissions from our custom system
    if hasattr(user, 'employee_profile') and user.employee_profile and user.employee_profile.department:
        department = user.employee_profile.department

        # Get assigned permissions from database
        assigned_permissions = DepartmentPermissionAssignment.objects.filter(
            department=department,
            permission__is_active=True
        ).select_related('permission')

        for assignment in assigned_permissions:
            permissions.add(assignment.permission.name)

    return permissions


def has_permission(user, permission):
    """Check if user has a specific permission"""
    if not user.is_authenticated:
        return False

    # Superusers have ALL permissions - no exceptions
    if user.is_superuser:
        return True

    # Check Django model permissions first (covers app.permission format)
    if ('.' in permission):
        if user.has_perm(permission):
            return True
    else:
        # Check common app permissions
        common_apps = ['customers', 'tickets', 'expenditure', 'billing', 'hr', 'payments', 'settings', 'network', 'notifications']
        for app in common_apps:
            if user.has_perm(f'{app}.{permission}'):
                return True

    # Check our custom permission system
    user_permissions = get_user_permissions(user)
    return permission in user_permissions


def permission_required(permission):
    """Decorator to require specific permission"""
    def decorator(view_func):
        @wraps(view_func)
        @login_required
        def _wrapped_view(request, *args, **kwargs):
            # Check Django model permissions first
            if (request.user.has_perm(f'customers.{permission}') or 
                request.user.has_perm(f'tickets.{permission}') or
                has_permission(request.user, permission)):
                return view_func(request, *args, **kwargs)
            raise PermissionDenied(f"Permission '{permission}' required")
        return _wrapped_view
    return decorator


def any_permission_required(*permissions):
    """Decorator to require any of the specified permissions"""
    def decorator(view_func):
        @wraps(view_func)
        @login_required
        def _wrapped_view(request, *args, **kwargs):
            user_permissions = get_user_permissions(request.user)
            # Flatten permissions in case a list is passed
            flattened_permissions = []
            for perm in permissions:
                if isinstance(perm, (list, tuple)):
                    flattened_permissions.extend(perm)
                else:
                    flattened_permissions.append(perm)

            if not any(perm in user_permissions for perm in flattened_permissions):
                raise PermissionDenied(f"One of these permissions required: {', '.join(flattened_permissions)}")
            return view_func(request, *args, **kwargs)
        return _wrapped_view
    return decorator


# Legacy function compatibility - All grant full access to superusers
def has_management_privileges(user):
    """Check if user has management/superuser privileges"""
    return user.is_superuser


def can_access_settings(user):
    """Check if user can access settings - superusers have full access"""
    return user.is_superuser


def can_access_hr(user):
    """Check if user can access HR module - superusers have full access"""
    return user.is_superuser


def has_admin_access(user):
    """Check if user has admin access - superusers have full access"""
    return user.is_superuser


# Mixins for class-based views
class PermissionRequiredMixin(LoginRequiredMixin):
    """Mixin to require specific permission"""
    permission_required = None

    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return self.handle_no_permission()

        if self.permission_required and not has_permission(request.user, self.permission_required):
            raise PermissionDenied(f"Permission '{self.permission_required}' required")

        return super().dispatch(request, *args, **kwargs)


class AnyPermissionRequiredMixin(LoginRequiredMixin):
    """Mixin to require any of the specified permissions"""
    permissions_required = []

    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return self.handle_no_permission()

        if self.permissions_required:
            user_permissions = get_user_permissions(request.user)
            if not any(perm in user_permissions for perm in self.permissions_required):
                raise PermissionDenied(f"One of these permissions required: {', '.join(self.permissions_required)}")

        return super().dispatch(request, *args, **kwargs)

class EmployeeRequiredMixin(LoginRequiredMixin):
    """Mixin to require employee authentication"""

    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return self.handle_no_permission()

        if not hasattr(request.user, 'employee_profile') or not request.user.employee_profile:
            messages.error(request, 'This page is only accessible to employees.')
            return redirect('accounts:employee_login')

        return super().dispatch(request, *args, **kwargs)

class CustomerRequiredMixin(LoginRequiredMixin):
    """Mixin to require customer authentication"""

    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return self.handle_no_permission()

        if not hasattr(request.user, 'customer_profile') or not request.user.customer_profile:
            messages.error(request, 'This page is only accessible to customers.')
            return redirect('customers:customer_login')

        return super().dispatch(request, *args, **kwargs)

class AdminRequiredMixin(LoginRequiredMixin):
    """Mixin to require admin/superuser authentication"""

    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return self.handle_no_permission()

        if not (request.user.is_superuser or request.user.is_staff):
            messages.error(request, 'This page requires administrator privileges.')
            raise PermissionDenied

        return super().dispatch(request, *args, **kwargs)

class DepartmentRequiredMixin(EmployeeRequiredMixin):
    """Mixin to require specific department membership"""
    department_name = None

    def dispatch(self, request, *args, **kwargs):
        response = super().dispatch(request, *args, **kwargs)
        if hasattr(response, 'status_code') and response.status_code != 200:
            return response

        employee = request.user.employee_profile
        if not employee.department or employee.department.name != self.department_name:
            messages.error(request, f'This page is only accessible to {self.department_name} staff.')
            raise PermissionDenied

        return response

class ManagementRequiredMixin(DepartmentRequiredMixin):
    """Mixin to require Management department membership"""
    department_name = 'Management'

class TechnicianRequiredMixin(DepartmentRequiredMixin):
    """Mixin to require Technician department membership"""
    department_name = 'Technician'

class MarketingRequiredMixin(DepartmentRequiredMixin):
    """Mixin to require Marketing department membership"""
    department_name = 'Marketing'

class FinancialAccessMixin(EmployeeRequiredMixin):
    """Mixin to require financial access permissions"""

    def dispatch(self, request, *args, **kwargs):
        response = super().dispatch(request, *args, **kwargs)
        if hasattr(response, 'status_code') and response.status_code != 200:
            return response

        if not has_financial_access(request.user):
            messages.error(request, 'You do not have permission to access financial information.')
            raise PermissionDenied

        return response

def customer_owns_object(view_func):
    """Decorator to ensure customers can only access their own objects"""
    @wraps(view_func)
    @login_required
    def _wrapped_view(request, *args, **kwargs):
        # This decorator should be used with views that have customer-specific objects
        # The view should implement object ownership checking
        return view_func(request, *args, **kwargs)
    return _wrapped_view