"use strict";
const index = require("./i18n-Bs8xJzoY.cjs");
require("./index-Br7AzmuA.cjs");
const dayjs = require("dayjs");
const uuid = require("uuid");
const log$2 = index.logger.getSubLogger({
  prefix: ["orgDomains.ts"]
});
function getOrgSlug(hostname, forcedSlug) {
  if (forcedSlug) {
    if (process.env.NEXT_PUBLIC_IS_E2E) {
      log$2.debug("Using provided forcedSlug in E2E", {
        forcedSlug
      });
      return forcedSlug;
    }
    log$2.debug("Ignoring forcedSlug in non-test mode", {
      forcedSlug
    });
  }
  if (index.SINGLE_ORG_SLUG) {
    log$2.debug("In Single Org Mode, using SINGLE_ORG_SLUG as the Org slug", { SINGLE_ORG_SLUG: index.SINGLE_ORG_SLUG });
    return index.SINGLE_ORG_SLUG;
  }
  if (!hostname.includes(".")) {
    log$2.warn('Org support not enabled for hostname without "."', { hostname });
    return null;
  }
  const currentHostname = index.ALLOWED_HOSTNAMES.find((ahn) => {
    const url = new URL(index.WEBAPP_URL);
    const testHostname = `${url.hostname}${url.port ? `:${url.port}` : ""}`;
    return testHostname.endsWith(`.${ahn}`);
  });
  if (!currentHostname) {
    log$2.warn("Match of WEBAPP_URL with ALLOWED_HOSTNAMES failed", { WEBAPP_URL: index.WEBAPP_URL, ALLOWED_HOSTNAMES: index.ALLOWED_HOSTNAMES });
    return null;
  }
  const slug = hostname.replace(currentHostname ? `.${currentHostname}` : "", "");
  const hasNoDotInSlug = slug.indexOf(".") === -1;
  if (hasNoDotInSlug) {
    return slug;
  }
  log$2.warn("Derived slug ended up having dots, so not considering it an org domain", { slug });
  return null;
}
function getOrgDomainConfig({
  hostname,
  fallback,
  forcedSlug,
  isPlatform
}) {
  if (isPlatform && forcedSlug) {
    return {
      isValidOrgDomain: true,
      currentOrgDomain: forcedSlug
    };
  }
  return getOrgDomainConfigFromHostname({
    hostname,
    fallback,
    forcedSlug
  });
}
function orgDomainConfig(req, fallback) {
  const forPlatform = isPlatformRequest(req);
  const forcedSlugHeader = req?.headers?.["x-cal-force-slug"];
  const forcedSlug = forcedSlugHeader instanceof Array ? forcedSlugHeader[0] : forcedSlugHeader;
  if (forPlatform && forcedSlug) {
    return {
      isValidOrgDomain: true,
      currentOrgDomain: forcedSlug
    };
  }
  const hostname = req?.headers?.host || "";
  return getOrgDomainConfigFromHostname({
    hostname,
    fallback,
    forcedSlug
  });
}
function isPlatformRequest(req) {
  return !!req?.headers?.["x-cal-client-id"];
}
function getOrgDomainConfigFromHostname({
  hostname,
  fallback,
  forcedSlug
}) {
  const currentOrgDomain = getOrgSlug(hostname, forcedSlug);
  const isValidOrgDomain = currentOrgDomain !== null && !index.RESERVED_SUBDOMAINS.includes(currentOrgDomain);
  if (isValidOrgDomain || !fallback) {
    return {
      currentOrgDomain: isValidOrgDomain ? currentOrgDomain : null,
      isValidOrgDomain
    };
  }
  const fallbackOrgSlug = fallback;
  const isValidFallbackDomain = !index.RESERVED_SUBDOMAINS.includes(fallbackOrgSlug);
  return {
    currentOrgDomain: isValidFallbackDomain ? fallbackOrgSlug : null,
    isValidOrgDomain: isValidFallbackDomain
  };
}
function subdomainSuffix() {
  if (!index.IS_PRODUCTION && process.env.LOCAL_TESTING_DOMAIN_VERCEL) {
    return process.env.LOCAL_TESTING_DOMAIN_VERCEL;
  }
  const urlSplit = index.WEBAPP_URL.replace("https://", "")?.replace("http://", "").split(".");
  return urlSplit.length === 3 ? urlSplit.slice(1).join(".") : urlSplit.join(".");
}
function getOrgFullOrigin(slug, options = { protocol: true }) {
  if (!slug)
    return options.protocol ? index.WEBSITE_URL : index.WEBSITE_URL.replace("https://", "").replace("http://", "");
  const orgFullOrigin = `${options.protocol ? `${new URL(index.WEBSITE_URL).protocol}//` : ""}${slug}.${subdomainSuffix()}`;
  return orgFullOrigin;
}
function getSlugOrRequestedSlug(slug) {
  const slugifiedValue = index.slugify(slug);
  return {
    OR: [
      { slug: slugifiedValue },
      {
        metadata: {
          path: ["requestedSlug"],
          equals: slugifiedValue
        }
      }
    ]
  };
}
function whereClauseForOrgWithSlugOrRequestedSlug(slug) {
  const slugifiedValue = index.slugify(slug);
  return {
    OR: [
      { slug: slugifiedValue },
      {
        metadata: {
          path: ["requestedSlug"],
          equals: slug
        }
      }
    ],
    isOrganization: true
  };
}
const getOrgUsernameFromEmail = (email, autoAcceptEmailDomain) => {
  const [emailUser, emailDomain = ""] = email.split("@");
  const username = emailDomain === autoAcceptEmailDomain ? index.slugify(emailUser) : index.slugify(`${emailUser}-${emailDomain.split(".")[0]}`);
  return username;
};
const getParsedTeam = (team) => {
  const metadata = index.teamMetadataSchema.parse(team.metadata);
  const requestedSlug = metadata?.requestedSlug ?? null;
  const { metadata: _1, ...rest } = team;
  return {
    ...rest,
    requestedSlug,
    metadata: {
      ...metadata,
      /**
       * @deprecated New orgs that are being created won't have requestedSlug set as the organizations are created after payment and thus slug is always available
       */
      requestedSlug
    }
  };
};
const userSelect$1 = {
  name: true,
  avatarUrl: true,
  username: true,
  id: true,
  email: true,
  locale: true,
  defaultScheduleId: true,
  startTime: true,
  endTime: true,
  bufferTime: true,
  isPlatformManaged: true
};
const membershipSelect = {
  id: true,
  teamId: true,
  userId: true,
  accepted: true,
  role: true,
  disableImpersonation: true
};
const log$1 = index.logger.getSubLogger({ prefix: ["repository/profile"] });
const organizationSettingsSelect = {
  allowSEOIndexing: true,
  orgProfileRedirectsToVerifiedDomain: true
};
const organizationSelect = {
  id: true,
  slug: true,
  name: true,
  metadata: true,
  logoUrl: true,
  bannerUrl: true,
  isPlatform: true,
  hideBranding: true
};
const organizationWithSettingsSelect = {
  ...organizationSelect,
  organizationSettings: {
    select: organizationSettingsSelect
  }
};
var LookupTarget = /* @__PURE__ */ ((LookupTarget2) => {
  LookupTarget2[LookupTarget2["User"] = 0] = "User";
  LookupTarget2[LookupTarget2["Profile"] = 1] = "Profile";
  return LookupTarget2;
})(LookupTarget || {});
class ProfileRepository {
  static generateProfileUid() {
    return uuid.v4();
  }
  // This is a minimal replication of UserRepository.findById only selecting the data we need here to prevent circular dependency
  static async findUserByid({ id }) {
    const user = await index.prisma.user.findUnique({
      where: {
        id
      },
      select: {
        id: true,
        username: true,
        name: true,
        avatarUrl: true,
        startTime: true,
        endTime: true,
        bufferTime: true,
        metadata: true
      }
    });
    if (!user) {
      return null;
    }
    return {
      ...user,
      metadata: index.userMetadata.parse(user.metadata)
    };
  }
  static getInheritedDataFromUser({
    user
  }) {
    return {
      name: user.name,
      avatarUrl: user.avatarUrl,
      startTime: user.startTime,
      endTime: user.endTime,
      bufferTime: user.bufferTime
    };
  }
  static getLookupTarget(upId) {
    if (upId.startsWith("usr-")) {
      return {
        type: 0,
        id: parseInt(upId.replace("usr-", ""))
      };
    }
    return {
      type: 1,
      id: parseInt(upId)
    };
  }
  static async _create({
    userId,
    organizationId,
    username,
    email,
    movedFromUserId
  }) {
    log$1.debug("_create", index.safeStringify({ userId, organizationId, username, email }));
    return index.prisma.profile.create({
      data: {
        uid: ProfileRepository.generateProfileUid(),
        user: {
          connect: {
            id: userId
          }
        },
        organization: {
          connect: {
            id: organizationId
          }
        },
        ...movedFromUserId ? {
          movedFromUser: {
            connect: {
              id: movedFromUserId
            }
          }
        } : null,
        username: username || email.split("@")[0]
      }
    });
  }
  /**
   * Accepts `email` as a source to derive username from when username is null
   * @returns
   */
  static create({
    userId,
    organizationId,
    username,
    email
  }) {
    return ProfileRepository._create({ userId, organizationId, username, email });
  }
  static async upsert({
    create,
    update,
    updateWhere
  }) {
    return index.prisma.profile.upsert({
      create: {
        uid: ProfileRepository.generateProfileUid(),
        user: {
          connect: {
            id: create.userId
          }
        },
        organization: {
          connect: {
            id: create.organizationId
          }
        },
        username: create.username || create.email.split("@")[0]
      },
      update: {
        username: update.username || update.email.split("@")[0]
      },
      where: {
        userId_organizationId: {
          userId: updateWhere.userId,
          organizationId: updateWhere.organizationId
        }
      }
    });
  }
  static async createForExistingUser({
    userId,
    organizationId,
    username,
    email,
    movedFromUserId
  }) {
    return await ProfileRepository._create({
      userId,
      organizationId,
      username,
      email,
      movedFromUserId
    });
  }
  static async createManyForExistingUsers({
    users,
    organizationId,
    orgAutoAcceptEmail
  }) {
    await index.prisma.profile.createMany({
      data: users.map((user) => ({
        uid: ProfileRepository.generateProfileUid(),
        userId: user.id,
        organizationId,
        username: user?.username || getOrgUsernameFromEmail(user.email, orgAutoAcceptEmail)
      })),
      skipDuplicates: true
    });
    const createdProfiles = await index.prisma.profile.findMany({
      where: {
        userId: {
          in: users.map((user) => user.id)
        },
        organizationId
      }
    });
    for (let i = 0; i < createdProfiles.length; i += index.DATABASE_CHUNK_SIZE) {
      const profilesBatch = createdProfiles.slice(i, i + index.DATABASE_CHUNK_SIZE);
      await Promise.allSettled([
        profilesBatch.map((profile) => {
          index.prisma.user.update({
            where: {
              id: profile.userId
            },
            data: {
              movedToProfile: {
                connect: { id: profile.id }
              }
            }
          });
        })
      ]);
    }
  }
  static async createManyPromise({
    users,
    organizationId,
    orgAutoAcceptEmail
  }) {
    return await index.prisma.profile.createMany({
      data: users.map((user) => ({
        uid: ProfileRepository.generateProfileUid(),
        userId: user.id,
        organizationId,
        username: user?.username || getOrgUsernameFromEmail(user.email, orgAutoAcceptEmail)
      })),
      skipDuplicates: true
    });
  }
  static createMany({
    users,
    organizationId,
    orgAutoAcceptEmail
  }) {
    return index.prisma.profile.createMany({
      data: users.map((user) => ({
        uid: ProfileRepository.generateProfileUid(),
        userId: user.id,
        organizationId,
        username: user?.username || getOrgUsernameFromEmail(user.email, orgAutoAcceptEmail)
      })),
      skipDuplicates: true
    });
  }
  static delete({ userId, organizationId }) {
    return index.prisma.profile.deleteMany({
      where: { userId, organizationId }
    });
  }
  static deleteMany({ userIds }) {
    return index.prisma.profile.deleteMany({
      where: { userId: { in: userIds } }
    });
  }
  static async findByUserIdAndOrgId({
    userId,
    organizationId
  }) {
    if (!organizationId) {
      return null;
    }
    const profile = await index.prisma.profile.findUnique({
      where: {
        userId_organizationId: {
          userId,
          organizationId
        }
      },
      include: {
        organization: {
          select: organizationSelect
        },
        user: {
          select: userSelect$1
        }
      }
    });
    if (!profile) {
      return null;
    }
    const organization = getParsedTeam(profile.organization);
    return normalizeProfile({
      ...profile,
      organization: {
        ...organization,
        requestedSlug: organization.metadata?.requestedSlug ?? null,
        metadata: organization.metadata
      }
    });
  }
  static async findByOrgIdAndUsername({
    organizationId,
    username
  }) {
    const profile = await index.prisma.profile.findUnique({
      where: {
        username_organizationId: {
          username,
          organizationId
        }
      },
      include: {
        organization: {
          select: organizationSelect
        },
        user: {
          select: userSelect$1
        }
      }
    });
    return profile;
  }
  static async findByUpId(upId) {
    const lookupTarget = ProfileRepository.getLookupTarget(upId);
    log$1.debug("findByUpId", index.safeStringify({ upId, lookupTarget }));
    if (lookupTarget.type === 0) {
      const user2 = await this.findUserByid({ id: lookupTarget.id });
      if (!user2) {
        return null;
      }
      return {
        username: user2.username,
        upId: `usr-${user2.id}`,
        id: null,
        organizationId: null,
        organization: null,
        ...ProfileRepository.getInheritedDataFromUser({ user: user2 })
      };
    }
    const profile = await ProfileRepository.findById(lookupTarget.id);
    if (!profile) {
      return null;
    }
    const user = profile.user;
    if (profile.organization?.isPlatform && !user.isPlatformManaged) {
      return {
        ...this.buildPersonalProfileFromUser({ user }),
        ...ProfileRepository.getInheritedDataFromUser({ user })
      };
    }
    return {
      ...profile,
      ...ProfileRepository.getInheritedDataFromUser({ user })
    };
  }
  static async findById(id) {
    if (!id) {
      return null;
    }
    const profile = await index.prisma.profile.findUnique({
      where: {
        id
      },
      include: {
        user: {
          select: userSelect$1
        },
        movedFromUser: {
          select: {
            id: true
          }
        },
        organization: {
          select: {
            id: true,
            logoUrl: true,
            name: true,
            slug: true,
            metadata: true,
            bannerUrl: true,
            isPrivate: true,
            isPlatform: true,
            hideBranding: true,
            organizationSettings: {
              select: {
                lockEventTypeCreationForUsers: true,
                allowSEOIndexing: true
              }
            },
            members: {
              distinct: ["role"],
              select: membershipSelect,
              where: {
                accepted: true,
                // Filter out memberships that are not owned by the user
                user: { profiles: { some: { id } } }
              }
            }
          }
        }
      }
    });
    if (!profile) {
      return null;
    }
    return normalizeProfile(profile);
  }
  static async findManyByOrgSlugOrRequestedSlug({
    usernames,
    orgSlug
  }) {
    index.logger.debug("findManyByOrgSlugOrRequestedSlug", index.safeStringify({ usernames, orgSlug }));
    const profiles = await index.prisma.profile.findMany({
      where: {
        username: {
          in: usernames
        },
        organization: whereClauseForOrgWithSlugOrRequestedSlug(orgSlug)
      },
      include: {
        user: {
          select: userSelect$1
        },
        organization: {
          select: organizationWithSettingsSelect
        }
      }
    });
    return profiles.map(normalizeProfile);
  }
  static async findAllProfilesForUserIncludingMovedUser(user) {
    const profiles = await ProfileRepository.findManyForUser(user);
    if (!profiles.length) {
      return [
        ProfileRepository.buildPersonalProfileFromUser({
          user
        })
      ];
    }
    return profiles;
  }
  static async findManyForUsers(userIds) {
    const profiles = await index.prisma.profile.findMany({
      where: {
        userId: {
          in: userIds
        }
      },
      include: {
        organization: {
          select: organizationSelect
        }
      }
    });
    return profiles.map((profile) => {
      const parsedOrganization = getParsedTeam(profile.organization);
      return normalizeProfile({
        username: profile.username,
        id: profile.id,
        userId: profile.userId,
        uid: profile.uid,
        name: parsedOrganization.name,
        organizationId: profile.organizationId,
        organization: {
          ...parsedOrganization,
          requestedSlug: parsedOrganization.metadata?.requestedSlug ?? null,
          metadata: parsedOrganization.metadata
        }
      });
    });
  }
  static async findManyForUser(user) {
    const profiles = (await index.prisma.profile.findMany({
      where: {
        userId: user.id
      },
      include: {
        organization: {
          select: organizationSelect
        }
      }
    })).map((profile) => {
      return {
        ...profile,
        organization: getParsedTeam(profile.organization)
      };
    }).map((profile) => {
      return normalizeProfile({
        username: profile.username,
        id: profile.id,
        userId: profile.userId,
        uid: profile.uid,
        name: profile.organization.name,
        organizationId: profile.organizationId,
        organization: {
          ...profile.organization,
          requestedSlug: profile.organization.metadata?.requestedSlug ?? null,
          metadata: profile.organization.metadata
        }
      });
    });
    return profiles;
  }
  static async findManyForOrg({ organizationId }) {
    return await index.prisma.profile.findMany({
      where: {
        organizationId
      },
      include: {
        user: {
          select: userSelect$1
        },
        organization: {
          select: organizationSelect
        }
      }
    });
  }
  static async findByUserIdAndProfileId({ userId, profileId }) {
    const profile = await index.prisma.profile.findUnique({
      where: {
        userId,
        id: profileId
      },
      include: {
        organization: {
          select: organizationSelect
        },
        user: {
          select: userSelect$1
        }
      }
    });
    if (!profile) {
      return profile;
    }
    return normalizeProfile(profile);
  }
  static async findByUserIdAndOrgSlug({
    userId,
    organizationId
  }) {
    const profile = await index.prisma.profile.findUnique({
      where: {
        userId_organizationId: {
          userId,
          organizationId
        }
      },
      include: {
        organization: {
          select: organizationSelect
        }
      }
    });
    return profile;
  }
  /**
   * Personal profile should come from Profile table only
   */
  static buildPersonalProfileFromUser({
    user
  }) {
    return {
      id: null,
      upId: `usr-${user.id}`,
      username: user.username,
      organizationId: null,
      organization: null
    };
  }
  static _getPrismaWhereForProfilesOfOrg({ orgSlug }) {
    return {
      profiles: {
        ...orgSlug ? {
          some: {
            organization: {
              slug: orgSlug
            }
          }
        } : (
          // If it's not orgSlug we want to ensure that no profile is there. Having a profile means that the user is a member of some organization.
          {
            none: {}
          }
        )
      }
    };
  }
}
const normalizeProfile = (profile) => {
  return {
    ...profile,
    upId: profile.id.toString(),
    organization: getParsedTeam(profile.organization),
    // Make these ↓ props ISO strings so that they can be returned from getServerSideProps as is without any issues
    ...profile.createdAt ? { createdAt: profile.createdAt.toISOString() } : null,
    ...profile.updatedAt ? { updatedAt: profile.updatedAt.toISOString() } : null
  };
};
const defaultDayRange = {
  start: new Date((/* @__PURE__ */ new Date()).setUTCHours(9, 0, 0, 0)),
  end: new Date((/* @__PURE__ */ new Date()).setUTCHours(17, 0, 0, 0))
};
const DEFAULT_SCHEDULE = [
  [],
  [defaultDayRange],
  [defaultDayRange],
  [defaultDayRange],
  [defaultDayRange],
  [defaultDayRange],
  []
];
function getAvailabilityFromSchedule(schedule) {
  return schedule.reduce((availability, times, day) => {
    const addNewTime = (time) => ({
      days: [day],
      startTime: time.start,
      endTime: time.end
    });
    const filteredTimes = times.filter((time) => {
      let idx;
      if ((idx = availability.findIndex(
        (schedule2) => schedule2.startTime.toString() === time.start.toString() && schedule2.endTime.toString() === time.end.toString()
      )) !== -1) {
        availability[idx].days.push(day);
        return false;
      }
      return true;
    });
    filteredTimes.forEach((time) => {
      availability.push(addNewTime(time));
    });
    return availability;
  }, []);
}
const MINUTES_IN_DAY = 60 * 24;
const MINUTES_DAY_END = MINUTES_IN_DAY - 1;
const MINUTES_DAY_START = 0;
function getWorkingHours(relativeTimeUnit, availability) {
  if (!availability.length) {
    return [];
  }
  const utcOffset = relativeTimeUnit.utcOffset ?? (relativeTimeUnit.timeZone ? dayjs().tz(relativeTimeUnit.timeZone).utcOffset() : 0);
  const workingHours = availability.reduce((currentWorkingHours, schedule) => {
    if (!schedule.days.length)
      return currentWorkingHours;
    const startTime = dayjs.utc(schedule.startTime).get("hour") * 60 + dayjs.utc(schedule.startTime).get("minute") - utcOffset;
    const endTime = dayjs.utc(schedule.endTime).get("hour") * 60 + dayjs.utc(schedule.endTime).get("minute") - utcOffset;
    const sameDayStartTime = Math.max(MINUTES_DAY_START, Math.min(MINUTES_DAY_END, startTime));
    const sameDayEndTime = Math.max(MINUTES_DAY_START, Math.min(MINUTES_DAY_END, endTime));
    if (sameDayEndTime < sameDayStartTime) {
      return currentWorkingHours;
    }
    if (sameDayStartTime !== sameDayEndTime) {
      const newWorkingHours = {
        days: schedule.days,
        startTime: sameDayStartTime,
        endTime: sameDayEndTime
      };
      if (schedule.userId)
        newWorkingHours.userId = schedule.userId;
      currentWorkingHours.push(newWorkingHours);
    }
    if (startTime < MINUTES_DAY_START || endTime < MINUTES_DAY_START) {
      const newWorkingHours = {
        days: schedule.days.map((day) => day - 1 >= 0 ? day - 1 : 6),
        startTime: startTime + MINUTES_IN_DAY,
        endTime: Math.min(endTime + MINUTES_IN_DAY, MINUTES_DAY_END)
      };
      if (schedule.userId)
        newWorkingHours.userId = schedule.userId;
      currentWorkingHours.push(newWorkingHours);
    } else if (startTime > MINUTES_DAY_END || endTime > MINUTES_IN_DAY) {
      const newWorkingHours = {
        days: schedule.days.map((day) => (day + 1) % 7),
        startTime: Math.max(startTime - MINUTES_IN_DAY, MINUTES_DAY_START),
        endTime: endTime - MINUTES_IN_DAY
      };
      if (schedule.userId)
        newWorkingHours.userId = schedule.userId;
      currentWorkingHours.push(newWorkingHours);
    }
    return currentWorkingHours;
  }, []);
  workingHours.sort((a, b) => a.startTime - b.startTime);
  return workingHours;
}
function isInMemoryDelegationCredential({
  credentialId
}) {
  return typeof credentialId === "number" && credentialId < 0;
}
const isDelegationCredential = isInMemoryDelegationCredential;
const isInDbDelegationCredential = (credential) => {
  if (!credential.id)
    return false;
  return !!credential.delegationCredentialId && !isInMemoryDelegationCredential({ credentialId: credential.id });
};
const buildNonDelegationCredential = (credential) => {
  if (!credential || isInDbDelegationCredential(credential))
    return null;
  return {
    ...credential,
    delegatedTo: null,
    delegatedToId: null,
    delegationCredentialId: null
  };
};
const buildNonDelegationCredentials = (credentials) => {
  const nonDelegationCredentials = credentials.filter(
    (credential) => !isInDbDelegationCredential(credential)
  );
  return nonDelegationCredentials.map(buildNonDelegationCredential);
};
function withSelectedCalendars(user) {
  const { selectedCalendars, ...restUser } = user;
  return {
    ...restUser,
    allSelectedCalendars: selectedCalendars,
    userLevelSelectedCalendars: selectedCalendars.filter((calendar) => !calendar.eventTypeId)
  };
}
const log = index.logger.getSubLogger({ prefix: ["[repository/user]"] });
const teamSelect = {
  id: true,
  name: true,
  slug: true,
  metadata: true,
  logoUrl: true,
  organizationSettings: true,
  isOrganization: true,
  isPlatform: true
};
const userSelect = {
  id: true,
  username: true,
  name: true,
  email: true,
  emailVerified: true,
  bio: true,
  avatarUrl: true,
  timeZone: true,
  startTime: true,
  endTime: true,
  weekStart: true,
  bufferTime: true,
  hideBranding: true,
  theme: true,
  createdDate: true,
  trialEndsAt: true,
  completedOnboarding: true,
  locale: true,
  timeFormat: true,
  twoFactorSecret: true,
  twoFactorEnabled: true,
  backupCodes: true,
  identityProviderId: true,
  invitedTo: true,
  brandColor: true,
  darkBrandColor: true,
  allowDynamicBooking: true,
  allowSEOIndexing: true,
  receiveMonthlyDigestEmail: true,
  verified: true,
  disableImpersonation: true,
  locked: true,
  movedToProfileId: true,
  metadata: true,
  isPlatformManaged: true,
  lastActiveAt: true,
  identityProvider: true,
  teams: true
};
class UserRepository {
  constructor(prismaClient) {
    this.prismaClient = prismaClient;
  }
  async findTeamsByUserId({ userId }) {
    const teamMemberships = await this.prismaClient.membership.findMany({
      where: {
        userId
      },
      include: {
        team: {
          select: teamSelect
        }
      }
    });
    const acceptedTeamMemberships = teamMemberships.filter((membership) => membership.accepted);
    const pendingTeamMemberships = teamMemberships.filter((membership) => !membership.accepted);
    return {
      teams: acceptedTeamMemberships.map((membership) => membership.team),
      memberships: teamMemberships,
      acceptedTeamMemberships,
      pendingTeamMemberships
    };
  }
  async findOrganizations({ userId }) {
    const { acceptedTeamMemberships } = await this.findTeamsByUserId({
      userId
    });
    const acceptedOrgMemberships = acceptedTeamMemberships.filter(
      (membership) => membership.team.isOrganization
    );
    const organizations = acceptedOrgMemberships.map((membership) => membership.team);
    return {
      organizations
    };
  }
  /**
   * It is aware of the fact that a user can be part of multiple organizations.
   */
  async findUsersByUsername({ orgSlug, usernameList }) {
    const { where, profiles } = await this._getWhereClauseForFindingUsersByUsername({
      orgSlug,
      usernameList
    });
    return (await this.prismaClient.user.findMany({
      select: userSelect,
      where
    })).map((user) => {
      if (!profiles) {
        return {
          ...user,
          profile: ProfileRepository.buildPersonalProfileFromUser({ user })
        };
      }
      const profile = profiles.find((profile2) => profile2.user.id === user.id) ?? null;
      if (!profile) {
        log.error("Profile not found for user", index.safeStringify({ user, profiles }));
        throw new Error("Profile couldn't be found");
      }
      const { user: _1, ...profileWithoutUser } = profile;
      return {
        ...user,
        profile: profileWithoutUser
      };
    });
  }
  async findPlatformMembersByUsernames({ usernameList }) {
    return (await this.prismaClient.user.findMany({
      select: userSelect,
      where: {
        username: {
          in: usernameList
        },
        isPlatformManaged: false,
        profiles: {
          some: {
            organization: {
              isPlatform: true
            }
          }
        }
      }
    })).map((user) => {
      return {
        ...user,
        profile: ProfileRepository.buildPersonalProfileFromUser({ user })
      };
    });
  }
  async _getWhereClauseForFindingUsersByUsername({
    orgSlug,
    usernameList
  }) {
    const profiles = orgSlug ? (await ProfileRepository.findManyByOrgSlugOrRequestedSlug({
      orgSlug,
      usernames: usernameList
    })).map((profile) => ({
      ...profile,
      organization: getParsedTeam(profile.organization)
    })) : null;
    const where = profiles && profiles.length > 0 ? {
      // Get UserIds from profiles
      id: {
        in: profiles.map((profile) => profile.user.id)
      }
    } : {
      username: {
        in: usernameList
      },
      ...orgSlug ? {
        organization: whereClauseForOrgWithSlugOrRequestedSlug(orgSlug)
      } : {
        organization: null
      }
    };
    return { where, profiles };
  }
  async findByEmail({ email }) {
    const user = await this.prismaClient.user.findUnique({
      where: {
        email: email.toLowerCase()
      },
      select: userSelect
    });
    return user;
  }
  async findByEmailAndIncludeProfilesAndPassword({ email }) {
    const user = await this.prismaClient.user.findUnique({
      where: {
        email: email.toLowerCase()
      },
      select: {
        locked: true,
        role: true,
        id: true,
        username: true,
        name: true,
        email: true,
        metadata: true,
        identityProvider: true,
        password: true,
        twoFactorEnabled: true,
        twoFactorSecret: true,
        backupCodes: true,
        locale: true,
        teams: {
          include: {
            team: {
              select: teamSelect
            }
          }
        },
        createdDate: true
      }
    });
    if (!user) {
      return null;
    }
    const allProfiles = await ProfileRepository.findAllProfilesForUserIncludingMovedUser(user);
    return {
      ...user,
      allProfiles
    };
  }
  async findById({ id }) {
    const user = await this.prismaClient.user.findUnique({
      where: {
        id
      },
      select: userSelect
    });
    if (!user) {
      return null;
    }
    return {
      ...user,
      metadata: index.userMetadata.parse(user.metadata)
    };
  }
  async findByIds({ ids }) {
    return this.prismaClient.user.findMany({
      where: {
        id: {
          in: ids
        }
      },
      select: userSelect
    });
  }
  async findByIdOrThrow({ id }) {
    const user = await this.findById({ id });
    if (!user) {
      throw new Error(`User with id ${id} not found`);
    }
    return user;
  }
  async findManyByOrganization({ organizationId }) {
    const profiles = await ProfileRepository.findManyForOrg({ organizationId });
    return profiles.map((profile) => profile.user);
  }
  isAMemberOfOrganization({
    user,
    organizationId
  }) {
    return user.profiles.some((profile) => profile.organizationId === organizationId);
  }
  async findIfAMemberOfSomeOrganization({ user }) {
    return !!(await ProfileRepository.findManyForUser({
      id: user.id
    })).length;
  }
  isMigratedToOrganization({
    user
  }) {
    return !!user.metadata?.migratedToOrgFrom;
  }
  async isMovedToAProfile({ user }) {
    return !!user.movedToProfileId;
  }
  async enrichUserWithTheProfile({
    user,
    upId
  }) {
    const profile = await ProfileRepository.findByUpId(upId);
    if (!profile) {
      return {
        ...user,
        profile: ProfileRepository.buildPersonalProfileFromUser({ user })
      };
    }
    return {
      ...user,
      profile
    };
  }
  /**
   * Use this method instead of `enrichUserWithTheProfile` if you don't directly have the profileId.
   * It can happen in following cases:
   * 1. While dealing with a User that hasn't been added to any organization yet and thus have no Profile entries.
   * 2. While dealing with a User that has been moved to a Profile i.e. he was invited to an organization when he was an existing user.
   * 3. We haven't added profileId to all the entities, so they aren't aware of which profile they belong to. So, we still mostly use this function to enrich the user with its profile.
   */
  async enrichUserWithItsProfile({
    user
  }) {
    const profiles = await ProfileRepository.findManyForUser({ id: user.id });
    if (profiles.length) {
      const profile = profiles[0];
      if (profile?.organization?.isPlatform) {
        return {
          ...user,
          nonProfileUsername: user.username,
          profile: ProfileRepository.buildPersonalProfileFromUser({ user })
        };
      }
      return {
        ...user,
        username: profile.username,
        nonProfileUsername: user.username,
        profile
      };
    }
    return {
      ...user,
      nonProfileUsername: user.username,
      profile: ProfileRepository.buildPersonalProfileFromUser({ user })
    };
  }
  async enrichUsersWithTheirProfiles(users) {
    if (users.length === 0)
      return [];
    const userIds = users.map((user) => user.id);
    const profiles = await ProfileRepository.findManyForUsers(userIds);
    const profileMap = /* @__PURE__ */ new Map();
    profiles.forEach((profile) => {
      if (!profileMap.has(profile.userId)) {
        profileMap.set(profile.userId, []);
      }
      profileMap.get(profile.userId).push(profile);
    });
    const personalProfileMap = /* @__PURE__ */ new Map();
    users.forEach((user) => {
      personalProfileMap.set(user.id, ProfileRepository.buildPersonalProfileFromUser({ user }));
    });
    return users.map((user) => {
      const userProfiles = profileMap.get(user.id) || [];
      if (userProfiles.length > 0) {
        const profile = userProfiles[0];
        if (profile?.organization?.isPlatform) {
          return {
            ...user,
            nonProfileUsername: user.username,
            profile: personalProfileMap.get(user.id)
          };
        }
        return {
          ...user,
          username: profile.username,
          nonProfileUsername: user.username,
          profile
        };
      }
      return {
        ...user,
        nonProfileUsername: user.username,
        profile: personalProfileMap.get(user.id)
      };
    });
  }
  enrichUserWithItsProfileBuiltFromUser({
    user
  }) {
    return {
      ...user,
      nonProfileUsername: user.username,
      profile: ProfileRepository.buildPersonalProfileFromUser({ user })
    };
  }
  async enrichEntityWithProfile(entity) {
    if ("profile" in entity) {
      const { profile, ...entityWithoutProfile } = entity;
      const { organization, ...profileWithoutOrganization } = profile || {};
      const parsedOrg = organization ? getParsedTeam(organization) : null;
      const ret = {
        ...entityWithoutProfile,
        profile: {
          ...profileWithoutOrganization,
          ...parsedOrg ? {
            organization: parsedOrg
          } : {
            organization: null
          }
        }
      };
      return ret;
    } else {
      const profiles = await ProfileRepository.findManyForUser(entity.user);
      if (!profiles.length) {
        return {
          ...entity,
          profile: ProfileRepository.buildPersonalProfileFromUser({ user: entity.user })
        };
      } else {
        return {
          ...entity,
          profile: profiles[0]
        };
      }
    }
  }
  async updateWhereId({
    whereId,
    data
  }) {
    return this.prismaClient.user.update({
      where: {
        id: whereId
      },
      data: {
        movedToProfile: data.movedToProfileId ? {
          connect: {
            id: data.movedToProfileId
          }
        } : void 0
      }
    });
  }
  async create(data) {
    const organizationIdValue = data.organizationId;
    const { email, username, creationSource, locked, hashedPassword, ...rest } = data;
    index.logger.info("create user", { email, username, organizationIdValue, locked });
    const t = await index.getTranslation("en", "common");
    const availability = getAvailabilityFromSchedule(DEFAULT_SCHEDULE);
    const user = await this.prismaClient.user.create({
      data: {
        username,
        email,
        ...hashedPassword && { password: { create: { hash: hashedPassword } } },
        // Default schedule
        schedules: {
          create: {
            name: t("default_schedule_name"),
            availability: {
              createMany: {
                data: availability.map((schedule) => ({
                  days: schedule.days,
                  startTime: schedule.startTime,
                  endTime: schedule.endTime
                }))
              }
            }
          }
        },
        creationSource,
        locked,
        ...organizationIdValue ? {
          organizationId: organizationIdValue,
          profiles: {
            create: {
              username,
              organizationId: organizationIdValue,
              uid: ProfileRepository.generateProfileUid()
            }
          }
        } : {},
        ...rest
      }
    });
    return user;
  }
  async getUserAdminTeams({ userId }) {
    return await this.prismaClient.user.findUnique({
      where: {
        id: userId
      },
      select: {
        id: true,
        avatarUrl: true,
        name: true,
        username: true,
        teams: {
          where: {
            accepted: true,
            OR: [
              {
                role: { in: [index.MembershipRole.ADMIN, index.MembershipRole.OWNER] }
              },
              {
                team: {
                  parent: {
                    members: {
                      some: {
                        id: userId,
                        role: { in: [index.MembershipRole.ADMIN, index.MembershipRole.OWNER] }
                      }
                    }
                  }
                }
              }
            ]
          },
          select: {
            team: {
              select: {
                id: true,
                name: true,
                logoUrl: true,
                isOrganization: true,
                parent: {
                  select: {
                    logoUrl: true,
                    name: true,
                    id: true
                  }
                }
              }
            }
          }
        }
      }
    });
  }
  async isAdminOfTeamOrParentOrg({ userId, teamId }) {
    const membershipQuery = {
      members: {
        some: {
          userId,
          role: { in: [index.MembershipRole.ADMIN, index.MembershipRole.OWNER] }
        }
      }
    };
    const teams = await this.prismaClient.team.findMany({
      where: {
        id: teamId,
        OR: [
          membershipQuery,
          {
            parent: { ...membershipQuery }
          }
        ]
      },
      select: {
        id: true
      }
    });
    return !!teams.length;
  }
  async isAdminOrOwnerOfTeam({ userId, teamId }) {
    const isAdminOrOwnerOfTeam = await this.prismaClient.membership.findUnique({
      where: {
        userId_teamId: {
          userId,
          teamId
        },
        role: { in: [index.MembershipRole.ADMIN, index.MembershipRole.OWNER] },
        accepted: true
      },
      select: {
        id: true
      }
    });
    return !!isAdminOrOwnerOfTeam;
  }
  async getTimeZoneAndDefaultScheduleId({ userId }) {
    return await this.prismaClient.user.findUnique({
      where: {
        id: userId
      },
      select: {
        timeZone: true,
        defaultScheduleId: true
      }
    });
  }
  async adminFindById(userId) {
    return await this.prismaClient.user.findUniqueOrThrow({
      where: {
        id: userId
      }
    });
  }
  async findUserTeams({ id }) {
    const user = await this.prismaClient.user.findUnique({
      where: {
        id
      },
      select: {
        completedOnboarding: true,
        teams: {
          select: {
            accepted: true,
            team: {
              select: {
                id: true,
                name: true,
                logoUrl: true
              }
            }
          }
        }
      }
    });
    if (!user) {
      return null;
    }
    return user;
  }
  async updateAvatar({ id, avatarUrl }) {
    await this.prismaClient.user.updateMany({
      where: {
        id,
        avatarUrl: {
          equals: null
        }
      },
      data: {
        avatarUrl
      }
    });
  }
  async findUserWithCredentials({ id }) {
    const user = await this.prismaClient.user.findUnique({
      where: {
        id
      },
      select: {
        credentials: {
          select: index.credentialForCalendarServiceSelect
        },
        timeZone: true,
        id: true,
        selectedCalendars: true
      }
    });
    if (!user) {
      return null;
    }
    const { credentials, ...userWithSelectedCalendars } = withSelectedCalendars(user);
    return {
      ...userWithSelectedCalendars,
      credentials: buildNonDelegationCredentials(credentials)
    };
  }
  async findUnlockedUserForSession({ userId }) {
    const user = await this.prismaClient.user.findUnique({
      where: {
        id: userId,
        // Locked users can't login
        locked: false
      },
      select: {
        id: true,
        username: true,
        name: true,
        email: true,
        emailVerified: true,
        bio: true,
        avatarUrl: true,
        timeZone: true,
        weekStart: true,
        startTime: true,
        endTime: true,
        defaultScheduleId: true,
        bufferTime: true,
        theme: true,
        appTheme: true,
        createdDate: true,
        hideBranding: true,
        twoFactorEnabled: true,
        disableImpersonation: true,
        identityProvider: true,
        identityProviderId: true,
        brandColor: true,
        darkBrandColor: true,
        movedToProfileId: true,
        selectedCalendars: {
          select: {
            eventTypeId: true,
            externalId: true,
            integration: true,
            updatedAt: true,
            googleChannelId: true
          }
        },
        completedOnboarding: true,
        destinationCalendar: true,
        locale: true,
        timeFormat: true,
        trialEndsAt: true,
        metadata: true,
        role: true,
        allowDynamicBooking: true,
        allowSEOIndexing: true,
        receiveMonthlyDigestEmail: true,
        profiles: true
      }
    });
    if (!user) {
      return null;
    }
    return withSelectedCalendars(user);
  }
  async getUserStats({ userId }) {
    const user = await this.prismaClient.user.findUnique({
      where: {
        id: userId
      },
      select: {
        _count: {
          select: {
            bookings: true,
            // We only need user level selected calendars
            selectedCalendars: {
              where: {
                eventTypeId: null
              }
            },
            teams: true,
            eventTypes: true
          }
        },
        teams: {
          select: {
            team: {
              select: {
                eventTypes: {
                  select: {
                    id: true
                  }
                }
              }
            }
          }
        }
      }
    });
    if (!user) {
      return null;
    }
    const { _count, ...restUser } = user;
    const { selectedCalendars, ...restCount } = _count;
    return {
      ...restUser,
      _count: {
        ...restCount,
        userLevelSelectedCalendars: selectedCalendars
      }
    };
  }
  async findManyByIdsIncludeDestinationAndSelectedCalendars({ ids }) {
    const users = await this.prismaClient.user.findMany({
      where: { id: { in: ids } },
      include: {
        selectedCalendars: true,
        destinationCalendar: true
      }
    });
    return users.map(withSelectedCalendars);
  }
  async updateStripeCustomerId({
    id,
    stripeCustomerId,
    existingMetadata
  }) {
    return this.prismaClient.user.update({
      where: { id },
      data: { metadata: { ...existingMetadata, stripeCustomerId } }
    });
  }
  async updateWhitelistWorkflows({ id, whitelistWorkflows }) {
    return this.prismaClient.user.update({
      where: { id },
      data: { whitelistWorkflows }
    });
  }
  async findManyUsersForDynamicEventType({
    currentOrgDomain,
    usernameList
  }) {
    const { where } = await this._getWhereClauseForFindingUsersByUsername({
      orgSlug: currentOrgDomain,
      usernameList
    });
    return this.prismaClient.user.findMany({
      where,
      select: {
        allowDynamicBooking: true,
        ...index.availabilityUserSelect,
        credentials: {
          select: index.credentialForCalendarServiceSelect
        }
      }
    });
  }
  async findUsersWithLastBooking({ userIds, eventTypeId }) {
    return this.prismaClient.user.findMany({
      where: {
        id: {
          in: userIds
        }
      },
      select: {
        id: true,
        bookings: {
          select: {
            createdAt: true
          },
          where: {
            eventTypeId,
            status: index.BookingStatus.ACCEPTED,
            attendees: {
              some: {
                noShow: false
              }
            },
            OR: [
              {
                noShowHost: false
              },
              {
                noShowHost: null
              }
            ]
          },
          orderBy: {
            createdAt: "desc"
          },
          take: 1
        }
      }
    });
  }
}
exports.DEFAULT_SCHEDULE = DEFAULT_SCHEDULE;
exports.LookupTarget = LookupTarget;
exports.ProfileRepository = ProfileRepository;
exports.UserRepository = UserRepository;
exports.buildNonDelegationCredential = buildNonDelegationCredential;
exports.buildNonDelegationCredentials = buildNonDelegationCredentials;
exports.getAvailabilityFromSchedule = getAvailabilityFromSchedule;
exports.getOrgDomainConfig = getOrgDomainConfig;
exports.getOrgFullOrigin = getOrgFullOrigin;
exports.getOrgUsernameFromEmail = getOrgUsernameFromEmail;
exports.getParsedTeam = getParsedTeam;
exports.getSlugOrRequestedSlug = getSlugOrRequestedSlug;
exports.getWorkingHours = getWorkingHours;
exports.isDelegationCredential = isDelegationCredential;
exports.isInMemoryDelegationCredential = isInMemoryDelegationCredential;
exports.orgDomainConfig = orgDomainConfig;
exports.subdomainSuffix = subdomainSuffix;
exports.whereClauseForOrgWithSlugOrRequestedSlug = whereClauseForOrgWithSlugOrRequestedSlug;
exports.withSelectedCalendars = withSelectedCalendars;
