from functools import wraps

from django.core.exceptions import PermissionDenied

ROLE_SUPER_ADMIN = "Super Admin"
ROLE_HR_ADMIN = "HR Admin"
ROLE_PAYROLL_OFFICER = "Payroll Officer"
ROLE_MANAGER = "Manager"
ROLE_EMPLOYEE = "Employee"
ROLE_ACCOUNTS = "Accounts"
ROLE_IT = "IT"

ROLE_NAMES = (
    ROLE_SUPER_ADMIN,
    ROLE_HR_ADMIN,
    ROLE_PAYROLL_OFFICER,
    ROLE_MANAGER,
    ROLE_EMPLOYEE,
    ROLE_ACCOUNTS,
    ROLE_IT,
)


def has_role(user, *role_names):
    if not user or not getattr(user, "is_authenticated", False):
        return False
    if user.is_superuser:
        return True
    wanted = {name for name in role_names if name}
    if not wanted:
        return True
    return user.groups.filter(name__in=wanted).exists()


def require_roles(*role_names):
    def decorator(view_func):
        @wraps(view_func)
        def wrapped(request, *args, **kwargs):
            if not has_role(request.user, *role_names):
                raise PermissionDenied("You do not have permission to perform this action.")
            return view_func(request, *args, **kwargs)
        return wrapped
    return decorator


def accessible_company_ids(user):
    """Return the explicit company scope for a user.

    Superusers are intentionally global. Other privileged roles are *not* global;
    their queries must be restricted to UserCompanyAccess. During upgrade from the
    trial build, an employee account without membership temporarily inherits the
    company of its employee profile so users are not locked out before seeding.
    """
    if not user or not getattr(user, "is_authenticated", False):
        return []
    if user.is_superuser:
        from apps.organization.models import Company
        return list(Company.objects.filter(is_active=True).values_list("id", flat=True))
    ids = list(user.company_accesses.filter(is_active=True).values_list("company_id", flat=True))
    if ids:
        return ids
    employee = getattr(user, "employee_profile", None)
    if employee and employee.company_id:
        return [employee.company_id]
    return []


def default_company_id(user):
    if not user or not getattr(user, "is_authenticated", False):
        return None
    if user.is_superuser:
        ids = accessible_company_ids(user)
        return ids[0] if ids else None
    membership = user.company_accesses.filter(is_active=True, is_default=True).values_list("company_id", flat=True).first()
    if membership:
        return membership
    ids = accessible_company_ids(user)
    return ids[0] if ids else None


def user_can_access_company(user, company_or_id):
    company_id = getattr(company_or_id, "pk", company_or_id)
    return bool(company_id and company_id in set(accessible_company_ids(user)))


def assert_company_access(user, company_or_id):
    if not user_can_access_company(user, company_or_id):
        raise PermissionDenied("You do not have access to this company.")


def scope_queryset_by_company(user, queryset, field="company"):
    """Apply tenant scoping to a queryset using a company lookup path.

    Examples: field="company", "employee__company", "period__company".
    """
    if getattr(user, "is_superuser", False):
        return queryset
    ids = accessible_company_ids(user)
    if not ids:
        return queryset.none()
    return queryset.filter(**{f"{field}__in": ids})


def can_access_employee(user, employee):
    """Object-scope rule: company membership first, then role/team scope."""
    if not user or not getattr(user, "is_authenticated", False):
        return False
    if not user_can_access_company(user, employee.company_id):
        return False
    if user.is_superuser or has_role(user, ROLE_SUPER_ADMIN, ROLE_HR_ADMIN):
        return True
    if getattr(employee, "user_id", None) == user.id:
        return True
    if has_role(user, ROLE_MANAGER):
        manager_employee = getattr(user, "employee_profile", None)
        return bool(manager_employee and employee.reporting_manager_id == manager_employee.id)
    return False


def assert_task_assignee(user, task):
    if not user or not getattr(user, "is_authenticated", False):
        raise PermissionDenied("Authentication is required.")
    if not user_can_access_company(user, task.company_id):
        raise PermissionDenied("This task belongs to another company.")
    if user.is_superuser:
        return
    if has_role(user, ROLE_SUPER_ADMIN):
        return
    if task.assigned_to_id:
        if task.assigned_to_id != user.id:
            raise PermissionDenied("This task is assigned to another user.")
        return
    if task.assigned_role and has_role(user, task.assigned_role):
        return
    raise PermissionDenied("This task is not assigned to you.")
