"""
Utility functions for role-based access control
"""
from sqlalchemy.orm import Session
from ...auth.models.user import User
from ...auth.models.role import Role

"""
Centralised helpers for role & permission checks.

This module is deliberately kept framework-agnostic so it can be used
from FastAPI dependencies, services and background jobs.

It also introduces finer-grained accountant roles while keeping full
backwards‑compatibility with existing code that checks for plain
`accountant`.
"""


# ---- Core helpers --------------------------------------------------------

def get_user_role_name(user: User, db: Session) -> str:
    """Get the role name for a user (falls back to 'customer')."""
    if not user or not getattr(user, "role_id", None):
        return "customer"
    try:
        # PERFORMANCE: Use eager-loaded relationship if available to avoid query
        if hasattr(user, "role") and user.role is not None:
            return user.role.name
        # Fallback: query if relationship not loaded
        role = db.query(Role).filter(Role.id == user.role_id).first()
        return role.name if role else "customer"
    except Exception:
        # Never break business logic because of RBAC helper failures
        return "customer"


def _is_role(user: User, db: Session, *role_names: str) -> bool:
    """Internal helper to check if user has any of the given roles."""
    role = get_user_role_name(user, db)
    return role in role_names


def is_admin(user: User, db: Session) -> bool:
    """Check if user is platform admin."""
    return _is_role(user, db, "admin")


def is_staff(user: User, db: Session) -> bool:
    """Check if user is staff."""
    return _is_role(user, db, "staff")


def is_customer(user: User, db: Session) -> bool:
    """Check if user is customer."""
    return _is_role(user, db, "customer")


def is_housekeeping(user: User, db: Session) -> bool:
    """Check if user is housekeeping."""
    return _is_role(user, db, "housekeeping")


# ---- Accountant role family ----------------------------------------------

ACCOUNTANT_BASE_ROLE = "accountant"

# Sub‑roles for finer-grained permissions. These are logical names only;
# they must also exist in the `roles` table to be usable.
ACCOUNTANT_SUB_ROLES = {
    "accountant_readonly",   # View‑only, no mutations
    "accountant_operator",   # Day‑to‑day ops within limits
    "accountant_approver",   # High‑risk approver (no self‑approval logic yet)
}


def is_accountant(user: User, db: Session) -> bool:
    """
    Check if user belongs to the accountant family.

    This returns True for:
    - 'accountant'
    - any of the accountant_* sub‑roles
    """
    role = get_user_role_name(user, db)
    return role == ACCOUNTANT_BASE_ROLE or role in ACCOUNTANT_SUB_ROLES


def is_readonly_accountant(user: User, db: Session) -> bool:
    """Check if user is an accountant with read‑only permissions."""
    return get_user_role_name(user, db) == "accountant_readonly"


def is_operator_accountant(user: User, db: Session) -> bool:
    """Check if user is an accountant operator."""
    return get_user_role_name(user, db) in {"accountant", "accountant_operator"}


def is_approver_accountant(user: User, db: Session) -> bool:
    """
    Check if user can act as an accountant approver for high‑risk actions.
    Plain 'accountant' is treated as having full approval powers.
    """
    return get_user_role_name(user, db) in {"accountant", "accountant_approver"}


# Aliases for backward compatibility and consistency with naming conventions
def is_accountant_readonly(user: User, db: Session) -> bool:
    """Alias for is_readonly_accountant."""
    return is_readonly_accountant(user, db)


def is_accountant_operator(user: User, db: Session) -> bool:
    """Alias for is_operator_accountant."""
    return is_operator_accountant(user, db)


def is_accountant_approver(user: User, db: Session) -> bool:
    """Alias for is_approver_accountant."""
    return is_approver_accountant(user, db)


# ---- Permission mapping (role -> capability set) -------------------------

# NOTE:
# This is an in‑code permission matrix. If you later introduce a full
# permission table, this mapping can become a cache over that table.
ROLE_PERMISSIONS = {
    # Admin: full access to everything
    "admin": {
        "financial.view_reports",
        "financial.manage_invoices",
        "financial.manage_payments",
        "financial.manage_settings",
        "financial.high_risk_approve",
        "users.manage",
    },
    # Existing generic accountant – treated as full‑power accountant
    "accountant": {
        "financial.view_reports",
        "financial.manage_invoices",
        "financial.manage_payments",
        "financial.manage_settings",
        "financial.high_risk_approve",
    },
    # New sub‑roles
    "accountant_readonly": {
        "financial.view_reports",
        "financial.view_invoices",
        "financial.view_payments",
        "financial.view_audit_trail",
    },
    "accountant_operator": {
        "financial.view_reports",
        "financial.view_invoices",
        "financial.view_payments",
        "financial.manage_invoices",   # normal invoice ops
        "financial.manage_payments",   # small/manual adjustments
    },
    "accountant_approver": {
        "financial.view_reports",
        "financial.view_invoices",
        "financial.view_payments",
        "financial.high_risk_approve",  # approvals only
    },
    # Staff: keep existing behaviour for now
    "staff": {
        "financial.view_invoices",
        "financial.manage_invoices",
        "financial.view_payments",
        "housekeeping.view_tasks",
        "housekeeping.manage_tasks",
        "housekeeping.view_rooms",
    },
    # Housekeeping: room cleaning and task management
    "housekeeping": {
        "housekeeping.view_tasks",           # View assigned tasks and unassigned tasks
        "housekeeping.manage_tasks",         # Update, start, complete assigned tasks
        "housekeeping.create_tasks",         # Create tasks (self-assign)
        "housekeeping.view_rooms",           # View rooms they need to clean
        "housekeeping.upload_photos",        # Upload photos for tasks
        "housekeeping.report_issues",        # Report maintenance issues from tasks
        "housekeeping.view_inspections",     # View assigned inspections
        "housekeeping.manage_inspections",   # Complete assigned inspections
    },
}


def get_user_permissions(user: User, db: Session) -> set[str]:
    """Return a set of logical permissions granted to the user."""
    role_name = get_user_role_name(user, db)
    return set(ROLE_PERMISSIONS.get(role_name, set()))


def user_has_permission(user: User, db: Session, permission: str) -> bool:
    """Check if user has a single permission."""
    return permission in get_user_permissions(user, db)


def user_has_permissions(user: User, db: Session, permissions: list[str]) -> bool:
    """Check if user has all listed permissions."""
    if not permissions:
        return True
    perms = get_user_permissions(user, db)
    return all(p in perms for p in permissions)


# ---- Higher‑level helpers used by routes/services ------------------------

def can_access_all_payments(user: User, db: Session) -> bool:
    """
    Check if user can see all payments.
    - Admins, staff, and any accountant family role may view all.
    """
    role_name = get_user_role_name(user, db)
    if role_name in {"admin", "staff"}:
        return True
    if is_accountant(user, db):
        return True
    return False


def can_access_all_invoices(user: User, db: Session) -> bool:
    """
    Check if user can see all invoices.
    - Admin, staff, and all accountant family roles may view all.
    """
    role_name = get_user_role_name(user, db)
    if role_name in {"admin", "staff"}:
        return True
    if is_accountant(user, db):
        return True
    return False


def can_create_invoices(user: User, db: Session) -> bool:
    """
    Check if user can create invoices.
    - Admin, staff, full accountant, and accountant_operator.
    - Readonly accountants are explicitly excluded.
    """
    role_name = get_user_role_name(user, db)
    return role_name in {"admin", "staff", "accountant", "accountant_operator"}


def can_manage_users(user: User, db: Session) -> bool:
    """Check if user can manage users (admin only)."""
    return is_admin(user, db)


def can_manage_housekeeping_tasks(user: User, db: Session) -> bool:
    """Check if user can manage housekeeping tasks."""
    role_name = get_user_role_name(user, db)
    return role_name in {"admin", "staff", "housekeeping"}


def can_view_all_housekeeping_tasks(user: User, db: Session) -> bool:
    """Check if user can view all housekeeping tasks (not just assigned)."""
    role_name = get_user_role_name(user, db)
    return role_name in {"admin", "staff"}


def can_assign_housekeeping_tasks(user: User, db: Session) -> bool:
    """Check if user can assign tasks to other users."""
    role_name = get_user_role_name(user, db)
    return role_name in {"admin", "staff"}


def can_manage_rooms(user: User, db: Session) -> bool:
    """Check if user can manage rooms."""
    role_name = get_user_role_name(user, db)
    return role_name in {"admin", "staff"}


def can_manage_bookings(user: User, db: Session) -> bool:
    """Check if user can manage bookings."""
    role_name = get_user_role_name(user, db)
    return role_name in {"admin", "staff"}


def can_access_all_rooms(user: User, db: Session) -> bool:
    """Check if user can access all rooms."""
    role_name = get_user_role_name(user, db)
    return role_name in {"admin", "staff", "housekeeping"}


def can_modify_booking_prices(user: User, db: Session) -> bool:
    """Check if user can modify booking prices (admin only)."""
    return is_admin(user, db)


