@blackcode_sa/metaestetics-api 1.13.20 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7838,7 +7838,7 @@ var contraindicationSchema = import_zod6.z.object({
7838
7838
  notes: import_zod6.z.string().optional().nullable(),
7839
7839
  isActive: import_zod6.z.boolean()
7840
7840
  });
7841
- var medicationSchema = import_zod6.z.object({
7841
+ var baseMedicationSchema = import_zod6.z.object({
7842
7842
  name: import_zod6.z.string().min(1),
7843
7843
  dosage: import_zod6.z.string().min(1),
7844
7844
  frequency: import_zod6.z.string().min(1),
@@ -7846,6 +7846,24 @@ var medicationSchema = import_zod6.z.object({
7846
7846
  endDate: timestampSchema.optional().nullable(),
7847
7847
  prescribedBy: import_zod6.z.string().optional().nullable()
7848
7848
  });
7849
+ var medicationSchema = baseMedicationSchema.refine(
7850
+ (data) => {
7851
+ if (!data.endDate) {
7852
+ return true;
7853
+ }
7854
+ if (!data.startDate) {
7855
+ return false;
7856
+ }
7857
+ const startDate = data.startDate.toDate();
7858
+ const endDate = data.endDate.toDate();
7859
+ return endDate >= startDate;
7860
+ },
7861
+ {
7862
+ message: "End date requires a start date and must be equal to or after start date",
7863
+ path: ["endDate"]
7864
+ // This will attach the error to the endDate field
7865
+ }
7866
+ );
7849
7867
  var patientMedicalInfoSchema = import_zod6.z.object({
7850
7868
  patientId: import_zod6.z.string(),
7851
7869
  vitalStats: vitalStatsSchema,
@@ -7881,9 +7899,26 @@ var updateContraindicationSchema = contraindicationSchema.partial().extend({
7881
7899
  contraindicationIndex: import_zod6.z.number().min(0)
7882
7900
  });
7883
7901
  var addMedicationSchema = medicationSchema;
7884
- var updateMedicationSchema = medicationSchema.partial().extend({
7902
+ var updateMedicationSchema = baseMedicationSchema.partial().extend({
7885
7903
  medicationIndex: import_zod6.z.number().min(0)
7886
- });
7904
+ }).refine(
7905
+ (data) => {
7906
+ if (!data.endDate) {
7907
+ return true;
7908
+ }
7909
+ if (!data.startDate) {
7910
+ return false;
7911
+ }
7912
+ const startDate = data.startDate.toDate();
7913
+ const endDate = data.endDate.toDate();
7914
+ return endDate >= startDate;
7915
+ },
7916
+ {
7917
+ message: "End date requires a start date and must be equal to or after start date",
7918
+ path: ["endDate"]
7919
+ // This will attach the error to the endDate field
7920
+ }
7921
+ );
7887
7922
 
7888
7923
  // src/validations/patient.schema.ts
7889
7924
  var locationDataSchema = import_zod7.z.object({
@@ -11680,6 +11715,240 @@ var PractitionerService = class extends BaseService {
11680
11715
  return null;
11681
11716
  }
11682
11717
  }
11718
+ /**
11719
+ * Finds all draft practitioner profiles by email address
11720
+ * Used when a doctor signs in with Google to show all clinic invitations
11721
+ *
11722
+ * @param email - Email address to search for
11723
+ * @returns Array of draft practitioner profiles with clinic information
11724
+ *
11725
+ * @remarks
11726
+ * Requires Firestore composite index on:
11727
+ * - Collection: practitioners
11728
+ * - Fields: basicInfo.email (Ascending), status (Ascending), userRef (Ascending)
11729
+ */
11730
+ async getDraftProfilesByEmail(email) {
11731
+ try {
11732
+ const normalizedEmail = email.toLowerCase().trim();
11733
+ console.log("[PRACTITIONER] Searching for all draft practitioners by email", {
11734
+ email: normalizedEmail
11735
+ });
11736
+ const q = (0, import_firestore32.query)(
11737
+ (0, import_firestore32.collection)(this.db, PRACTITIONERS_COLLECTION),
11738
+ (0, import_firestore32.where)("basicInfo.email", "==", normalizedEmail),
11739
+ (0, import_firestore32.where)("status", "==", "draft" /* DRAFT */),
11740
+ (0, import_firestore32.where)("userRef", "==", "")
11741
+ );
11742
+ const querySnapshot = await (0, import_firestore32.getDocs)(q);
11743
+ if (querySnapshot.empty) {
11744
+ console.log("[PRACTITIONER] No draft practitioners found for email", {
11745
+ email: normalizedEmail
11746
+ });
11747
+ return [];
11748
+ }
11749
+ const draftPractitioners = querySnapshot.docs.map(
11750
+ (doc47) => doc47.data()
11751
+ );
11752
+ console.log("[PRACTITIONER] Found draft practitioners", {
11753
+ email: normalizedEmail,
11754
+ count: draftPractitioners.length,
11755
+ practitionerIds: draftPractitioners.map((p) => p.id)
11756
+ });
11757
+ return draftPractitioners;
11758
+ } catch (error) {
11759
+ console.error(
11760
+ "[PRACTITIONER] Error finding draft practitioners by email:",
11761
+ error
11762
+ );
11763
+ return [];
11764
+ }
11765
+ }
11766
+ /**
11767
+ * Claims a draft practitioner profile and links it to a user account
11768
+ * Used when a doctor selects which clinic(s) to join after Google Sign-In
11769
+ *
11770
+ * @param practitionerId - ID of the draft practitioner profile to claim
11771
+ * @param userId - ID of the user account to link the profile to
11772
+ * @returns The claimed practitioner profile
11773
+ */
11774
+ async claimDraftProfileWithGoogle(practitionerId, userId) {
11775
+ try {
11776
+ console.log("[PRACTITIONER] Claiming draft profile with Google", {
11777
+ practitionerId,
11778
+ userId
11779
+ });
11780
+ const practitioner = await this.getPractitioner(practitionerId);
11781
+ if (!practitioner) {
11782
+ throw new Error(`Practitioner ${practitionerId} not found`);
11783
+ }
11784
+ if (practitioner.status !== "draft" /* DRAFT */) {
11785
+ throw new Error("This practitioner profile has already been claimed");
11786
+ }
11787
+ const existingPractitioner = await this.getPractitionerByUserRef(userId);
11788
+ if (existingPractitioner) {
11789
+ console.log("[PRACTITIONER] User already has profile, merging clinics");
11790
+ const mergedClinics = Array.from(/* @__PURE__ */ new Set([
11791
+ ...existingPractitioner.clinics,
11792
+ ...practitioner.clinics
11793
+ ]));
11794
+ const mergedWorkingHours = [...existingPractitioner.clinicWorkingHours];
11795
+ for (const workingHours of practitioner.clinicWorkingHours) {
11796
+ if (!mergedWorkingHours.find((wh) => wh.clinicId === workingHours.clinicId)) {
11797
+ mergedWorkingHours.push(workingHours);
11798
+ }
11799
+ }
11800
+ const mergedClinicsInfo = [...existingPractitioner.clinicsInfo];
11801
+ for (const clinicInfo of practitioner.clinicsInfo) {
11802
+ if (!mergedClinicsInfo.find((ci) => ci.id === clinicInfo.id)) {
11803
+ mergedClinicsInfo.push(clinicInfo);
11804
+ }
11805
+ }
11806
+ const updatedPractitioner2 = await this.updatePractitioner(existingPractitioner.id, {
11807
+ clinics: mergedClinics,
11808
+ clinicWorkingHours: mergedWorkingHours,
11809
+ clinicsInfo: mergedClinicsInfo
11810
+ });
11811
+ await (0, import_firestore32.deleteDoc)((0, import_firestore32.doc)(this.db, PRACTITIONERS_COLLECTION, practitionerId));
11812
+ const activeTokens2 = await this.getPractitionerActiveTokens(practitionerId);
11813
+ for (const token of activeTokens2) {
11814
+ await this.markTokenAsUsed(token.id, practitionerId, userId);
11815
+ }
11816
+ return updatedPractitioner2;
11817
+ }
11818
+ const updatedPractitioner = await this.updatePractitioner(practitioner.id, {
11819
+ userRef: userId,
11820
+ status: "active" /* ACTIVE */
11821
+ });
11822
+ const activeTokens = await this.getPractitionerActiveTokens(practitionerId);
11823
+ for (const token of activeTokens) {
11824
+ await this.markTokenAsUsed(token.id, practitionerId, userId);
11825
+ }
11826
+ console.log("[PRACTITIONER] Draft profile claimed successfully", {
11827
+ practitionerId: updatedPractitioner.id,
11828
+ userId
11829
+ });
11830
+ return updatedPractitioner;
11831
+ } catch (error) {
11832
+ console.error(
11833
+ "[PRACTITIONER] Error claiming draft profile with Google:",
11834
+ error
11835
+ );
11836
+ throw error;
11837
+ }
11838
+ }
11839
+ /**
11840
+ * Claims multiple draft practitioner profiles and merges them into one profile
11841
+ * Used when a doctor selects multiple clinics to join after Google Sign-In
11842
+ *
11843
+ * @param practitionerIds - Array of draft practitioner profile IDs to claim
11844
+ * @param userId - ID of the user account to link the profiles to
11845
+ * @returns The claimed practitioner profile (first one becomes main, others merged)
11846
+ */
11847
+ async claimMultipleDraftProfilesWithGoogle(practitionerIds, userId) {
11848
+ try {
11849
+ if (practitionerIds.length === 0) {
11850
+ throw new Error("No practitioner IDs provided");
11851
+ }
11852
+ console.log("[PRACTITIONER] Claiming multiple draft profiles with Google", {
11853
+ practitionerIds,
11854
+ userId,
11855
+ count: practitionerIds.length
11856
+ });
11857
+ const draftProfiles = await Promise.all(
11858
+ practitionerIds.map((id) => this.getPractitioner(id))
11859
+ );
11860
+ const validDrafts = draftProfiles.filter((p) => {
11861
+ if (!p) return false;
11862
+ if (p.status !== "draft" /* DRAFT */) {
11863
+ throw new Error(`Practitioner ${p.id} has already been claimed`);
11864
+ }
11865
+ return true;
11866
+ });
11867
+ if (validDrafts.length === 0) {
11868
+ throw new Error("No valid draft profiles found");
11869
+ }
11870
+ const existingPractitioner = await this.getPractitionerByUserRef(userId);
11871
+ if (existingPractitioner) {
11872
+ let mergedClinics2 = new Set(existingPractitioner.clinics);
11873
+ let mergedWorkingHours2 = [...existingPractitioner.clinicWorkingHours];
11874
+ let mergedClinicsInfo2 = [...existingPractitioner.clinicsInfo];
11875
+ for (const draft of validDrafts) {
11876
+ draft.clinics.forEach((clinicId) => mergedClinics2.add(clinicId));
11877
+ for (const workingHours of draft.clinicWorkingHours) {
11878
+ if (!mergedWorkingHours2.find((wh) => wh.clinicId === workingHours.clinicId)) {
11879
+ mergedWorkingHours2.push(workingHours);
11880
+ }
11881
+ }
11882
+ for (const clinicInfo of draft.clinicsInfo) {
11883
+ if (!mergedClinicsInfo2.find((ci) => ci.id === clinicInfo.id)) {
11884
+ mergedClinicsInfo2.push(clinicInfo);
11885
+ }
11886
+ }
11887
+ }
11888
+ const updatedPractitioner2 = await this.updatePractitioner(existingPractitioner.id, {
11889
+ clinics: Array.from(mergedClinics2),
11890
+ clinicWorkingHours: mergedWorkingHours2,
11891
+ clinicsInfo: mergedClinicsInfo2
11892
+ });
11893
+ for (const draft of validDrafts) {
11894
+ await (0, import_firestore32.deleteDoc)((0, import_firestore32.doc)(this.db, PRACTITIONERS_COLLECTION, draft.id));
11895
+ const activeTokens = await this.getPractitionerActiveTokens(draft.id);
11896
+ for (const token of activeTokens) {
11897
+ await this.markTokenAsUsed(token.id, draft.id, userId);
11898
+ }
11899
+ }
11900
+ return updatedPractitioner2;
11901
+ }
11902
+ const mainDraft = validDrafts[0];
11903
+ const otherDrafts = validDrafts.slice(1);
11904
+ let mergedClinics = new Set(mainDraft.clinics);
11905
+ let mergedWorkingHours = [...mainDraft.clinicWorkingHours];
11906
+ let mergedClinicsInfo = [...mainDraft.clinicsInfo];
11907
+ for (const draft of otherDrafts) {
11908
+ draft.clinics.forEach((clinicId) => mergedClinics.add(clinicId));
11909
+ for (const workingHours of draft.clinicWorkingHours) {
11910
+ if (!mergedWorkingHours.find((wh) => wh.clinicId === workingHours.clinicId)) {
11911
+ mergedWorkingHours.push(workingHours);
11912
+ }
11913
+ }
11914
+ for (const clinicInfo of draft.clinicsInfo) {
11915
+ if (!mergedClinicsInfo.find((ci) => ci.id === clinicInfo.id)) {
11916
+ mergedClinicsInfo.push(clinicInfo);
11917
+ }
11918
+ }
11919
+ }
11920
+ const updatedPractitioner = await this.updatePractitioner(mainDraft.id, {
11921
+ userRef: userId,
11922
+ status: "active" /* ACTIVE */,
11923
+ clinics: Array.from(mergedClinics),
11924
+ clinicWorkingHours: mergedWorkingHours,
11925
+ clinicsInfo: mergedClinicsInfo
11926
+ });
11927
+ const mainActiveTokens = await this.getPractitionerActiveTokens(mainDraft.id);
11928
+ for (const token of mainActiveTokens) {
11929
+ await this.markTokenAsUsed(token.id, mainDraft.id, userId);
11930
+ }
11931
+ for (const draft of otherDrafts) {
11932
+ await (0, import_firestore32.deleteDoc)((0, import_firestore32.doc)(this.db, PRACTITIONERS_COLLECTION, draft.id));
11933
+ const activeTokens = await this.getPractitionerActiveTokens(draft.id);
11934
+ for (const token of activeTokens) {
11935
+ await this.markTokenAsUsed(token.id, draft.id, userId);
11936
+ }
11937
+ }
11938
+ console.log("[PRACTITIONER] Multiple draft profiles claimed successfully", {
11939
+ practitionerId: updatedPractitioner.id,
11940
+ userId,
11941
+ mergedCount: validDrafts.length
11942
+ });
11943
+ return updatedPractitioner;
11944
+ } catch (error) {
11945
+ console.error(
11946
+ "[PRACTITIONER] Error claiming multiple draft profiles with Google:",
11947
+ error
11948
+ );
11949
+ throw error;
11950
+ }
11951
+ }
11683
11952
  /**
11684
11953
  * Dohvata sve zdravstvene radnike za određenu kliniku sa statusom ACTIVE
11685
11954
  */
@@ -12315,6 +12584,9 @@ var PractitionerService = class extends BaseService {
12315
12584
  */
12316
12585
  async EnableFreeConsultation(practitionerId, clinicId) {
12317
12586
  try {
12587
+ console.log(
12588
+ `[EnableFreeConsultation] Starting for practitioner ${practitionerId} in clinic ${clinicId}`
12589
+ );
12318
12590
  await this.ensureFreeConsultationInfrastructure();
12319
12591
  const practitioner = await this.getPractitioner(practitionerId);
12320
12592
  if (!practitioner) {
@@ -12330,32 +12602,83 @@ var PractitionerService = class extends BaseService {
12330
12602
  );
12331
12603
  }
12332
12604
  const [activeProcedures, inactiveProcedures] = await Promise.all([
12333
- this.getProcedureService().getProceduresByPractitioner(practitionerId),
12605
+ this.getProcedureService().getProceduresByPractitioner(
12606
+ practitionerId,
12607
+ void 0,
12608
+ // clinicBranchId
12609
+ false
12610
+ // excludeDraftPractitioners - allow draft practitioners
12611
+ ),
12334
12612
  this.getProcedureService().getInactiveProceduresByPractitioner(
12335
12613
  practitionerId
12336
12614
  )
12337
12615
  ]);
12338
12616
  const allProcedures = [...activeProcedures, ...inactiveProcedures];
12339
- const existingConsultation = allProcedures.find(
12617
+ const existingConsultations = allProcedures.filter(
12340
12618
  (procedure) => procedure.technology.id === "free-consultation-tech" && procedure.clinicBranchId === clinicId
12341
12619
  );
12620
+ console.log(
12621
+ `[EnableFreeConsultation] Found ${existingConsultations.length} existing free consultation(s)`
12622
+ );
12623
+ if (existingConsultations.length > 1) {
12624
+ console.warn(
12625
+ `[EnableFreeConsultation] WARNING: Found ${existingConsultations.length} duplicate free consultations for practitioner ${practitionerId} in clinic ${clinicId}`
12626
+ );
12627
+ for (let i = 1; i < existingConsultations.length; i++) {
12628
+ console.log(
12629
+ `[EnableFreeConsultation] Deactivating duplicate consultation ${existingConsultations[i].id}`
12630
+ );
12631
+ await this.getProcedureService().deactivateProcedure(
12632
+ existingConsultations[i].id
12633
+ );
12634
+ }
12635
+ }
12636
+ const existingConsultation = existingConsultations[0];
12342
12637
  if (existingConsultation) {
12343
12638
  if (existingConsultation.isActive) {
12344
12639
  console.log(
12345
- `Free consultation already active for practitioner ${practitionerId} in clinic ${clinicId}`
12640
+ `[EnableFreeConsultation] Free consultation already active for practitioner ${practitionerId} in clinic ${clinicId}`
12346
12641
  );
12347
12642
  return;
12348
12643
  } else {
12644
+ console.log(
12645
+ `[EnableFreeConsultation] Reactivating existing consultation ${existingConsultation.id}`
12646
+ );
12349
12647
  await this.getProcedureService().updateProcedure(
12350
12648
  existingConsultation.id,
12351
12649
  { isActive: true }
12352
12650
  );
12353
12651
  console.log(
12354
- `Reactivated existing free consultation for practitioner ${practitionerId} in clinic ${clinicId}`
12652
+ `[EnableFreeConsultation] Reactivated existing free consultation for practitioner ${practitionerId} in clinic ${clinicId}`
12355
12653
  );
12356
12654
  return;
12357
12655
  }
12358
12656
  }
12657
+ console.log(
12658
+ `[EnableFreeConsultation] Final race condition check before creating new procedure`
12659
+ );
12660
+ const finalCheckProcedures = await this.getProcedureService().getProceduresByPractitioner(
12661
+ practitionerId,
12662
+ void 0,
12663
+ // clinicBranchId
12664
+ false
12665
+ // excludeDraftPractitioners - allow draft practitioners
12666
+ );
12667
+ const raceConditionCheck = finalCheckProcedures.find(
12668
+ (procedure) => procedure.technology.id === "free-consultation-tech" && procedure.clinicBranchId === clinicId
12669
+ );
12670
+ if (raceConditionCheck) {
12671
+ console.log(
12672
+ `[EnableFreeConsultation] Race condition detected! Procedure was created by another request. Using existing procedure ${raceConditionCheck.id}`
12673
+ );
12674
+ if (!raceConditionCheck.isActive) {
12675
+ await this.getProcedureService().updateProcedure(
12676
+ raceConditionCheck.id,
12677
+ { isActive: true }
12678
+ );
12679
+ }
12680
+ return;
12681
+ }
12359
12682
  const consultationData = {
12360
12683
  name: "Free Consultation",
12361
12684
  nameLower: "free consultation",
@@ -12375,15 +12698,18 @@ var PractitionerService = class extends BaseService {
12375
12698
  photos: []
12376
12699
  // No photos for consultation
12377
12700
  };
12701
+ console.log(
12702
+ `[EnableFreeConsultation] Creating new free consultation procedure`
12703
+ );
12378
12704
  await this.getProcedureService().createConsultationProcedure(
12379
12705
  consultationData
12380
12706
  );
12381
12707
  console.log(
12382
- `Free consultation enabled for practitioner ${practitionerId} in clinic ${clinicId}`
12708
+ `[EnableFreeConsultation] Successfully created free consultation for practitioner ${practitionerId} in clinic ${clinicId}`
12383
12709
  );
12384
12710
  } catch (error) {
12385
12711
  console.error(
12386
- `Error enabling free consultation for practitioner ${practitionerId} in clinic ${clinicId}:`,
12712
+ `[EnableFreeConsultation] Error enabling free consultation for practitioner ${practitionerId} in clinic ${clinicId}:`,
12387
12713
  error
12388
12714
  );
12389
12715
  throw error;
@@ -12479,17 +12805,40 @@ var PractitionerService = class extends BaseService {
12479
12805
  );
12480
12806
  }
12481
12807
  const existingProcedures = await this.getProcedureService().getProceduresByPractitioner(
12482
- practitionerId
12808
+ practitionerId,
12809
+ void 0,
12810
+ // clinicBranchId (optional)
12811
+ false
12812
+ // excludeDraftPractitioners - must be false to find procedures for draft practitioners
12813
+ );
12814
+ console.log(
12815
+ `[DisableFreeConsultation] Found ${existingProcedures.length} procedures for practitioner ${practitionerId}`
12483
12816
  );
12484
12817
  const freeConsultation = existingProcedures.find(
12485
12818
  (procedure) => procedure.technology.id === "free-consultation-tech" && procedure.clinicBranchId === clinicId && procedure.isActive
12486
12819
  );
12487
12820
  if (!freeConsultation) {
12488
12821
  console.log(
12489
- `No active free consultation found for practitioner ${practitionerId} in clinic ${clinicId}`
12822
+ `[DisableFreeConsultation] No active free consultation found for practitioner ${practitionerId} in clinic ${clinicId}`
12823
+ );
12824
+ console.log(
12825
+ `[DisableFreeConsultation] Existing procedures:`,
12826
+ existingProcedures.map((p) => {
12827
+ var _a;
12828
+ return {
12829
+ id: p.id,
12830
+ name: p.name,
12831
+ technologyId: (_a = p.technology) == null ? void 0 : _a.id,
12832
+ clinicBranchId: p.clinicBranchId,
12833
+ isActive: p.isActive
12834
+ };
12835
+ })
12490
12836
  );
12491
12837
  return;
12492
12838
  }
12839
+ console.log(
12840
+ `[DisableFreeConsultation] Found free consultation procedure ${freeConsultation.id}, deactivating...`
12841
+ );
12493
12842
  await this.getProcedureService().deactivateProcedure(freeConsultation.id);
12494
12843
  console.log(
12495
12844
  `Free consultation disabled for practitioner ${practitionerId} in clinic ${clinicId}`
@@ -15672,6 +16021,135 @@ var AuthService = class extends BaseService {
15672
16021
  throw handleFirebaseError(error);
15673
16022
  }
15674
16023
  }
16024
+ /**
16025
+ * Signs up or signs in a practitioner with Google authentication.
16026
+ * Checks for existing practitioner account or draft profiles.
16027
+ *
16028
+ * @param idToken - The Google ID token obtained from the mobile app
16029
+ * @returns Object containing user, practitioner (if exists), and draft profiles (if any)
16030
+ */
16031
+ async signUpPractitionerWithGoogle(idToken) {
16032
+ try {
16033
+ console.log("[AUTH] Starting practitioner Google Sign-In/Sign-Up");
16034
+ let email;
16035
+ try {
16036
+ const payloadBase64 = idToken.split(".")[1];
16037
+ const payloadJson = globalThis.atob ? globalThis.atob(payloadBase64) : Buffer.from(payloadBase64, "base64").toString("utf8");
16038
+ const payload = JSON.parse(payloadJson);
16039
+ email = payload.email;
16040
+ } catch (decodeError) {
16041
+ console.error("[AUTH] Failed to decode email from Google ID token:", decodeError);
16042
+ throw new AuthError(
16043
+ "Unable to read email from Google token. Please try again.",
16044
+ "AUTH/INVALID_GOOGLE_TOKEN",
16045
+ 400
16046
+ );
16047
+ }
16048
+ if (!email) {
16049
+ throw new AuthError(
16050
+ "Unable to read email from Google token. Please try again.",
16051
+ "AUTH/INVALID_GOOGLE_TOKEN",
16052
+ 400
16053
+ );
16054
+ }
16055
+ const normalizedEmail = email.toLowerCase().trim();
16056
+ console.log("[AUTH] Extracted email from Google token:", normalizedEmail);
16057
+ const methods = await (0, import_auth8.fetchSignInMethodsForEmail)(this.auth, normalizedEmail);
16058
+ const hasGoogleMethod = methods.includes(import_auth8.GoogleAuthProvider.GOOGLE_SIGN_IN_METHOD);
16059
+ const hasEmailMethod = methods.includes(import_auth8.EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD);
16060
+ const practitionerService = new PractitionerService(this.db, this.auth, this.app);
16061
+ if (hasGoogleMethod) {
16062
+ console.log("[AUTH] User exists with Google provider, signing in");
16063
+ const credential2 = import_auth8.GoogleAuthProvider.credential(idToken);
16064
+ const { user: firebaseUser2 } = await (0, import_auth8.signInWithCredential)(this.auth, credential2);
16065
+ const existingUser2 = await this.userService.getUserById(firebaseUser2.uid);
16066
+ if (!existingUser2) {
16067
+ await (0, import_auth8.signOut)(this.auth);
16068
+ throw new AuthError(
16069
+ "No account found. Please contact support.",
16070
+ "AUTH/USER_NOT_FOUND",
16071
+ 404
16072
+ );
16073
+ }
16074
+ let practitioner2 = null;
16075
+ if (existingUser2.practitionerProfile) {
16076
+ practitioner2 = await practitionerService.getPractitioner(existingUser2.practitionerProfile);
16077
+ }
16078
+ const draftProfiles2 = await practitionerService.getDraftProfilesByEmail(normalizedEmail);
16079
+ return {
16080
+ user: existingUser2,
16081
+ practitioner: practitioner2,
16082
+ draftProfiles: draftProfiles2
16083
+ };
16084
+ }
16085
+ if (hasEmailMethod && !hasGoogleMethod) {
16086
+ console.log("[AUTH] User exists with email/password only");
16087
+ throw new AuthError(
16088
+ "An account with this email already exists. Please sign in with your email and password, then link your Google account in settings.",
16089
+ "AUTH/EMAIL_ALREADY_EXISTS",
16090
+ 409
16091
+ );
16092
+ }
16093
+ console.log("[AUTH] Signing in with Google credential");
16094
+ const credential = import_auth8.GoogleAuthProvider.credential(idToken);
16095
+ let firebaseUser;
16096
+ try {
16097
+ const result = await (0, import_auth8.signInWithCredential)(this.auth, credential);
16098
+ firebaseUser = result.user;
16099
+ } catch (error) {
16100
+ if (error.code === "auth/account-exists-with-different-credential") {
16101
+ throw new AuthError(
16102
+ "An account with this email already exists. Please sign in with your email and password, then link your Google account in settings.",
16103
+ "AUTH/EMAIL_ALREADY_EXISTS",
16104
+ 409
16105
+ );
16106
+ }
16107
+ throw error;
16108
+ }
16109
+ let existingUser = null;
16110
+ try {
16111
+ const existingUserDoc = await this.userService.getUserById(firebaseUser.uid);
16112
+ if (existingUserDoc) {
16113
+ existingUser = existingUserDoc;
16114
+ console.log("[AUTH] Found existing User document");
16115
+ }
16116
+ } catch (error) {
16117
+ console.error("[AUTH] Error checking for existing user:", error);
16118
+ }
16119
+ console.log("[AUTH] Checking for draft profiles");
16120
+ const draftProfiles = await practitionerService.getDraftProfilesByEmail(normalizedEmail);
16121
+ let user;
16122
+ if (existingUser) {
16123
+ user = existingUser;
16124
+ console.log("[AUTH] Using existing user account");
16125
+ } else {
16126
+ user = await this.userService.createUser(firebaseUser, ["practitioner" /* PRACTITIONER */], {
16127
+ skipProfileCreation: true
16128
+ });
16129
+ console.log("[AUTH] Created new user account");
16130
+ }
16131
+ let practitioner = null;
16132
+ if (user.practitionerProfile) {
16133
+ practitioner = await practitionerService.getPractitioner(user.practitionerProfile);
16134
+ }
16135
+ console.log("[AUTH] Google Sign-In complete", {
16136
+ userId: user.uid,
16137
+ hasPractitioner: !!practitioner,
16138
+ draftProfilesCount: draftProfiles.length
16139
+ });
16140
+ return {
16141
+ user,
16142
+ practitioner,
16143
+ draftProfiles
16144
+ };
16145
+ } catch (error) {
16146
+ console.error("[AUTH] Error in signUpPractitionerWithGoogle:", error);
16147
+ if (error instanceof AuthError) {
16148
+ throw error;
16149
+ }
16150
+ throw handleFirebaseError(error);
16151
+ }
16152
+ }
15675
16153
  /**
15676
16154
  * Links a Google account to the currently signed-in user using an ID token.
15677
16155
  * This is used to upgrade an anonymous user or to allow an existing user