# Procedure Service

This service manages medical/aesthetic procedure data within the Firestore database. It handles the creation, retrieval, updating, and deletion of procedures linked to practitioners and clinics.

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

## `ProcedureService` Class

Extends `BaseService`.

### Constructor

```typescript
constructor(
  db: Firestore,
  auth: Auth,
  app: FirebaseApp,
  categoryService: CategoryService,
  subcategoryService: SubcategoryService,
  technologyService: TechnologyService,
  productService: ProductService,
  mediaService: MediaService
)
```

Initializes the service with Firestore, Auth, App instances, and required dependency services for fetching related category, subcategory, technology, and product details.

### Core Methods

- **`createProcedure(data: CreateProcedureData): Promise<Procedure>`**

  - Creates a new procedure document in Firestore.
  - Validates input data using `createProcedureSchema`.
  - Fetches full Category, Subcategory, Technology, and Product documents using injected services.
  - **Products Metadata:** Transforms `productsMetadata` from validation format (with `productId` strings) to full `ProcedureProduct` objects (with complete `product` objects).
  - Retrieves associated Clinic and Practitioner documents to embed `clinicInfo` and `doctorInfo`.
  - Processes and uploads media files using the MediaService.
  - Embeds treatment details from the technology (blocking conditions, benefits, requirements).
  - Initializes default `reviewInfo` with zero values.
  - Sets `isActive` to `true` by default.
  - **Aggregation Note:** Adding procedure info to associated Practitioner and Clinic documents is handled by Cloud Functions.

- **`getProcedure(id: string): Promise<Procedure | null>`**

  - Retrieves a single procedure document by its ID.
  - Returns null if not found.

- **`getProceduresByClinicBranch(clinicBranchId: string): Promise<Procedure[]>`**

  - Retrieves all active procedures associated with a specific clinic branch.
  - Uses a Firestore query with `where("clinicBranchId", "==", clinicBranchId)` and `where("isActive", "==", true)`.

- **`getProceduresByPractitioner(practitionerId: string): Promise<Procedure[]>`**

  - Retrieves all active procedures associated with a specific practitioner.
  - Uses a Firestore query with `where("practitionerId", "==", practitionerId)` and `where("isActive", "==", true)`.

- **`updateProcedure(id: string, data: UpdateProcedureData): Promise<Procedure>`**

  - Updates an existing procedure document with partial data.
  - Validates input data using `updateProcedureSchema`.
  - **Products Metadata:** If `productsMetadata` is provided, transforms from validation format to full `ProcedureProduct` objects.
  - Handles complex updates of related entities:
    - If practitioner changes: Fetches new practitioner data and updates embedded `doctorInfo`.
    - If clinic changes: Fetches new clinic data and updates embedded `clinicInfo`.
    - If category/subcategory changes: Fetches and updates embedded objects.
    - If technology/product changes: Fetches and updates embedded objects plus derived fields.
  - Processes and uploads media files if photos are provided.
  - Updates the `updatedAt` timestamp.
  - **Aggregation Note:** Updating aggregated data in related entities is handled by Cloud Functions.

- **`deactivateProcedure(id: string): Promise<void>`**

  - Sets the `isActive` flag of a procedure to `false`.
  - Updates the `updatedAt` timestamp.
  - **Aggregation Note:** Removing from active lists in related entities is handled by Cloud Functions.

- **`deleteProcedure(id: string): Promise<boolean>`**

  - Permanently deletes a procedure document from Firestore.
  - Returns `true` if successful, `false` if the document didn't exist.
  - **Aggregation Note:** Removing from related entities is handled by Cloud Functions.

- **`getAllowedTechnologies(practitioner: Practitioner): Promise<{ technologies: Technology[]; families: ProcedureFamily[]; categories: string[]; subcategories: string[]; }>`**

  - Delegates to the `TechnologyService.getAllowedTechnologies` method.
  - Determines which technologies a practitioner is certified to perform based on their certification level.
  - Returns an object containing arrays of allowed technologies, families, categories, and subcategories.

- **`getAllProcedures(pagination?: number, lastDoc?: any): Promise<{ procedures: Procedure[]; lastDoc: any }>`**

  - Retrieves all procedures, ordered by name.
  - Supports optional pagination with `limit` and `startAfter`.
  - Returns both the procedures array and the last document for cursor-based pagination.

- **`bulkCreateProcedures(baseData: Omit<CreateProcedureData, "practitionerId">, practitionerIds: string[]): Promise<Procedure[]>`**

  - Creates multiple procedures for different practitioners using common base data.
  - Optimized for bulk creation to reduce database reads and writes.
  - Uses Firestore batch writes for atomic creation.
  - Transforms `productsMetadata` once and reuses for all procedures.

- **`createConsultationProcedure(data: Omit<CreateProcedureData, "productId">): Promise<Procedure>`**
  - Special method for consultation procedures that don't require specific products.
  - Creates a placeholder product for consultation procedures.
  - Transforms `productsMetadata` from validation format to full objects.

### Private Helper Methods

- **`transformProductsMetadata(productsMetadata, technologyId): Promise<ProcedureProduct[]>`**

  - Converts validation format (with `productId` strings) to full `ProcedureProduct` objects.
  - Fetches complete product objects for each product ID.
  - Essential for maintaining type safety between validation and storage formats.

- **`processMedia(media, ownerId, collectionName): Promise<string | null>`**

  - Handles media file uploads using MediaService.
  - Supports string URLs, File objects, and Blob objects.

- **`processMediaArray(mediaArray, ownerId, collectionName): Promise<string[]>`**
  - Processes arrays of media resources.
  - Used for handling procedure photos.

## Data Transformation

The service handles a critical data transformation between input and storage:

- **Input (`CreateProcedureData`/`UpdateProcedureData`):** `productsMetadata` contains objects with `productId: string`

  ```typescript
  productsMetadata: {
    productId: string;
    price: number;
    currency: Currency;
    pricingMeasure: PricingMeasure;
    isDefault?: boolean;
  }[]
  ```

- **Storage (Firestore `Procedure`):** `productsMetadata` contains objects with `product: Product` (full object)
  ```typescript
  productsMetadata: ProcedureProduct[] // where ProcedureProduct contains full Product object
  ```

This transformation ensures:

1. **Type safety** throughout the application
2. **Denormalized data** for efficient queries (no need to fetch products separately)
3. **Consistent API contract** for clients (input uses IDs, storage uses full objects)
4. **Performance optimization** (embedded product data in procedures)

## Validation Schemas

The service uses different validation schemas for different stages:

- **`procedureProductDataSchema`**: Validates input data with `productId` strings (used in `createProcedureSchema`/`updateProcedureSchema`)
- **`storedProcedureProductSchema`**: Validates stored data with full `Product` objects (used in `procedureSchema`)
- **`createProcedureSchema`**: Validates procedure creation data (input format)
- **`updateProcedureSchema`**: Validates procedure update data (input format)
- **`procedureSchema`**: Validates complete stored procedure documents (storage format)

This ensures validation matches the actual data format at each stage of the process.
