import { useCallback } from 'react'; import { useAuth } from '../../auth/hooks/useAuth'; import { privacyAuditLogger } from '../services/privacyAuditLogger'; import type { PHIFields } from '../services/privacyAuditLogger'; export interface UsePrivacyAuditLogOptions { defaultMetadata?: Record; autoIncludeUser?: boolean; } export interface PrivacyAuditLogFunction { ( action: string, options?: { resourceType?: string; resourceId?: string; resourceName?: string; phi?: PHIFields; metadata?: Record; severity?: 'low' | 'medium' | 'high' | 'critical'; success?: boolean; error?: string; } ): Promise; } export interface UsePrivacyAuditLogReturn { logEvent: PrivacyAuditLogFunction; logPHIAccess: ( patientId: string, accessType: 'view' | 'update' | 'delete', phiData?: PHIFields ) => Promise; logError: ( action: string, error: Error | string, options?: { resourceType?: string; resourceId?: string; metadata?: Record; } ) => Promise; } /** * Hook for privacy-aware audit logging with automatic user context */ export function usePrivacyAuditLog( options: UsePrivacyAuditLogOptions = {} ): UsePrivacyAuditLogReturn { const { user } = useAuth(); const { defaultMetadata = {}, autoIncludeUser = true } = options; const logEvent: PrivacyAuditLogFunction = useCallback( async (action, eventOptions = {}) => { const actor = autoIncludeUser && user ? { id: user.uid, email: user.email || undefined, name: user.displayName || undefined, role: user.customClaims?.role || undefined, } : { id: 'anonymous' }; const metadata = { ...defaultMetadata, ...eventOptions.metadata, userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined, timestamp: new Date().toISOString(), }; return privacyAuditLogger.logEvent({ actor, action, resource: eventOptions.resourceType ? { type: eventOptions.resourceType, id: eventOptions.resourceId || '', name: eventOptions.resourceName, } : undefined, phi: eventOptions.phi, metadata, severity: eventOptions.severity, success: eventOptions.success, error: eventOptions.error, }); }, [user, autoIncludeUser, defaultMetadata] ); const logPHIAccess = useCallback( async ( patientId: string, accessType: 'view' | 'update' | 'delete', phiData?: PHIFields ) => { return logEvent(`phi_${accessType}`, { resourceType: 'patient', resourceId: patientId, phi: phiData, severity: accessType === 'delete' ? 'high' : 'medium', }); }, [logEvent] ); const logError = useCallback( async ( action: string, error: Error | string, errorOptions?: { resourceType?: string; resourceId?: string; metadata?: Record; } ) => { const errorMessage = error instanceof Error ? error.message : error; const errorStack = error instanceof Error ? error.stack : undefined; return logEvent(action, { ...errorOptions, success: false, error: errorMessage, severity: 'high', metadata: { ...errorOptions?.metadata, errorStack, errorType: error instanceof Error ? error.name : 'Error', }, }); }, [logEvent] ); return { logEvent, logPHIAccess, logError, }; } /** * Hook for tracking PHI field access with automatic logging */ export function usePHIFieldTracking(patientId: string) { const { logPHIAccess } = usePrivacyAuditLog(); const trackFieldAccess = useCallback( (fieldName: string, value: any, accessType: 'view' | 'update' = 'view') => { // Log the access without exposing the actual value logPHIAccess(patientId, accessType, { [fieldName]: '***REDACTED***', // Log that field was accessed without the value }); return value; }, [patientId, logPHIAccess] ); const createTrackedField = useCallback( (fieldName: string) => ({ get: (value: T): T => trackFieldAccess(fieldName, value, 'view'), set: (value: T): T => trackFieldAccess(fieldName, value, 'update'), }), [trackFieldAccess] ); return { trackFieldAccess, createTrackedField, }; }