* @link https://descomplicar.pt * @since 1.0.0 */ namespace Care_API\Endpoints; use Care_API\Services\Database\Patient_Service; use Care_API\Services\Auth_Service; use Care_API\Utils\Input_Validator; use Care_API\Utils\Error_Handler; use WP_REST_Request; use WP_REST_Response; use WP_Error; if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Class Patient_Endpoints * * REST API endpoints for patient management * * @since 1.0.0 */ class Patient_Endpoints { /** * API namespace * * @var string */ private const NAMESPACE = 'kivicare/v1'; /** * Register all patient endpoints * * @since 1.0.0 */ public static function register_routes() { // Create patient register_rest_route( self::NAMESPACE, '/patients', array( 'methods' => 'POST', 'callback' => array( self::class, 'create_patient' ), 'permission_callback' => array( Auth_Service::class, 'check_authentication' ), 'args' => self::get_create_patient_args() ) ); // Get single patient register_rest_route( self::NAMESPACE, '/patients/(?P\d+)', array( 'methods' => 'GET', 'callback' => array( self::class, 'get_patient' ), 'permission_callback' => array( Auth_Service::class, 'check_authentication' ), 'args' => array( 'id' => array( 'required' => true, 'validate_callback' => function( $param ) { return is_numeric( $param ); }, 'sanitize_callback' => 'absint' ) ) ) ); // Update patient register_rest_route( self::NAMESPACE, '/patients/(?P\d+)', array( 'methods' => 'PUT', 'callback' => array( self::class, 'update_patient' ), 'permission_callback' => array( Auth_Service::class, 'check_authentication' ), 'args' => self::get_update_patient_args() ) ); // Search patients register_rest_route( self::NAMESPACE, '/patients/search', array( 'methods' => 'GET', 'callback' => array( self::class, 'search_patients' ), 'permission_callback' => array( Auth_Service::class, 'check_authentication' ), 'args' => self::get_search_args() ) ); // Get patient dashboard register_rest_route( self::NAMESPACE, '/patients/(?P\d+)/dashboard', array( 'methods' => 'GET', 'callback' => array( self::class, 'get_patient_dashboard' ), 'permission_callback' => array( Auth_Service::class, 'check_authentication' ), 'args' => array( 'id' => array( 'required' => true, 'validate_callback' => function( $param ) { return is_numeric( $param ); }, 'sanitize_callback' => 'absint' ) ) ) ); // Get patient medical history register_rest_route( self::NAMESPACE, '/patients/(?P\d+)/history', array( 'methods' => 'GET', 'callback' => array( self::class, 'get_patient_history' ), 'permission_callback' => array( Auth_Service::class, 'check_authentication' ), 'args' => array( 'id' => array( 'required' => true, 'validate_callback' => function( $param ) { return is_numeric( $param ); }, 'sanitize_callback' => 'absint' ), 'type' => array( 'validate_callback' => function( $param ) { return in_array( $param, array( 'encounters', 'appointments', 'prescriptions', 'bills', 'all' ) ); }, 'sanitize_callback' => 'sanitize_text_field', 'default' => 'all' ) ) ) ); // Bulk operations register_rest_route( self::NAMESPACE, '/patients/bulk', array( 'methods' => 'POST', 'callback' => array( self::class, 'bulk_operations' ), 'permission_callback' => array( Auth_Service::class, 'check_authentication' ), 'args' => self::get_bulk_operation_args() ) ); } /** * Create a new patient * * @param WP_REST_Request $request Request object * @return WP_REST_Response|WP_Error * @since 1.0.0 */ public static function create_patient( WP_REST_Request $request ) { try { $data = $request->get_json_params(); // Validate required fields $validation = Input_Validator::validate_patient_data( $data, 'create' ); if ( is_wp_error( $validation ) ) { return $validation; } // Sanitize input data $patient_data = Input_Validator::sanitize_patient_data( $data ); $result = Patient_Service::create_patient( $patient_data, $patient_data['clinic_id'] ); if ( is_wp_error( $result ) ) { return Error_Handler::handle_service_error( $result ); } return new WP_REST_Response( array( 'success' => true, 'message' => 'Patient created successfully', 'data' => $result ), 201 ); } catch ( Exception $e ) { return Error_Handler::handle_exception( $e ); } } /** * Get single patient * * @param WP_REST_Request $request Request object * @return WP_REST_Response|WP_Error * @since 1.0.0 */ public static function get_patient( WP_REST_Request $request ) { try { $patient_id = $request['id']; $result = Patient_Service::get_patient_with_metadata( $patient_id ); if ( is_wp_error( $result ) ) { return Error_Handler::handle_service_error( $result ); } return new WP_REST_Response( array( 'success' => true, 'data' => $result ), 200 ); } catch ( Exception $e ) { return Error_Handler::handle_exception( $e ); } } /** * Update patient * * @param WP_REST_Request $request Request object * @return WP_REST_Response|WP_Error * @since 1.0.0 */ public static function update_patient( WP_REST_Request $request ) { try { $patient_id = $request['id']; $data = $request->get_json_params(); // Validate input data $validation = Input_Validator::validate_patient_data( $data, 'update' ); if ( is_wp_error( $validation ) ) { return $validation; } // Sanitize input data $patient_data = Input_Validator::sanitize_patient_data( $data ); $result = Patient_Service::update_patient( $patient_id, $patient_data ); if ( is_wp_error( $result ) ) { return Error_Handler::handle_service_error( $result ); } return new WP_REST_Response( array( 'success' => true, 'message' => 'Patient updated successfully', 'data' => $result ), 200 ); } catch ( Exception $e ) { return Error_Handler::handle_exception( $e ); } } /** * Search patients * * @param WP_REST_Request $request Request object * @return WP_REST_Response|WP_Error * @since 1.0.0 */ public static function search_patients( WP_REST_Request $request ) { try { $params = $request->get_params(); $search_term = sanitize_text_field( $params['q'] ?? '' ); $clinic_id = absint( $params['clinic_id'] ?? 0 ); if ( empty( $search_term ) ) { return new WP_Error( 'missing_search_term', 'Search term is required', array( 'status' => 400 ) ); } if ( empty( $clinic_id ) ) { return new WP_Error( 'missing_clinic_id', 'Clinic ID is required', array( 'status' => 400 ) ); } $filters = array(); if ( ! empty( $params['age_min'] ) ) { $filters['age_min'] = absint( $params['age_min'] ); } if ( ! empty( $params['age_max'] ) ) { $filters['age_max'] = absint( $params['age_max'] ); } if ( ! empty( $params['gender'] ) ) { $filters['gender'] = sanitize_text_field( $params['gender'] ); } if ( isset( $params['status'] ) ) { $filters['status'] = absint( $params['status'] ); } $result = Patient_Service::search_patients( $search_term, $clinic_id, $filters ); return new WP_REST_Response( array( 'success' => true, 'data' => $result ), 200 ); } catch ( Exception $e ) { return Error_Handler::handle_exception( $e ); } } /** * Get patient dashboard * * @param WP_REST_Request $request Request object * @return WP_REST_Response|WP_Error * @since 1.0.0 */ public static function get_patient_dashboard( WP_REST_Request $request ) { try { $patient_id = $request['id']; $result = Patient_Service::get_patient_dashboard( $patient_id ); if ( is_wp_error( $result ) ) { return Error_Handler::handle_service_error( $result ); } return new WP_REST_Response( array( 'success' => true, 'data' => $result ), 200 ); } catch ( Exception $e ) { return Error_Handler::handle_exception( $e ); } } /** * Get patient medical history * * @param WP_REST_Request $request Request object * @return WP_REST_Response|WP_Error * @since 1.0.0 */ public static function get_patient_history( WP_REST_Request $request ) { try { $patient_id = $request['id']; $type = $request['type'] ?? 'all'; $result = array(); switch ( $type ) { case 'encounters': // Would call Encounter_Service::get_patient_encounter_history $result = array( 'encounters' => array() ); // Placeholder break; case 'appointments': // Would call Appointment_Service::get_patient_appointments $result = array( 'appointments' => array() ); // Placeholder break; case 'prescriptions': // Would call Prescription_Service::get_patient_prescription_history $result = array( 'prescriptions' => array() ); // Placeholder break; case 'bills': // Would call Bill_Service::get_patient_bills $result = array( 'bills' => array() ); // Placeholder break; case 'all': default: $patient = Patient_Service::get_patient_with_metadata( $patient_id ); if ( is_wp_error( $patient ) ) { return Error_Handler::handle_service_error( $patient ); } $result = array( 'encounters' => $patient['encounters'] ?? array(), 'appointments' => $patient['appointments'] ?? array(), 'prescriptions' => $patient['prescriptions'] ?? array(), 'bills' => $patient['bills'] ?? array(), 'medical_history' => $patient['medical_history'] ?? array() ); break; } return new WP_REST_Response( array( 'success' => true, 'data' => $result ), 200 ); } catch ( Exception $e ) { return Error_Handler::handle_exception( $e ); } } /** * Bulk operations on patients * * @param WP_REST_Request $request Request object * @return WP_REST_Response|WP_Error * @since 1.0.0 */ public static function bulk_operations( WP_REST_Request $request ) { try { $data = $request->get_json_params(); $action = sanitize_text_field( $data['action'] ?? '' ); $patient_ids = array_map( 'absint', $data['patient_ids'] ?? array() ); if ( empty( $action ) || empty( $patient_ids ) ) { return new WP_Error( 'invalid_bulk_data', 'Action and patient IDs are required', array( 'status' => 400 ) ); } $results = array(); $errors = array(); switch ( $action ) { case 'activate': foreach ( $patient_ids as $patient_id ) { $result = Patient_Service::update_patient( $patient_id, array( 'status' => 1 ) ); if ( is_wp_error( $result ) ) { $errors[] = array( 'id' => $patient_id, 'error' => $result->get_error_message() ); } else { $results[] = array( 'id' => $patient_id, 'status' => 'activated' ); } } break; case 'deactivate': foreach ( $patient_ids as $patient_id ) { $result = Patient_Service::update_patient( $patient_id, array( 'status' => 0 ) ); if ( is_wp_error( $result ) ) { $errors[] = array( 'id' => $patient_id, 'error' => $result->get_error_message() ); } else { $results[] = array( 'id' => $patient_id, 'status' => 'deactivated' ); } } break; default: return new WP_Error( 'invalid_bulk_action', 'Invalid bulk action', array( 'status' => 400 ) ); } return new WP_REST_Response( array( 'success' => true, 'message' => 'Bulk operation completed', 'results' => $results, 'errors' => $errors ), 200 ); } catch ( Exception $e ) { return Error_Handler::handle_exception( $e ); } } /** * Get arguments for create patient endpoint * * @return array * @since 1.0.0 */ private static function get_create_patient_args() { return array( 'first_name' => array( 'required' => true, 'validate_callback' => function( $param ) { return ! empty( $param ) && is_string( $param ); }, 'sanitize_callback' => 'sanitize_text_field' ), 'last_name' => array( 'required' => true, 'validate_callback' => function( $param ) { return ! empty( $param ) && is_string( $param ); }, 'sanitize_callback' => 'sanitize_text_field' ), 'clinic_id' => array( 'required' => true, 'validate_callback' => function( $param ) { return is_numeric( $param ) && $param > 0; }, 'sanitize_callback' => 'absint' ), 'user_email' => array( 'validate_callback' => function( $param ) { return empty( $param ) || is_email( $param ); }, 'sanitize_callback' => 'sanitize_email' ), 'contact_no' => array( 'validate_callback' => 'rest_validate_request_arg', 'sanitize_callback' => 'sanitize_text_field' ), 'dob' => array( 'validate_callback' => 'rest_validate_request_arg', 'sanitize_callback' => 'sanitize_text_field' ), 'gender' => array( 'validate_callback' => function( $param ) { return empty( $param ) || in_array( strtolower( $param ), array( 'male', 'female', 'other' ) ); }, 'sanitize_callback' => 'sanitize_text_field' ), 'blood_group' => array( 'validate_callback' => 'rest_validate_request_arg', 'sanitize_callback' => 'sanitize_text_field' ), 'address' => array( 'validate_callback' => 'rest_validate_request_arg', 'sanitize_callback' => 'sanitize_textarea_field' ) ); } /** * Get arguments for update patient endpoint * * @return array * @since 1.0.0 */ private static function get_update_patient_args() { $args = self::get_create_patient_args(); // Make all fields optional for update foreach ( $args as &$arg ) { $arg['required'] = false; } return $args; } /** * Get arguments for search endpoint * * @return array * @since 1.0.0 */ private static function get_search_args() { return array( 'q' => array( 'required' => true, 'validate_callback' => function( $param ) { return ! empty( $param ) && is_string( $param ); }, 'sanitize_callback' => 'sanitize_text_field' ), 'clinic_id' => array( 'required' => true, 'validate_callback' => function( $param ) { return is_numeric( $param ) && $param > 0; }, 'sanitize_callback' => 'absint' ), 'age_min' => array( 'validate_callback' => function( $param ) { return is_numeric( $param ) && $param >= 0; }, 'sanitize_callback' => 'absint' ), 'age_max' => array( 'validate_callback' => function( $param ) { return is_numeric( $param ) && $param >= 0; }, 'sanitize_callback' => 'absint' ), 'gender' => array( 'validate_callback' => function( $param ) { return in_array( strtolower( $param ), array( 'male', 'female', 'other' ) ); }, 'sanitize_callback' => 'sanitize_text_field' ), 'status' => array( 'validate_callback' => function( $param ) { return in_array( $param, array( 0, 1, '0', '1' ) ); }, 'sanitize_callback' => 'absint' ) ); } /** * Get arguments for bulk operations endpoint * * @return array * @since 1.0.0 */ private static function get_bulk_operation_args() { return array( 'action' => array( 'required' => true, 'validate_callback' => function( $param ) { return in_array( $param, array( 'activate', 'deactivate' ) ); }, 'sanitize_callback' => 'sanitize_text_field' ), 'patient_ids' => array( 'required' => true, 'validate_callback' => function( $param ) { return is_array( $param ) && ! empty( $param ); }, 'sanitize_callback' => function( $param ) { return array_map( 'absint', $param ); } ) ); } }