"""
Permission definitions and resolution engine.

Implements RBAC permission evaluation following the precedence order:
  1. Superadmin bypass (always granted)
  2. Explicit user override (UserPermissionOverride.is_granted)
  3. Role default grant (RolePermission.default_granted)
  4. Deny (if no grant exists)
"""

import enum

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.user import (
    Permission,
    RolePermission,
    User,
    UserPermissionOverride,
    UserRole,
)


class PermissionCode(enum.StrEnum):
    """System-wide permission codes for Munal LMS."""

    # Labs
    LAB_READ = "lab:read"
    LAB_WRITE = "lab:write"

    # Users
    USER_READ = "user:read"
    USER_WRITE = "user:write"

    # Patients
    PATIENT_READ = "patient:read"
    PATIENT_WRITE = "patient:write"

    # Tests and Catalogs
    TEST_READ = "test:read"
    TEST_WRITE = "test:write"

    # Health Packages
    PACKAGE_READ = "package:read"
    PACKAGE_WRITE = "package:write"

    # Appointments
    APPOINTMENT_READ = "appointment:read"
    APPOINTMENT_WRITE = "appointment:write"

    # Visit Orders
    ORDER_READ = "order:read"
    ORDER_WRITE = "order:write"

    # Samples & Phlebotomy
    SAMPLE_READ = "sample:read"
    SAMPLE_WRITE = "sample:write"

    # Clinical Results & Worksheets
    RESULT_READ = "result:read"
    RESULT_WRITE = "result:write"

    # Diagnostic Reports & Verification
    REPORT_READ = "report:read"
    REPORT_WRITE = "report:write"
    REPORT_VERIFY = "report:verify"

    # Discounts & Billing Rules
    DISCOUNT_READ = "discount:read"
    DISCOUNT_WRITE = "discount:write"

    # Bills & Invoicing
    BILL_READ = "bill:read"
    BILL_WRITE = "bill:write"

    # Payments & Settlements
    PAYMENT_READ = "payment:read"
    PAYMENT_WRITE = "payment:write"

    # Referrals & Commissions
    REFERRAL_READ = "referral:read"
    REFERRAL_WRITE = "referral:write"
    COMMISSION_READ = "commission:read"
    COMMISSION_WRITE = "commission:write"

    # Notifications & Comms
    NOTIFICATION_READ = "notification:read"
    NOTIFICATION_WRITE = "notification:write"

    # Dashboard & Analytics
    DASHBOARD_READ = "dashboard:read"
    AUDIT_READ = "audit:read"
    REPORTING_READ = "reporting:read"
    REPORTING_EXPORT = "reporting:export"


# Default permissions assigned to roles when seeded
ROLE_DEFAULT_PERMISSIONS: dict[UserRole, list[PermissionCode]] = {
    UserRole.SUPERADMIN: list(PermissionCode),
    UserRole.DOCTOR: [
        PermissionCode.PATIENT_READ,
        PermissionCode.TEST_READ,
        PermissionCode.ORDER_READ,
        PermissionCode.ORDER_WRITE,
        PermissionCode.APPOINTMENT_READ,
        PermissionCode.SAMPLE_READ,
        PermissionCode.RESULT_READ,
        PermissionCode.RESULT_WRITE,
        PermissionCode.REPORT_READ,
        PermissionCode.REPORT_WRITE,
        PermissionCode.REPORT_VERIFY,
        PermissionCode.DASHBOARD_READ,
    ],
    UserRole.PATHOLOGIST: [
        PermissionCode.PATIENT_READ,
        PermissionCode.TEST_READ,
        PermissionCode.ORDER_READ,
        PermissionCode.ORDER_WRITE,
        PermissionCode.SAMPLE_READ,
        PermissionCode.SAMPLE_WRITE,
        PermissionCode.RESULT_READ,
        PermissionCode.RESULT_WRITE,
        PermissionCode.REPORT_READ,
        PermissionCode.REPORT_WRITE,
        PermissionCode.REPORT_VERIFY,
        PermissionCode.DASHBOARD_READ,
    ],
    UserRole.RADIOLOGIST: [
        PermissionCode.PATIENT_READ,
        PermissionCode.TEST_READ,
        PermissionCode.ORDER_READ,
        PermissionCode.ORDER_WRITE,
        PermissionCode.RESULT_READ,
        PermissionCode.REPORT_READ,
        PermissionCode.REPORT_WRITE,
        PermissionCode.REPORT_VERIFY,
        PermissionCode.DASHBOARD_READ,
    ],
    UserRole.RECEPTIONIST: [
        PermissionCode.PATIENT_READ,
        PermissionCode.PATIENT_WRITE,
        PermissionCode.TEST_READ,
        PermissionCode.PACKAGE_READ,
        PermissionCode.APPOINTMENT_READ,
        PermissionCode.APPOINTMENT_WRITE,
        PermissionCode.ORDER_READ,
        PermissionCode.ORDER_WRITE,
        PermissionCode.SAMPLE_READ,
        PermissionCode.SAMPLE_WRITE,
        PermissionCode.RESULT_READ,
        PermissionCode.REPORT_READ,
        PermissionCode.DISCOUNT_READ,
        PermissionCode.BILL_READ,
        PermissionCode.BILL_WRITE,
        PermissionCode.PAYMENT_READ,
        PermissionCode.PAYMENT_WRITE,
        PermissionCode.REFERRAL_READ,
        PermissionCode.NOTIFICATION_READ,
        PermissionCode.NOTIFICATION_WRITE,
        PermissionCode.DASHBOARD_READ,
    ],
    UserRole.PHLEBOTOMIST: [
        PermissionCode.PATIENT_READ,
        PermissionCode.ORDER_READ,
        PermissionCode.ORDER_WRITE,
        PermissionCode.APPOINTMENT_READ,
        PermissionCode.SAMPLE_READ,
        PermissionCode.SAMPLE_WRITE,
        PermissionCode.RESULT_READ,
        PermissionCode.RESULT_WRITE,
        PermissionCode.REPORT_READ,
        PermissionCode.REPORT_WRITE,
        PermissionCode.DASHBOARD_READ,
    ],
    UserRole.ACCOUNTANT: [
        PermissionCode.PATIENT_READ,
        PermissionCode.ORDER_READ,
        PermissionCode.USER_READ,
        PermissionCode.REPORT_READ,
        PermissionCode.DISCOUNT_READ,
        PermissionCode.DISCOUNT_WRITE,
        PermissionCode.BILL_READ,
        PermissionCode.BILL_WRITE,
        PermissionCode.PAYMENT_READ,
        PermissionCode.PAYMENT_WRITE,
        PermissionCode.REFERRAL_READ,
        PermissionCode.REFERRAL_WRITE,
        PermissionCode.COMMISSION_READ,
        PermissionCode.COMMISSION_WRITE,
        PermissionCode.DASHBOARD_READ,
        PermissionCode.REPORTING_READ,
        PermissionCode.REPORTING_EXPORT,
    ],
}


async def resolve_user_permissions(db: AsyncSession, user: User) -> set[str]:
    """
    Resolve the effective set of granted permission codes for a given user.

    Order of evaluation:
      1. Fetch globally active permissions (is_active == True).
      2. Superadmin bypass: Returns all active permissions.
      3. Role defaults: Loads RolePermission for user.role.
      4. User overrides: Overwrites role defaults (grants add, denies remove).
      5. Intersect with globally active permissions (disabled features cannot be executed).
    """
    active_stmt = select(Permission.code).where(Permission.is_active.is_(True))
    active_res = await db.execute(active_stmt)
    active_codes = set(active_res.scalars().all())

    # 1. Fetch role default permissions
    role_stmt = (
        select(Permission.code, RolePermission.default_granted)
        .join(Permission, RolePermission.permission_id == Permission.id)
        .where(RolePermission.role == user.role, Permission.is_active.is_(True))
    )
    role_res = await db.execute(role_stmt)
    role_rows = role_res.all()

    if not role_rows and user.is_superadmin:
        granted = set(active_codes)
    elif user.is_superadmin:
        # Superadmin inherits all active capabilities unless explicitly revoked for the role
        revoked_by_role = {code for code, is_granted in role_rows if not is_granted}
        granted = set(active_codes) - revoked_by_role
    else:
        granted = {code for code, is_granted in role_rows if is_granted}

    # 2. Fetch user permission overrides
    override_stmt = (
        select(Permission.code, UserPermissionOverride.is_granted)
        .join(Permission, UserPermissionOverride.permission_id == Permission.id)
        .where(UserPermissionOverride.user_id == user.id, Permission.is_active.is_(True))
    )
    override_res = await db.execute(override_stmt)
    for code, is_granted in override_res.all():
        if is_granted:
            granted.add(code)
        else:
            granted.discard(code)

    return granted & active_codes
