import type { Permission, DataScope, AuthContext, Role } from "@/shared/types/permissions"
import { isPermission } from "@/shared/lib/type-guards"
import { getSession } from "@/shared/lib/session"
import { PermissionDeniedError } from "@/shared/lib/errors"

// Re-export for backward compatibility (other modules still import from here)
export { PermissionDeniedError } from "@/shared/lib/errors"

/**
 * Resolve the data scope for a user based on their roles.
 *
 * Delegates to the RBAC module's configuration-driven resolver via dynamic
 * import, so the shared layer never statically depends on modules/*.
 * This is the same pattern used by shared/lib/session.ts to break the
 * shared ↔ auth circular dependency.
 *
 * P1-5/P1-6 audit fix: removed direct DB queries against classes,
 * classEnrollments, classSubjectTeachers, grades, parentStudentRelations
 * tables. The resolver now calls module data-access functions and is
 * driven by a configuration array (DATA_SCOPE_RULES) instead of hardcoded
 * role-name checks.
 */
async function resolveDataScope(userId: string, roleNames: Role[]): Promise<DataScope> {
  const { resolveDataScopeFromConfig } = await import("@/modules/rbac/lib/data-scope-resolver")
  return resolveDataScopeFromConfig(userId, roleNames)
}

/**
 * Get the full authentication context for the current user.
 * Throws if not authenticated.
 */
export async function getAuthContext(): Promise<AuthContext> {
  const session = await getSession()
  const userId = session?.user?.id
  if (!userId) throw new PermissionDeniedError("auth_required")

  // Prefer session data (already resolved in JWT callback)
  // Use type guards to safely coerce session values
  const roleNames = (session.user.roles ?? []).filter(
    (r): r is Role => typeof r === "string"
  )
  const permissions = (session.user.permissions ?? []).filter(isPermission)

  // Resolve data scope from DB (not cached in JWT since it can change)
  const dataScope = await resolveDataScope(userId, roleNames)

  return { userId, roles: roleNames, permissions, dataScope }
}

/**
 * Assert the current user has the specified permission.
 * Returns AuthContext on success, throws PermissionDeniedError on failure.
 */
export async function requirePermission(permission: Permission): Promise<AuthContext> {
  const ctx = await getAuthContext()
  if (!ctx.permissions.includes(permission)) {
    throw new PermissionDeniedError(permission)
  }
  return ctx
}

/**
 * Check permission without throwing. Useful for conditional logic.
 */
export async function checkPermission(
  permission: Permission
): Promise<{ allowed: boolean; ctx: AuthContext }> {
  const ctx = await getAuthContext()
  return { allowed: ctx.permissions.includes(permission), ctx }
}

/**
 * Convenience: assert the user is authenticated (has any role).
 * Returns AuthContext on success.
 */
export async function requireAuth(): Promise<AuthContext> {
  return getAuthContext()
}
