# Practitioner Service

This service manages practitioner (doctor, therapist, etc.) data within the Firestore database. It handles practitioner profiles, draft profiles, registration tokens, and associations with clinics.

**Note:** Data aggregation into related entities (Clinics, Procedures) is handled by Cloud Functions triggered by Firestore events.

## `PractitionerService` Class

Extends `BaseService`.

### Constructor

```typescript
constructor(
  db: Firestore,
  auth: Auth,
  app: FirebaseApp,
  clinicService?: ClinicService // Optional dependency injection
)
```

Initializes the service with Firestore, Auth, App instances. Optionally accepts a `ClinicService` instance for operations requiring clinic data (like draft creation or token validation).

### Methods

- **`createPractitioner(data: CreatePractitionerData): Promise<Practitioner>`**

  - Creates a new, fully active practitioner profile.
  - Validates data using `createPractitionerSchema`.
  - Generates a unique ID using `this.generateId()`.
  - Initializes default review info.
  - Sets default values for `isActive` (true), `isVerified` (false), and `status` (ACTIVE).
  - Saves the practitioner document.
  - **Aggregation Note:** Adding practitioner info to associated `Clinics` is handled by Cloud Functions.

- **`createDraftPractitioner(data: CreateDraftPractitionerData, createdBy: string, clinicId: string): Promise<{ practitioner: Practitioner; token: PractitionerToken }>`**

  - Creates a draft practitioner profile (status `DRAFT`) not yet linked to a user (`userRef` is empty). Typically used by clinic admins.
  - Validates data using `createDraftPractitionerSchema`.
  - Verifies that the specified `clinicId` exists.
  - Associates the practitioner with the specified `clinicId` (and potentially others in `data.clinics`).
  - Sets `isActive` and `isVerified` to `false` by default.
  - Saves the draft practitioner document.
  - Automatically creates a registration token (`PractitionerToken`) for this draft profile.
  - Returns both the created draft practitioner and the registration token.

- **`createPractitionerToken(data: CreatePractitionerTokenData, createdBy: string): Promise<PractitionerToken>`**

  - Creates a registration token for an existing `DRAFT` practitioner.
  - Validates input data using `createPractitionerTokenSchema`.
  - Ensures the practitioner exists, is in `DRAFT` status, and belongs to the specified `clinicId`.
  - Generates a unique, short, uppercase token string.
  - Sets a default expiration of 7 days if not provided.
  - Saves the token in the `register_tokens` subcollection of the practitioner document.

- **`getPractitionerActiveTokens(practitionerId: string): Promise<PractitionerToken[]>`**

  - Retrieves all `ACTIVE` and non-expired registration tokens for a specific practitioner.

- **`validateToken(tokenString: string): Promise<PractitionerToken | null>`**

  - Finds an `ACTIVE`, non-expired token matching the `tokenString` across _all_ practitioners' `register_tokens` subcollections. Returns the token object or `null`.

- **`markTokenAsUsed(tokenId: string, practitionerId: string, userId: string): Promise<void>`**

  - Updates a specific token's status to `USED` and records who used it (`userId`) and when.

- **`getPractitioner(practitionerId: string): Promise<Practitioner | null>`**

  - Retrieves a single practitioner document by its ID.

- **`getPractitionerByUserRef(userRef: string): Promise<Practitioner | null>`**

  - Finds and retrieves a practitioner document based on the linked user ID (`userRef`).

- **`getPractitionersByClinic(clinicId: string): Promise<Practitioner[]>`**

  - Retrieves all `ACTIVE` practitioners associated with a specific `clinicId`.

- **`getAllPractitionersByClinic(clinicId: string): Promise<Practitioner[]>`**

  - Retrieves all `ACTIVE` practitioners (regardless of status like DRAFT) associated with a `clinicId`.

- **`getDraftPractitionersByClinic(clinicId: string): Promise<Practitioner[]>`**

  - Retrieves all practitioners with `DRAFT` status associated with a specific `clinicId`.

- **`updatePractitioner(practitionerId: string, data: UpdatePractitionerData): Promise<Practitioner>`**

  - Updates an existing practitioner document with partial data.
  - Sets the `updatedAt` timestamp.
  - **Aggregation Note:** Updates to aggregated data in `Clinics` and `Procedures` are handled by Cloud Functions.

- **`addClinic(practitionerId: string, clinicId: string): Promise<void>`**

  - Adds a `clinicId` to the practitioner's `clinics` array.
  - Prevents duplicates.
  - **Aggregation Note:** Updating the clinic's `doctors`/`doctorsInfo` is handled by Cloud Functions.

- **`removeClinic(practitionerId: string, clinicId: string): Promise<void>`**

  - Removes a `clinicId` from the practitioner's `clinics` array.
  - **Aggregation Note:** Updating the clinic's `doctors`/`doctorsInfo` is handled by Cloud Functions.

- **`deactivatePractitioner(practitionerId: string): Promise<void>`**

  - Sets the practitioner's `isActive` flag to `false` using `updatePractitioner`.
  - **Aggregation Note:** Related updates are handled by Cloud Functions.

- **`activatePractitioner(practitionerId: string): Promise<void>`**

  - Sets the practitioner's `isActive` flag to `true` using `updatePractitioner`.
  - **Aggregation Note:** Related updates are handled by Cloud Functions.

- **`deletePractitioner(practitionerId: string): Promise<void>`**

  - Permanently deletes a practitioner document.
  - **Aggregation Note:** Removal of associated data from `Clinics`, `Procedures`, etc., is handled by Cloud Functions.

- **`validateTokenAndClaimProfile(tokenString: string, userId: string): Promise<Practitioner | null>`**

  - Orchestrates the process of a user claiming a draft profile:
    1.  Validates the `tokenString` using `validateToken`.
    2.  Retrieves the associated `DRAFT` practitioner.
    3.  Checks if the user (`userId`) already has a profile.
    4.  Updates the practitioner's `userRef` to the `userId` and status to `ACTIVE` using `updatePractitioner`.
    5.  Marks the token as `USED` using `markTokenAsUsed`.
    6.  Returns the now-claimed practitioner profile.

- **`getAllPractitioners(options?: { pagination?: number; lastDoc?: any; includeDraftPractitioners?: boolean }): Promise<{ practitioners: Practitioner[]; lastDoc: any }>`**

  - Retrieves a list of practitioners, ordered by name.
  - Supports pagination (`pagination`, `lastDoc`).
  - Optionally includes `DRAFT` practitioners.

- **`getPractitionersByFilters(filters: { ... }): Promise<{ practitioners: Practitioner[]; lastDoc: any }>`**
  - Retrieves practitioners based on complex filter criteria:
    - Name (first/last)
    - Certifications, Specialties
    - Procedures (Family, Category, Subcategory, Technology)
    - Location/Radius - Filters based on associated clinics
    - Rating (`reviewInfo.averageRating`)
  - Combines Firestore queries with in-memory filtering for non-indexed fields.
  - Supports pagination.
  - Optionally includes `DRAFT` practitioners.
