import { createHash } from "node:crypto";
import process from "node:process";
import dailyMeta from "@calcom/app-store/dailyvideo/_metadata";
import googleMeetMeta from "@calcom/app-store/googlevideo/_metadata";
import zoomMeta from "@calcom/app-store/zoomvideo/_metadata";
import dayjs from "@calcom/dayjs";
import { hashPassword } from "@calcom/lib/auth/hashPassword";
import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "@calcom/lib/availability";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { prisma } from "@calcom/prisma";
import type { Membership, Team, User, UserPermissionRole } from "@calcom/prisma/client";
import { Prisma } from "@calcom/prisma/client";
import { BookingStatus, MembershipRole, RedirectType, SchedulingType } from "@calcom/prisma/enums";
import type { Ensure } from "@calcom/types/utils";
import { uuid } from "short-uuid";
import type z from "zod";
import type { teamMetadataSchema } from "../packages/prisma/zod-utils";
import mainAppStore from "./seed-app-store";
import mainHugeEventTypesSeed from "./seed-huge-event-types";
import { createOAuthClientForUser, createUserAndEventType } from "./seed-utils";

function hashAPIKey(apiKey: string): string {
  return createHash("sha256").update(apiKey).digest("hex");
}

async function createTeamAndAddUsers(
  teamInput: Prisma.TeamCreateInput,
  users: { id: number; username: string; role?: MembershipRole }[] = []
) {
  const checkUnpublishedTeam = async (slug: string) => {
    return await prisma.team.findFirst({
      where: {
        metadata: {
          path: ["requestedSlug"],
          equals: slug,
        },
      },
    });
  };
  const createTeam = async (team: Prisma.TeamCreateInput) => {
    try {
      const requestedSlug = (team.metadata as z.infer<typeof teamMetadataSchema>)?.requestedSlug;
      if (requestedSlug) {
        const unpublishedTeam = await checkUnpublishedTeam(requestedSlug);
        if (unpublishedTeam) {
          throw Error("Unique constraint failed on the fields");
        }
      }
      return await prisma.team.create({
        data: {
          ...team,
        },
        include: {
          eventTypes: true,
        },
      });
    } catch (_err) {
      if (_err instanceof Error && _err.message.indexOf("Unique constraint failed on the fields") !== -1) {
        console.log(`Team '${team.name}' already exists, skipping.`);
        return;
      }
      throw _err;
    }
  };

  const team = await createTeam(teamInput);
  if (!team) {
    return;
  }

  console.log(
    `🏢 Created team '${teamInput.name}' - ${process.env.NEXT_PUBLIC_WEBAPP_URL}/team/${team.slug}`
  );

  for (const user of users) {
    const { role = MembershipRole.OWNER, id, username } = user;
    await prisma.membership.create({
      data: {
        createdAt: new Date(),
        teamId: team.id,
        userId: id,
        role: role,
        accepted: true,
      },
    });
    console.log(`\t👤 Added '${teamInput.name}' membership for '${username}' with role '${role}'`);
  }

  // Connect users and create hosts for team event types
  for (const eventType of team.eventTypes) {
    const isCollective = eventType.schedulingType === SchedulingType.COLLECTIVE;
    await prisma.eventType.update({
      where: {
        id: eventType.id,
      },
      data: {
        users: {
          connect: users.map((user) => ({ id: user.id })),
        },
        hosts: {
          create: users.map((user) => ({
            userId: user.id,
            isFixed: isCollective,
          })),
        },
      },
    });
  }

  return team;
}

async function createOrganizationAndAddMembersAndTeams({
  org: { orgData, members: orgMembers },
  teams,
  usersOutsideOrg,
}: {
  org: {
    orgData: Ensure<Partial<Prisma.TeamCreateInput>, "name" | "slug"> & {
      organizationSettings: Prisma.OrganizationSettingsCreateWithoutOrganizationInput;
    };
    members: {
      memberData: Ensure<Partial<Prisma.UserCreateInput>, "username" | "name" | "email" | "password">;
      orgMembership: Partial<Membership>;
      orgProfile: {
        username: string;
      };
      inTeams: { slug: string; role: MembershipRole }[];
    }[];
  };
  teams: {
    teamData: Omit<Ensure<Partial<Prisma.TeamCreateInput>, "name" | "slug">, "members">;
    nonOrgMembers: Ensure<Partial<Prisma.UserCreateInput>, "username" | "name" | "email" | "password">[];
  }[];
  usersOutsideOrg: {
    name: string;
    username: string;
    email: string;
  }[];
}) {
  console.log(`\n🏢 Creating organization "${orgData.name}"`);

  const existingTeam = await prisma.team.findFirst({
    where: {
      slug: orgData.slug,
      parentId: null,
    },
  });

  if (existingTeam) {
    console.log(`Organization with slug '${orgData.slug}' already exists, ensuring settings are up to date.`);
    await prisma.organizationSettings.upsert({
      where: { organizationId: existingTeam.id },
      update: { ...orgData.organizationSettings },
      create: { organizationId: existingTeam.id, ...orgData.organizationSettings },
    });
    return;
  }

  const orgMembersInDb: (User & {
    inTeams: { slug: string; role: MembershipRole }[];
    orgMembership: Partial<Membership>;
    orgProfile: {
      username: string;
    };
  })[] = [];

  const batchSize = 50;
  for (let i = 0; i < orgMembers.length; i += batchSize) {
    const batch = orgMembers.slice(i, i + batchSize);

    const batchResults = await Promise.all(
      batch.map(async (member) => {
        try {
          const newUser = await createUserAndEventType({
            user: {
              ...member.memberData,
              theme:
                member.memberData.theme === "dark" || member.memberData.theme === "light"
                  ? member.memberData.theme
                  : undefined,
              password: member.memberData.password.create?.hash ?? "",
            },
            eventTypes: [
              {
                title: "30min",
                slug: "30min",
                length: 30,
                _bookings: [
                  {
                    uid: uuid(),
                    title: "30min",
                    startTime: dayjs().add(1, "day").toDate(),
                    endTime: dayjs().add(1, "day").add(30, "minutes").toDate(),
                  },
                ],
              },
            ],
          });

          // Create temp org redirect with upsert to handle duplicates
          await prisma.tempOrgRedirect.upsert({
            where: {
              from_type_fromOrgId: {
                from: member.memberData.username,
                type: RedirectType.User,
                fromOrgId: 0,
              },
            },
            update: {
              toUrl: `${WEBAPP_URL}/${member.orgProfile.username}`,
            },
            create: {
              fromOrgId: 0,
              type: RedirectType.User,
              from: member.memberData.username,
              toUrl: `${WEBAPP_URL}/${member.orgProfile.username}`,
            },
          });

          return {
            ...newUser,
            inTeams: member.inTeams,
            orgMembership: member.orgMembership,
            orgProfile: member.orgProfile,
          };
        } catch (e) {
          if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2002") {
            console.log("Organization member already seeded, skipping");
            const existingUser = await prisma.user.findUnique({
              where: {
                email_username: {
                  email: member.memberData.email,
                  username: member.memberData.username,
                },
              },
            });
            if (!existingUser) throw e;
            return {
              ...existingUser,
              inTeams: member.inTeams,
              orgMembership: member.orgMembership,
              orgProfile: member.orgProfile,
            };
          }
          throw e;
        }
      })
    );

    orgMembersInDb.push(...batchResults.filter((r): r is NonNullable<typeof r> => r !== null));
  }

  await Promise.all(
    usersOutsideOrg.map(async (user) => {
      await prisma.user.upsert({
        where: { email_username: { email: user.email, username: user.username } },
        update: {},
        create: {
          username: user.username,
          name: user.name,
          email: user.email,
          emailVerified: new Date(),
          password: {
            create: {
              hash: await hashPassword(user.username),
            },
          },
        },
      });
    })
  );

  const { organizationSettings, ...restOrgData } = orgData;

  // Step 1: Create organization (team) with just metadata and organizationSettings
  const orgInDb = await prisma.team.create({
    data: {
      ...restOrgData,
      metadata: {
        ...(orgData.metadata && typeof orgData.metadata === "object" ? orgData.metadata : {}),
        isOrganization: true,
      },
      organizationSettings: {
        create: {
          ...organizationSettings,
        },
      },
    },
    select: {
      id: true,
    },
  });

  // Step 2: Create org profiles in batches to avoid large transactions
  const profileBatchSize = 50;
  for (let i = 0; i < orgMembersInDb.length; i += profileBatchSize) {
    const batch = orgMembersInDb.slice(i, i + profileBatchSize);
    await Promise.all(
      batch.map((member) =>
        prisma.profile.create({
          data: {
            uid: uuid(),
            username: member.orgProfile.username,
            organizationId: orgInDb.id,
            userId: member.id,
            movedFromUser: {
              connect: {
                id: member.id,
              },
            },
          },
        })
      )
    );
  }

  // Step 3: Create memberships using createMany for better performance
  const membershipBatchSize = 100;
  for (let i = 0; i < orgMembersInDb.length; i += membershipBatchSize) {
    const batch = orgMembersInDb.slice(i, i + membershipBatchSize);
    await prisma.membership.createMany({
      data: batch.map((member) => ({
        teamId: orgInDb.id,
        userId: member.id,
        role: member.orgMembership.role || "MEMBER",
        accepted: member.orgMembership.accepted ?? false,
      })),
    });
  }

  // Step 4: Fetch created profiles to rebuild orgMembersInDBWithProfileId
  const createdProfiles = await prisma.profile.findMany({
    where: {
      organizationId: orgInDb.id,
      userId: { in: orgMembersInDb.map((m) => m.id) },
    },
    select: {
      id: true,
      userId: true,
    },
  });

  const orgMembersInDBWithProfileId = orgMembersInDb.map((member) => ({
    ...member,
    profile: {
      ...member.orgProfile,
      id: createdProfiles.find((p) => p.userId === member.id)?.id,
    },
  }));

  // For each member create one event
  for (const member of orgMembersInDBWithProfileId) {
    await prisma.eventType.create({
      data: {
        title: `${member.name} Event`,
        slug: `${member.username}-event`,
        length: 15,
        owner: {
          connect: {
            id: member.id,
          },
        },
        profile: {
          connect: {
            id: member.profile.id,
          },
        },
        users: {
          connect: {
            id: member.id,
          },
        },
      },
    });

    // Create schedule for every member
    await prisma.schedule.create({
      data: {
        name: "Working Hours",
        userId: member.id,
        availability: {
          create: {
            days: [1, 2, 3, 4, 5],
            startTime: "1970-01-01T09:00:00.000Z",
            endTime: "1970-01-01T17:00:00.000Z",
          },
        },
      },
    });
  }

  const organizationTeams: Team[] = [];

  // Create all the teams in the organization
  for (let teamIndex = 0; teamIndex < teams.length; teamIndex++) {
    const nonOrgMembers: User[] = [];
    const team = teams[teamIndex];
    for (const nonOrgMember of team.nonOrgMembers) {
      nonOrgMembers.push(
        await prisma.user.create({
          data: {
            ...nonOrgMember,
            password: {
              create: {
                hash: await hashPassword(nonOrgMember.username),
              },
            },
            emailVerified: new Date(),
          },
        })
      );
    }
    organizationTeams.push(
      await prisma.team.create({
        data: {
          ...team.teamData,
          parent: {
            connect: {
              id: orgInDb.id,
            },
          },
          metadata: team.teamData.metadata || {},
          members: {
            create: nonOrgMembers.map((member) => ({
              user: {
                connect: {
                  id: member.id,
                },
              },
              role: "MEMBER",
              accepted: true,
            })),
          },
        },
      })
    );

    const ownerForEvent = orgMembersInDBWithProfileId[0];
    if (!ownerForEvent) {
      console.log(
        `Warning: No organization members with profiles found for creating team event, skipping event creation for team ${team.teamData.slug}`
      );
      continue;
    }
    // Create event for each team
    await prisma.eventType.create({
      data: {
        title: `${team.teamData.name} Event 1`,
        slug: `${team.teamData.slug}-event-1`,
        schedulingType: SchedulingType.ROUND_ROBIN,
        length: 15,
        team: {
          connect: {
            id: organizationTeams[teamIndex].id,
          },
        },
        owner: {
          connect: {
            id: ownerForEvent.id,
          },
        },
        profile: {
          connect: {
            id: ownerForEvent.profile.id,
          },
        },
        users: {
          connect: {
            id: ownerForEvent.id,
          },
        },
      },
    });
  }

  // Create memberships for all the organization members with the respective teams
  for (const member of orgMembersInDBWithProfileId) {
    for (const { slug: teamSlug, role } of member.inTeams) {
      const team = organizationTeams.find((t) => t.slug === teamSlug);
      if (!team) {
        throw Error(`Team with slug ${teamSlug} not found`);
      }
      await prisma.membership.create({
        data: {
          createdAt: new Date(),
          teamId: team.id,
          userId: member.id,
          role: role,
          accepted: true,
        },
      });
    }
  }
}

async function seedApiKey(userId: number, apiKey: string) {
  const hashedKey = hashAPIKey(apiKey);

  const existingKey = await prisma.apiKey.findFirst({
    where: { hashedKey },
  });

  if (existingKey) {
    console.log(`🔑 API Key already exists, skipping.`);
    return;
  }

  await prisma.apiKey.create({
    data: {
      userId,
      hashedKey,
      note: "Seeded API Key for local development",
      expiresAt: null,
    },
  });

  const apiKeyPrefix = process.env.API_KEY_PREFIX ?? "cal_";
  console.log(`🔑 Created seeded API Key: ${apiKeyPrefix}${apiKey}`);
}

async function ensureAcmeOwnerHasApiKeySeeded() {
  const owner1AcmeUser = await prisma.user.findFirst({
    where: { email: "owner1-acme@example.com" },
  });
  if (owner1AcmeUser) {
    await seedApiKey(owner1AcmeUser.id, "0123456789abcdef0123456789abcdef");
  }
}

async function seedPerHostLocationsInAcmeOrg() {
  const acmeOrg = await prisma.team.findFirst({
    where: { slug: "acme", parentId: null },
    select: { id: true },
  });
  if (!acmeOrg) {
    console.log("Acme org not found, skipping per-host location seeding.");
    return;
  }

  const acmeTeam = await prisma.team.findFirst({
    where: { slug: "team1", parentId: acmeOrg.id },
    select: { id: true },
  });
  if (!acmeTeam) {
    console.log("Acme Team 1 not found, skipping per-host location seeding.");
    return;
  }

  const existingEvent = await prisma.eventType.findFirst({
    where: { slug: "per-host-location-event", teamId: acmeTeam.id },
  });
  if (existingEvent) {
    console.log("Per-host location event already seeded in Acme, skipping.");
    return;
  }

  const owner = await prisma.user.findFirst({
    where: { email: "owner1-acme@example.com" },
    select: { id: true },
  });
  const member0 = await prisma.user.findFirst({
    where: { email: "member0-acme@example.com" },
    select: { id: true },
  });
  const member2 = await prisma.user.findFirst({
    where: { email: "member2-acme@example.com" },
    select: { id: true },
  });
  if (!owner || !member0 || !member2) {
    console.log("Required Acme members not found, skipping per-host location seeding.");
    return;
  }

  const hosts = [owner, member0, member2];

  const ownerProfile = await prisma.profile.findFirst({
    where: { userId: owner.id, organizationId: acmeOrg.id },
    select: { id: true },
  });

  const eventType = await prisma.eventType.create({
    data: {
      title: "Per Host Location Event",
      slug: "per-host-location-event",
      length: 30,
      schedulingType: SchedulingType.ROUND_ROBIN,
      enablePerHostLocations: true,
      team: { connect: { id: acmeTeam.id } },
      owner: { connect: { id: owner.id } },
      ...(ownerProfile ? { profile: { connect: { id: ownerProfile.id } } } : {}),
      users: { connect: hosts.map((h) => ({ id: h.id })) },
      hosts: {
        create: hosts.map((h) => ({
          userId: h.id,
          isFixed: false,
        })),
      },
    },
  });

  await prisma.hostLocation.create({
    data: {
      userId: owner.id,
      eventTypeId: eventType.id,
      type: "integrations:daily",
    },
  });

  await prisma.hostLocation.create({
    data: {
      userId: member0.id,
      eventTypeId: eventType.id,
      type: "link",
      link: "https://example.com/meet",
    },
  });

  await prisma.hostLocation.create({
    data: {
      userId: member2.id,
      eventTypeId: eventType.id,
      type: "userPhone",
      phoneNumber: "+1234567890",
    },
  });

  console.log(
    `Seeded per-host locations in Acme Org for event "${eventType.slug}" (id=${eventType.id}): ` +
      `owner1->Cal Video, member0->Link, member2->Phone`
  );
}

async function main() {
  await createUserAndEventType({
    user: {
      email: "delete-me@example.com",
      password: "delete-me",
      username: "delete-me",
      name: "delete-me",
    },
  });

  await createUserAndEventType({
    user: {
      email: "onboarding@example.com",
      password: "onboarding",
      username: "onboarding",
      name: "onboarding",
      completedOnboarding: false,
    },
  });

  await createUserAndEventType({
    user: {
      email: "free-first-hidden@example.com",
      password: "free-first-hidden",
      username: "free-first-hidden",
      name: "Free First Hidden Example",
    },
    eventTypes: [
      {
        title: "30min",
        slug: "30min",
        length: 30,
        hidden: true,
      },
      {
        title: "60min",
        slug: "60min",
        length: 30,
      },
    ],
  });

  await createUserAndEventType({
    user: {
      email: "pro@example.com",
      name: "Pro Example",
      password: "pro",
      username: "pro",
      theme: "light",
    },
    eventTypes: [
      {
        title: "30min",
        slug: "30min",
        length: 30,
        _bookings: [
          {
            uid: uuid(),
            title: "30min",
            startTime: dayjs().add(1, "day").toDate(),
            endTime: dayjs().add(1, "day").add(30, "minutes").toDate(),
          },
          {
            uid: uuid(),
            title: "30min",
            startTime: dayjs().add(2, "day").toDate(),
            endTime: dayjs().add(2, "day").add(30, "minutes").toDate(),
            status: BookingStatus.PENDING,
          },
          {
            // hardcode UID so that we can easily test rescheduling in embed
            uid: "qm3kwt3aTnVD7vmP9tiT2f",
            title: "30min Seeded Booking",
            startTime: dayjs().add(3, "day").toDate(),
            endTime: dayjs().add(3, "day").add(30, "minutes").toDate(),
            status: BookingStatus.PENDING,
          },
        ],
      },
      {
        title: "60min",
        slug: "60min",
        length: 60,
      },
      {
        title: "Multiple duration",
        slug: "multiple-duration",
        length: 75,
        metadata: {
          multipleDuration: [30, 75, 90],
        },
      },
      {
        title: "paid",
        slug: "paid",
        length: 60,
        price: 100,
      },
      {
        title: "In person meeting",
        slug: "in-person",
        length: 60,
        locations: [{ type: "inPerson", address: "London" }],
      },
      {
        title: "Zoom Event",
        slug: "zoom",
        length: 60,
        locations: [{ type: zoomMeta.appData?.location?.type }],
      },
      {
        title: "Daily Event",
        slug: "daily",
        length: 60,
        locations: [{ type: dailyMeta.appData?.location?.type }],
      },
      {
        title: "Google Meet",
        slug: "google-meet",
        length: 60,
        locations: [{ type: googleMeetMeta.appData?.location?.type }],
      },
      {
        title: "Yoga class",
        slug: "yoga-class",
        length: 30,
        recurringEvent: { freq: 2, count: 12, interval: 1 },
        _bookings: [
          {
            uid: uuid(),
            title: "Yoga class",
            recurringEventId: Buffer.from("yoga-class").toString("base64"),
            startTime: dayjs().add(1, "day").toDate(),
            endTime: dayjs().add(1, "day").add(30, "minutes").toDate(),
            status: BookingStatus.ACCEPTED,
          },
          {
            uid: uuid(),
            title: "Yoga class",
            recurringEventId: Buffer.from("yoga-class").toString("base64"),
            startTime: dayjs().add(1, "day").add(1, "week").toDate(),
            endTime: dayjs().add(1, "day").add(1, "week").add(30, "minutes").toDate(),
            status: BookingStatus.ACCEPTED,
          },
          {
            uid: uuid(),
            title: "Yoga class",
            recurringEventId: Buffer.from("yoga-class").toString("base64"),
            startTime: dayjs().add(1, "day").add(2, "week").toDate(),
            endTime: dayjs().add(1, "day").add(2, "week").add(30, "minutes").toDate(),
            status: BookingStatus.ACCEPTED,
          },
          {
            uid: uuid(),
            title: "Yoga class",
            recurringEventId: Buffer.from("yoga-class").toString("base64"),
            startTime: dayjs().add(1, "day").add(3, "week").toDate(),
            endTime: dayjs().add(1, "day").add(3, "week").add(30, "minutes").toDate(),
            status: BookingStatus.ACCEPTED,
          },
          {
            uid: uuid(),
            title: "Yoga class",
            recurringEventId: Buffer.from("yoga-class").toString("base64"),
            startTime: dayjs().add(1, "day").add(4, "week").toDate(),
            endTime: dayjs().add(1, "day").add(4, "week").add(30, "minutes").toDate(),
            status: BookingStatus.ACCEPTED,
          },
          {
            uid: uuid(),
            title: "Yoga class",
            recurringEventId: Buffer.from("yoga-class").toString("base64"),
            startTime: dayjs().add(1, "day").add(5, "week").toDate(),
            endTime: dayjs().add(1, "day").add(5, "week").add(30, "minutes").toDate(),
            status: BookingStatus.ACCEPTED,
          },
          {
            uid: uuid(),
            title: "Seeded Yoga class",
            description: "seeded",
            recurringEventId: Buffer.from("seeded-yoga-class").toString("base64"),
            startTime: dayjs().subtract(4, "day").toDate(),
            endTime: dayjs().subtract(4, "day").add(30, "minutes").toDate(),
            status: BookingStatus.ACCEPTED,
          },
          {
            uid: uuid(),
            title: "Seeded Yoga class",
            description: "seeded",
            recurringEventId: Buffer.from("seeded-yoga-class").toString("base64"),
            startTime: dayjs().subtract(4, "day").add(1, "week").toDate(),
            endTime: dayjs().subtract(4, "day").add(1, "week").add(30, "minutes").toDate(),
            status: BookingStatus.ACCEPTED,
          },
          {
            uid: uuid(),
            title: "Seeded Yoga class",
            description: "seeded",
            recurringEventId: Buffer.from("seeded-yoga-class").toString("base64"),
            startTime: dayjs().subtract(4, "day").add(2, "week").toDate(),
            endTime: dayjs().subtract(4, "day").add(2, "week").add(30, "minutes").toDate(),
            status: BookingStatus.ACCEPTED,
          },
          {
            uid: uuid(),
            title: "Seeded Yoga class",
            description: "seeded",
            recurringEventId: Buffer.from("seeded-yoga-class").toString("base64"),
            startTime: dayjs().subtract(4, "day").add(3, "week").toDate(),
            endTime: dayjs().subtract(4, "day").add(3, "week").add(30, "minutes").toDate(),
            status: BookingStatus.ACCEPTED,
          },
        ],
      },
      {
        title: "Tennis class",
        slug: "tennis-class",
        length: 60,
        recurringEvent: { freq: 2, count: 10, interval: 2 },
        requiresConfirmation: true,
        _bookings: [
          {
            uid: uuid(),
            title: "Tennis class",
            recurringEventId: Buffer.from("tennis-class").toString("base64"),
            startTime: dayjs().add(2, "day").toDate(),
            endTime: dayjs().add(2, "day").add(60, "minutes").toDate(),
            status: BookingStatus.PENDING,
          },
          {
            uid: uuid(),
            title: "Tennis class",
            recurringEventId: Buffer.from("tennis-class").toString("base64"),
            startTime: dayjs().add(2, "day").add(2, "week").toDate(),
            endTime: dayjs().add(2, "day").add(2, "week").add(60, "minutes").toDate(),
            status: BookingStatus.PENDING,
          },
          {
            uid: uuid(),
            title: "Tennis class",
            recurringEventId: Buffer.from("tennis-class").toString("base64"),
            startTime: dayjs().add(2, "day").add(4, "week").toDate(),
            endTime: dayjs().add(2, "day").add(4, "week").add(60, "minutes").toDate(),
            status: BookingStatus.PENDING,
          },
          {
            uid: uuid(),
            title: "Tennis class",
            recurringEventId: Buffer.from("tennis-class").toString("base64"),
            startTime: dayjs().add(2, "day").add(8, "week").toDate(),
            endTime: dayjs().add(2, "day").add(8, "week").add(60, "minutes").toDate(),
            status: BookingStatus.PENDING,
          },
          {
            uid: uuid(),
            title: "Tennis class",
            recurringEventId: Buffer.from("tennis-class").toString("base64"),
            startTime: dayjs().add(2, "day").add(10, "week").toDate(),
            endTime: dayjs().add(2, "day").add(10, "week").add(60, "minutes").toDate(),
            status: BookingStatus.PENDING,
          },
        ],
      },
    ],
  });

  await createUserAndEventType({
    user: {
      email: "trial@example.com",
      password: "trial",
      username: "trial",
      name: "Trial Example",
    },
    eventTypes: [
      {
        title: "30min",
        slug: "30min",
        length: 30,
      },
      {
        title: "60min",
        slug: "60min",
        length: 60,
      },
    ],
  });

  await createUserAndEventType({
    user: {
      email: "free@example.com",
      password: "free",
      username: "free",
      name: "Free Example",
    },
    eventTypes: [
      {
        title: "30min",
        slug: "30min",
        length: 30,
      },
      {
        title: "60min",
        slug: "60min",
        length: 30,
      },
    ],
  });

  await createUserAndEventType({
    user: {
      email: "usa@example.com",
      password: "usa",
      username: "usa",
      name: "USA Timezone Example",
      timeZone: "America/Phoenix",
    },
    eventTypes: [
      {
        title: "30min",
        slug: "30min",
        length: 30,
      },
    ],
  });

  const freeUserTeam = await createUserAndEventType({
    user: {
      email: "teamfree@example.com",
      password: "teamfree",
      username: "teamfree",
      name: "Team Free Example",
    },
  });

  const proUserTeam = await createUserAndEventType({
    user: {
      email: "teampro@example.com",
      password: "teampro",
      username: "teampro",
      name: "Team Pro Example",
    },
  });

  const pro2UserTeam = await createUserAndEventType({
    user: {
      email: "teampro2@example.com",
      password: "teampro2",
      username: "teampro2",
      name: "Team Pro Example 2",
    },
  });

  const pro3UserTeam = await createUserAndEventType({
    user: {
      email: "teampro3@example.com",
      password: "teampro3",
      username: "teampro3",
      name: "Team Pro Example 3",
    },
  });

  const pro4UserTeam = await createUserAndEventType({
    user: {
      email: "teampro4@example.com",
      password: "teampro4",
      username: "teampro4",
      name: "Team Pro Example 4",
    },
  });

  const admin = await createUserAndEventType({
    user: {
      email: "admin@example.com",
      /** To comply with admin password requirements  */
      password: "ADMINadmin2022!",
      username: "admin",
      name: "Admin Example",
      role: "ADMIN",
    },
  });

  const clientId = process.env.SEED_OAUTH2_CLIENT_ID;
  const clientSecret = process.env.SEED_OAUTH2_CLIENT_SECRET_HASHED;

  if (clientId && clientSecret) {
    await createOAuthClientForUser(admin.id, {
      clientId,
      clientSecret,
      name: "atoms examples app oauth 2 client",
      purpose: "test atoms examples app with oauth 2",
      redirectUri: "http://localhost:4321",
      websiteUrl: "http://localhost:4321",
      enablePkce: false,
    });
  }

  if (process.env.E2E_TEST_CALCOM_QA_EMAIL && process.env.E2E_TEST_CALCOM_QA_PASSWORD) {
    await createUserAndEventType({
      user: {
        email: process.env.E2E_TEST_CALCOM_QA_EMAIL || "qa@example.com",
        password: process.env.E2E_TEST_CALCOM_QA_PASSWORD || "qa",
        username: "qa",
        name: "QA Example",
      },
      eventTypes: [
        {
          title: "15min",
          slug: "15min",
          length: 15,
        },
      ],
      credentials: [
        process.env.E2E_TEST_CALCOM_QA_GCAL_CREDENTIALS
          ? {
              type: "google_calendar",
              key: JSON.parse(process.env.E2E_TEST_CALCOM_QA_GCAL_CREDENTIALS) as Prisma.JsonObject,
              appId: "google-calendar",
            }
          : null,
      ],
    });
  }

  await createTeamAndAddUsers(
    {
      name: "Seeded Team",
      slug: "seeded-team",
      eventTypes: {
        createMany: {
          data: [
            {
              title: "Collective Seeded Team Event",
              slug: "collective-seeded-team-event",
              length: 15,
              schedulingType: "COLLECTIVE",
            },
            {
              title: "Round Robin Seeded Team Event",
              slug: "round-robin-seeded-team-event",
              length: 15,
              schedulingType: "ROUND_ROBIN",
            },
          ],
        },
      },
      createdAt: new Date(),
    },
    [
      {
        id: proUserTeam.id,
        username: proUserTeam.name || "Unknown",
      },
      {
        id: freeUserTeam.id,
        username: freeUserTeam.name || "Unknown",
      },
      {
        id: pro2UserTeam.id,
        username: pro2UserTeam.name || "Unknown",
        role: "MEMBER",
      },
      {
        id: pro3UserTeam.id,
        username: pro3UserTeam.name || "Unknown",
      },
      {
        id: pro4UserTeam.id,
        username: pro4UserTeam.name || "Unknown",
      },
    ]
  );

  await createTeamAndAddUsers(
    {
      name: "Seeded Team (Marketing)",
      slug: "seeded-team-marketing",
      eventTypes: {
        createMany: {
          data: [
            {
              title: "Collective Seeded Team Event",
              slug: "collective-seeded-team-event",
              length: 15,
              schedulingType: "COLLECTIVE",
            },
            {
              title: "Round Robin Seeded Team Event",
              slug: "round-robin-seeded-team-event",
              length: 15,
              schedulingType: "ROUND_ROBIN",
            },
          ],
        },
      },
      createdAt: new Date(),
    },
    [
      {
        id: proUserTeam.id,
        username: proUserTeam.name || "Unknown",
      },
      {
        id: freeUserTeam.id,
        username: freeUserTeam.name || "Unknown",
      },
      {
        id: pro2UserTeam.id,
        username: pro2UserTeam.name || "Unknown",
        role: "MEMBER",
      },
      {
        id: pro3UserTeam.id,
        username: pro3UserTeam.name || "Unknown",
      },
      {
        id: pro4UserTeam.id,
        username: pro4UserTeam.name || "Unknown",
      },
    ]
  );

  await createTeamAndAddUsers(
    {
      name: "Seeded Team (Design)",
      slug: "seeded-team-design",
      eventTypes: {
        createMany: {
          data: [
            {
              title: "Collective Seeded Team Event",
              slug: "collective-seeded-team-event",
              length: 15,
              schedulingType: "COLLECTIVE",
            },
            {
              title: "Round Robin Seeded Team Event",
              slug: "round-robin-seeded-team-event",
              length: 15,
              schedulingType: "ROUND_ROBIN",
            },
          ],
        },
      },
      createdAt: new Date(),
    },
    [
      {
        id: proUserTeam.id,
        username: proUserTeam.name || "Unknown",
      },
      {
        id: freeUserTeam.id,
        username: freeUserTeam.name || "Unknown",
      },
      {
        id: pro2UserTeam.id,
        username: pro2UserTeam.name || "Unknown",
        role: "MEMBER",
      },
      {
        id: pro3UserTeam.id,
        username: pro3UserTeam.name || "Unknown",
      },
      {
        id: pro4UserTeam.id,
        username: pro4UserTeam.name || "Unknown",
      },
    ]
  );

  await createOrganizationAndAddMembersAndTeams({
    org: {
      orgData: {
        name: "Acme Inc",
        slug: "acme",
        isOrganization: true,
        organizationSettings: {
          isOrganizationVerified: true,
          orgAutoAcceptEmail: "acme.com",
          isAdminAPIEnabled: true,
          isAdminReviewed: true,
        },
      },
      members: [
        {
          memberData: {
            email: "owner1-acme@example.com",
            password: {
              create: {
                hash: "owner1-acme",
              },
            },
            username: "owner1-acme",
            name: "Owner 1",
          },
          orgMembership: {
            role: "OWNER",
            accepted: true,
          },
          orgProfile: {
            username: "owner1",
          },
          inTeams: [
            {
              slug: "team1",
              role: "ADMIN",
            },
          ],
        },
        ...Array.from({ length: 10 }, (_, i) => ({
          memberData: {
            email: `member${i}-acme@example.com`,
            password: {
              create: {
                hash: `member${i}-acme`,
              },
            },
            username: `member${i}-acme`,
            name: `Member ${i}`,
          },
          orgMembership: {
            role: MembershipRole.MEMBER,
            accepted: true,
          },
          orgProfile: {
            username: `member${i}`,
          },
          inTeams:
            i % 2 === 0
              ? [
                  {
                    slug: "team1",
                    role: MembershipRole.MEMBER,
                  },
                ]
              : [],
        })),
      ],
    },
    teams: [
      {
        teamData: {
          name: "Team 1",
          slug: "team1",
        },
        nonOrgMembers: [
          {
            email: "non-acme-member-1@example.com",
            password: {
              create: {
                hash: "non-acme-member-1",
              },
            },
            username: "non-acme-member-1",
            name: "NonAcme Member1",
          },
        ],
      },
    ],
    usersOutsideOrg: [
      {
        name: "Jane Doe",
        email: "jane@acme.com",
        username: "jane-outside-org",
      },
    ],
  });

  await createOrganizationAndAddMembersAndTeams({
    org: {
      orgData: {
        name: "Dunder Mifflin",
        slug: "dunder-mifflin",
        isOrganization: true,
        organizationSettings: {
          isOrganizationVerified: true,
          orgAutoAcceptEmail: "dunder-mifflin.com",
          isAdminReviewed: true,
        },
      },
      members: [
        {
          memberData: {
            email: "owner1-dunder@example.com",
            password: {
              create: {
                hash: "owner1-dunder",
              },
            },
            username: "owner1-dunder",
            name: "Owner 1",
          },
          orgMembership: {
            role: "OWNER",
            accepted: true,
          },
          orgProfile: {
            username: "owner1",
          },
          inTeams: [
            {
              slug: "team1",
              role: "ADMIN",
            },
          ],
        },
      ],
    },
    teams: [
      {
        teamData: {
          name: "Team 1",
          slug: "team1",
        },
        nonOrgMembers: [
          {
            email: "non-dunder-member-1@example.com",
            password: {
              create: {
                hash: "non-dunder-member-1",
              },
            },
            username: "non-dunder-member-1",
            name: "NonDunder Member1",
          },
        ],
      },
    ],
    usersOutsideOrg: [
      {
        name: "John Doe",
        email: "john@dunder-mifflin.com",
        username: "john-outside-org",
      },
    ],
  });

  // Routing forms feature removed - routing form seeding no longer needed

  await ensureAcmeOwnerHasApiKeySeeded();
  await seedPerHostLocationsInAcmeOrg();
}

async function runSeed() {
  await prisma.$connect();

  await mainAppStore();
  await main();
  await mainHugeEventTypesSeed();
}

runSeed()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });
