# Clinic Service

This service manages clinic data within the Firestore database. It provides methods for creating, reading, updating, and managing clinics, their branches, and associated data like tags and administrators.

**Note:** This service relies on helper functions defined in the `./utils` directory for specific operations like photo uploads, geo-queries, and data fetching. Cloud Functions handle data aggregation into related entities (Practitioners, Procedures, ClinicGroups).

## `ClinicService` Class

Extends `BaseService`.

### Constructor

```typescript
constructor(
  db: Firestore,
  auth: Auth,
  app: FirebaseApp,
  clinicGroupService: ClinicGroupService,
  clinicAdminService: ClinicAdminService
)
```

Initializes the service with Firestore, Auth, App instances, and required dependency services (`ClinicGroupService`, `ClinicAdminService`).

### Core Methods

- **`createClinic(data: CreateClinicData, creatorAdminId: string): Promise<Clinic>`**
  - Creates a new clinic document in Firestore.
  - Validates input data using `createClinicSchema`.
  - Verifies the `creatorAdminId` and their association with the specified `clinicGroupId`.
  - Calculates the geohash for the clinic's location.
  - Initializes default review information.
  - Handles photo uploads for logo, cover photo, featured photos, and photos with tags using utility functions.
  - Updates the creator admin's `clinicsManaged` list.
  - **Aggregation Note:** Adding the clinic to the `ClinicGroup` is handled by Cloud Functions.
- **`updateClinic(clinicId: string, data: Partial<Omit<Clinic, \"id\" | \"createdAt\" | \"clinicGroupId\">>, adminId: string): Promise<Clinic>`**
  - Updates an existing clinic document.
  - Fetches the current clinic data.
  - Validates the partial update data against the `clinicSchema` after merging with existing data.
  - Updates the geohash if the location is changed.
  - Handles photo updates/uploads if necessary.
  - **Aggregation Note:** Aggregation updates in related entities (Practitioners, Procedures, ClinicGroup) are handled by Cloud Functions.
- **`deactivateClinic(clinicId: string, adminId: string): Promise<void>`**
  - Sets the `isActive` flag of a clinic to `false`.
  - Requires admin permission (verification logic might be in the calling function or assumed).
  - **Aggregation Note:** Aggregation updates (e.g., removing from active lists, updating related entities) are handled by Cloud Functions.
- **`getClinic(clinicId: string): Promise<Clinic | null>`**
  - Retrieves a single clinic document by its ID using `ClinicUtils.getClinic`.
- **`getClinicsByGroup(groupId: string): Promise<Clinic[]>`**
  - Retrieves all clinic documents belonging to a specific `clinicGroupId` using `ClinicUtils.getClinicsByGroup`.
- **`findClinicsInRadius(center: { latitude: number; longitude: number }, radiusInKm: number, filters?: { procedures?: string[]; tags?: ClinicTag[] }): Promise<Clinic[]>`**
  - Finds active clinics within a specified radius using geohash queries.
  - Leverages `SearchUtils.findClinicsInRadius`.
  - Optionally filters results by procedure IDs (services) and tags.
- **`addTags(clinicId: string, adminId: string, newTags: { tags?: ClinicTag[] }): Promise<Clinic>`**
  - Adds specified tags to a clinic's `tags` array using `TagUtils.addTags`.
  - Ensures the admin has permission. Prevents duplicates.
- **`removeTags(clinicId: string, adminId: string, tagsToRemove: { tags?: ClinicTag[] }): Promise<Clinic>`**
  - Removes specified tags from a clinic's `tags` array using `TagUtils.removeTags`.
  - Ensures the admin has permission.
- **`getClinicsByAdmin(adminId: string, options?: { isActive?: boolean; includeGroupClinics?: boolean }): Promise<Clinic[]>`**
  - Retrieves clinics associated with a specific admin using `ClinicUtils.getClinicsByAdmin`.
  - Handles options for filtering by `isActive` and including all group clinics for owners.
- **`getActiveClinicsByAdmin(adminId: string): Promise<Clinic[]>`**
  - Retrieves only the active clinics associated with a specific admin using `ClinicUtils.getActiveClinicsByAdmin`.
- **`createClinicBranch(clinicGroupId: string, setupData: ClinicBranchSetupData, adminId: string): Promise<Clinic>`**
  - Creates a new clinic (branch) within an existing `clinicGroupId`.
  - Validates group existence. Uses `createClinic` internally.
- **`getClinicById(clinicId: string): Promise<Clinic | null>`**
  - Retrieves a single clinic document by its ID using `ClinicUtils.getClinicById`.
- **`getAllClinics(pagination?: number, lastDoc?: any): Promise<{ clinics: Clinic[]; lastDoc: any }>`**
  - Retrieves all clinics, ordered by ID, optionally with pagination, using `ClinicUtils.getAllClinics`.
- **`getAllClinicsInRange(center: { latitude: number; longitude: number }, rangeInKm: number, pagination?: number, lastDoc?: any): Promise<{ clinics: (Clinic & { distance: number })[]; lastDoc: any }>`**
  - Retrieves all clinics within a range, sorted by distance, optionally with pagination, using `ClinicUtils.getAllClinicsInRange`.
- **`getClinicsByFilters(filters: { ... }): Promise<{ clinics: (Clinic & { distance?: number })[]; lastDoc: any }>`**
  - Retrieves clinics based on complex filters (location, tags, procedures, rating, etc.).
  - Uses `FilterUtils.getClinicsByFilters` for combined query and in-memory filtering.

### Utility Functions (`./utils`)

- **`clinic.utils.ts`**: Core fetching (`getClinic`, `getClinicsByGroup`, etc.), create/update/deactivate logic wrappers, admin checks.
- **`filter.utils.ts`**: Complex filtering logic (`getClinicsByFilters`), including geo-queries.
- **`clinic-group.utils.ts`**: Helpers for clinic group interactions (used by `ClinicGroupService`).
- **`tag.utils.ts`**: Logic for adding/removing tags (`addTags`, `removeTags`).
- **`photos.utils.ts`**: Firebase Storage interactions (`uploadPhoto`, `uploadMultiplePhotos`, `deletePhoto`).
- **`admin.utils.ts`**: Helpers for clinic admin interactions (used by `ClinicAdminService`).
- **`search.utils.ts`**: Geo-radius search logic (`findClinicsInRadius`).

# Clinic Services

This directory contains services related to clinic operations.

## Services

### ClinicService

Handles clinic CRUD operations, searching, and management.

### ClinicGroupService

Manages clinic groups and their operations.

### ClinicAdminService

Handles clinic administrator operations.

### PractitionerInviteService

Manages the practitioner invitation system for clinics.

## PractitionerInviteService

The `PractitionerInviteService` handles the complete flow of inviting practitioners to join clinics.

### Flow

1. Clinic searches for active practitioners by name or email (using existing practitioner search services)
2. Clinic creates an invite request with proposed working hours
3. Practitioner receives the invite and can accept or reject it
4. Admins can cancel pending invites if needed
5. Connection between practitioner and clinic is handled by aggregation side-effects

### Methods

#### Core Invite Methods

- `createInviteAdmin(practitionerId, clinicId, proposedWorkingHours, invitedBy, message?)` - Create a new invite
- `acceptInviteDoctor(inviteId)` - Doctor accepts an invite
- `rejectInviteDoctor(inviteId, rejectionReason?)` - Doctor rejects an invite
- `cancelInviteAdmin(inviteId, cancelReason?)` - Admin cancels a pending invite

#### Retrieval Methods

- `getAllInvitesDoctor(practitionerId, statusFilter?)` - Get all invites for a practitioner
- `getAllInvitesClinic(clinicId, statusFilter?)` - Get all invites for a clinic
- `getInviteById(inviteId)` - Get a specific invite
- `getInvitesWithFilters(filters)` - Advanced filtering

#### Admin Methods

- `deleteInvite(inviteId)` - Delete an invite (admin only)

### Status Filtering

All retrieval methods support filtering by invite status:

- `PENDING` - Invite has been sent but not responded to
- `ACCEPTED` - Practitioner has accepted the invite
- `REJECTED` - Practitioner has rejected the invite
- `CANCELLED` - Admin has cancelled the invite

### Usage Example

```typescript
// Create service instance
const practitionerInviteService = new PractitionerInviteService(db, auth, app);

// Create an invite
const invite = await practitionerInviteService.createInviteAdmin(
  "practitioner123",
  "clinic456",
  proposedHours,
  "admin789",
  "We'd love to have you join our team!"
);

// Get clinic's invites
const clinicInvites = await practitionerInviteService.getAllInvitesClinic(
  "clinic456",
  [PractitionerInviteStatus.PENDING]
);

// Doctor accepts invite
const acceptedInvite = await practitionerInviteService.acceptInviteDoctor(
  invite.id
);

// Admin cancels pending invite
const cancelledInvite = await practitionerInviteService.cancelInviteAdmin(
  invite.id,
  "Position no longer available"
);
```

### Data Structure

The service uses existing aggregation types from the profile module:

- `PractitionerProfileInfo` - Complete practitioner information including certification
- `ClinicInfo` - Complete clinic information including location and contact details

### Error Handling

All methods include proper error handling and logging. Common errors:

- Practitioner/Clinic not found
- Duplicate pending invites
- Invalid status transitions (only pending invites can be accepted/rejected/cancelled)
- Permission issues

### Firestore Collections

- Collection: `practitioner-invites`
- Documents contain complete invite information with embedded practitioner and clinic details
