HEX
Server: nginx/1.24.0
System: Linux DGT-WORDPRESS-VM-SERVER 6.14.0-1014-azure #14~24.04.1-Ubuntu SMP Fri Oct 3 20:52:11 UTC 2025 x86_64
User: ubuntu (1000)
PHP: 8.4.12
Disabled: NONE
Upload Files
File: /mnt/data/ghayatcom/ghayatcom-api/app/Services/FhirService.php
<?php

namespace App\Services;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Support\Facades\Log;
use Faker\Factory as FakerFactory;
use Carbon\Carbon;

class FhirService
{
    protected $client;
    protected $config;
    protected $faker;

    public function __construct()
    {
        $this->faker = FakerFactory::create();
        $this->config = config('services.fhir');
        $this->client = new Client([
            'base_uri' => $this->config['base_url'],
            'timeout' => $this->config['timeout'],
            'headers' => $this->config['default_headers'],
            'verify' => false
        ]);
    }

    protected function getAuthHeaders()
    {
        switch ($this->config['auth_type']) {
            case 'basic':
                return [
                    'auth' => [
                        $this->config['auth_credentials']['username'],
                        $this->config['auth_credentials']['password'],
                    ],
                ];
            case 'oauth2':
                return [
                    'headers' => [
                        'Authorization' => 'Bearer ' . $this->getOAuthToken(),
                    ],
                ];
            case 'token':
                return [
                    'headers' => [
                        'Authorization' => 'Bearer ' . $this->config['auth_credentials']['token'],
                    ],
                ];
            default:
                return [];
        }
    }

    protected function getOAuthToken()
    {
        // Implement OAuth2 token retrieval logic
        // This is a simplified version - you may need to adjust based on your FHIR server's OAuth2 implementation
        $tokenClient = new Client([
            'base_uri' => $this->config['base_url'],
        ]);

        $response = $tokenClient->post('/oauth2/token', [
            'form_params' => [
                'grant_type' => 'client_credentials',
                'client_id' => $this->config['auth_credentials']['client_id'],
                'client_secret' => $this->config['auth_credentials']['client_secret'],
                'scope' => 'user/*.*',
            ],
        ]);

        $data = json_decode($response->getBody(), true);
        return $data['access_token'];
    }

    public function getResource($resourceType, $id, array $params = [])
    {
        try {
            $url = "/{$resourceType}/{$id}";
            if (!empty($params)) {
                $url .= '?' . http_build_query($params);
            }

            $response = $this->client->get($url, $this->getAuthHeaders());
            return json_decode($response->getBody(), true);
        } catch (RequestException $e) {
            Log::error("FHIR GET failed: " . $e->getMessage());
            return null;
        }
    }

    public function searchResource($resourceType, array $params = [])
    {
        try {
            $url = "/{$resourceType}";
            if (!empty($params)) {
                $url .= '?' . http_build_query($params);
            }

            $response = $this->client->get($url, $this->getAuthHeaders());
            return json_decode($response->getBody(), true);
        } catch (RequestException $e) {
            Log::error("FHIR SEARCH failed: " . $e->getMessage());
            return null;
        }
    }

    public function createResource($resourceType, array $data)
    {
        try {
            $response = $this->client->post("/{$resourceType}", array_merge(
                $this->getAuthHeaders(),
                ['json' => $data]
            ));
            return json_decode($response->getBody(), true);
        } catch (RequestException $e) {
            Log::error("FHIR CREATE failed: " . $e->getMessage());
            return null;
        }
    }

    public function updateResource($resourceType, $id, array $data)
    {
        try {
            $response = $this->client->put("/{$resourceType}/{$id}", array_merge(
                $this->getAuthHeaders(),
                ['json' => $data]
            ));
            return json_decode($response->getBody(), true);
        } catch (RequestException $e) {
            Log::error("FHIR UPDATE failed: " . $e->getMessage());
            return null;
        }
    }

    public function deleteResource($resourceType, $id)
    {
        try {
            $response = $this->client->delete("/{$resourceType}/{$id}", $this->getAuthHeaders());
            return $response->getStatusCode() === 204;
        } catch (RequestException $e) {
            Log::error("FHIR DELETE failed: " . $e->getMessage());
            return false;
        }
    }

    public function getPatientEverything($patientId)
    {
        try {
            $response = $this->client->get("/Patient/{$patientId}/\$everything", $this->getAuthHeaders());
            return json_decode($response->getBody(), true);
        } catch (RequestException $e) {
            Log::error("FHIR Patient $everything failed: " . $e->getMessage());
            return null;
        }
    }

    public function generatePatient($patient)
    {
        $gender = !empty($patient->gender) ? $patient->gender : '';
        
        return [
            'resourceType' => 'Patient',
            'identifier' => [
                [
                    'system' => config('custom.login_link'),
                    'value' => $patient->id
                ]
            ],
            'active' => true,
            'name' => [
                [
                    'use' => 'official',
                    'family' => $patient->last_name,
                    'given' => [$patient->first_name]
                ]
            ],
            'telecom' => [
                [
                    'system' => 'phone',
                    'value' => $patient->mobile_number,
                    'use' => 'home'
                ],
                [
                    'system' => 'email',
                    'value' => $patient->email,
                    'use' => 'home'
                ]
            ],
            'gender' => strtolower($gender),
            'birthDate' => Carbon::parse($patient->dob)->format('Y-m-d'),
            'address' => [
                [
                    'use' => 'home',
                    'line' => [$patient->address],
                    // 'city' => $this->faker->city,
                    // 'state' => $this->faker->stateAbbr,
                    'postalCode' => $patient->post_code,
                    'country' => $patient->country_id->iso2
                ]
            ]
        ];
    }

    public function generateCondition($patientId, $diagnosis)
    {
        return [
            'resourceType' => 'Condition',
            'clinicalStatus' => [
                'coding' => [
                    [
                        'system' => 'http://terminology.hl7.org/CodeSystem/condition-clinical',
                        'code' => 'active',
                    ]
                ]
            ],
            'verificationStatus' => [
                'coding' => [
                    [
                        'system' => 'http://terminology.hl7.org/CodeSystem/condition-ver-status',
                        'code' => 'confirmed',
                    ]
                ]
            ],
            'category' => [
                [
                    'coding' => [
                        [
                            'system' => 'http://terminology.hl7.org/CodeSystem/condition-category',
                            'code' => 'problem-list-item',
                            'display' => 'Problem List Item'
                        ]
                    ]
                ]
            ],
            'code' => [
                'coding' => [
                    [
                        'system' => 'http://snomed.info/sct',
                        // 'code' => $condition['code'],
                        'display' => $diagnosis
                    ]
                ],
                'text' => $diagnosis
            ],
            'subject' => [
                'reference' => "Patient/$patientId"
            ],
            'onsetDateTime' => Carbon::now()->format('Y-m-d'),
            'recordedDate' => Carbon::now()->format('Y-m-d')
        ];
    }

    public function generateMedicationRequest($patientId, $prescriptions)
    {
        return [
            'resourceType' => 'MedicationRequest',
            'status' => 'active',
            'intent' => 'order',
            'medicationCodeableConcept' => [
                'coding' => [
                    [
                        'system' => 'http://www.nlm.nih.gov/research/umls/rxnorm',
                        // 'code' => $medication['code'],
                        'display' => $prescriptions
                    ]
                ]
            ],
            'subject' => [
                'reference' => "Patient/$patientId"
            ],
            // 'requester' => [
            //     'reference' => "Practitioner/$practitionerId"
            // ],
            'authoredOn' => Carbon::now()->format('Y-m-d'),
            'dosageInstruction' => [
                [
                    'text' => $prescriptions,
                    // 'timing' => [
                    //     'repeat' => [
                    //         'frequency' => $this->faker->numberBetween(1, 3),
                    //         'period' => 1,
                    //         'periodUnit' => 'd'
                    //     ]
                    // ],
                    // 'route' => [
                    //     'coding' => [
                    //         [
                    //             'system' => 'http://snomed.info/sct',
                    //             'code' => '26643006',
                    //             'display' => 'Oral route'
                    //         ]
                    //     ]
                    // ]
                ]
            ]
        ];
    }

    public function generateObservation($patientId, $observation)
    {
        return [
            'resourceType' => 'Observation',
            'status' => 'final',
            'code' => [
                'text' => $observation->health_profile_master->measurement_name
            ],
            'valueQuantity' => [
                'value' => $observation->measurement_val,
                'unit' => $observation->health_profile_master->units
            ],
            'subject' => [
                'reference' => "Patient/$patientId"
            ],
            'effectiveDateTime' => Carbon::now()->format('Y-m-d'),
            'issued' => Carbon::now()->format('Y-m-d')
        ];
    }

    public function generateAllergy($patientId, $allergy)
    {
        return [
            'resourceType' => 'AllergyIntolerance',
            'clinicalStatus' => [
                'coding' => [
                    [
                        'system' => 'http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical',
                        'code' => 'active'
                    ]
                ]
            ],
            'verificationStatus' => [
                'coding' => [
                    [
                        'system' => 'http://terminology.hl7.org/CodeSystem/allergyintolerance-verification',
                        'code' => 'confirmed'
                    ]
                ]
            ],
            'type' => 'allergy',
            'category' => ['food', 'medication', 'environment'][rand(0, 2)],
            'criticality' => 'unable-to-assess',
            'code' => [
                'coding' => [
                    [
                        'system' => 'http://snomed.info/sct',
                        // 'code' => $allergy['code'],
                        'display' => $allergy
                    ]
                ]
            ],
            'patient' => [
                'reference' => "Patient/$patientId"
            ],
            'onsetDateTime' => Carbon::now()->format('Y-m-d'),
            'recordedDate' => Carbon::now()->format('Y-m-d')
        ];
    }

    public function generateProcedure($patientId, $procedure)
    {
        return [
            'resourceType' => 'Procedure',
            'code' => [
                'text' => $procedure
            ],
            'status' => 'completed',
            'performedDateTime' => Carbon::now()->toDateString(),
            'subject' => [
                'reference' => "Patient/$patientId"
            ]
        ];
    }
}