import type { DestinationCalendar } from "@prisma/client";
import type { Prisma } from "@prisma/client";

import { metadata as GoogleMeetMetadata } from "@calcom/app-store/googlevideo/_metadata";
import { MeetLocationType } from "@calcom/app-store/locations";
import EventManager from "@calcom/core/EventManager";
import type { EventManagerInitParams } from "@calcom/core/EventManager";
import { getVideoCallDetails } from "@calcom/features/bookings/lib/handleNewBooking/getVideoCallDetails";
import { getVideoCallUrlFromCalEvent } from "@calcom/lib/CalEventParser";
import logger from "@calcom/lib/logger";
import { getTranslation } from "@calcom/lib/server/i18n";
import { BookingReferenceRepository } from "@calcom/lib/server/repository/bookingReference";
import { prisma } from "@calcom/prisma";
import type { CalendarEvent, AdditionalInformation } from "@calcom/types/Calendar";

export const handleRescheduleEventManager = async ({
  evt,
  rescheduleUid,
  newBookingId,
  changedOrganizer,
  previousHostDestinationCalendar,
  initParams,
  bookingLocation,
  bookingId,
  bookingICalUID,
  bookingMetadata,
}: {
  evt: CalendarEvent;
  rescheduleUid: string;
  newBookingId?: number;
  changedOrganizer?: boolean;
  previousHostDestinationCalendar?: DestinationCalendar[] | null;
  initParams: EventManagerInitParams;
  bookingLocation: string | null;
  bookingId: number;
  bookingICalUID?: string | null;
  bookingMetadata?: Prisma.JsonValue;
}) => {
  const handleRescheduleEventManager = logger.getSubLogger({
    prefix: ["handleRescheduleEventManager", `${bookingId}`],
  });

  const eventManager = new EventManager(initParams.user, initParams?.eventTypeAppMetadata);

  const updateManager = await eventManager.reschedule(
    evt,
    rescheduleUid,
    newBookingId,
    changedOrganizer,
    previousHostDestinationCalendar
  );

  const results = updateManager.results ?? [];

  const { metadata: videoMetadata, videoCallUrl: _videoCallUrl } = getVideoCallDetails({
    results: results,
  });

  let videoCallUrl = _videoCallUrl;
  let metadata: AdditionalInformation = {};
  metadata = videoMetadata;

  if (results.length) {
    // Handle Google Meet results
    if (bookingLocation === MeetLocationType) {
      const googleMeetResult = {
        appName: GoogleMeetMetadata.name,
        type: "conferencing",
        uid: results[0].uid,
        originalEvent: results[0].originalEvent,
      };

      // Find index of google_calendar inside createManager.referencesToCreate
      const googleCalIndex = updateManager.referencesToCreate.findIndex(
        (ref) => ref.type === "google_calendar"
      );
      const googleCalResult = results[googleCalIndex];

      const t = await getTranslation("en", "common");

      if (!googleCalResult) {
        handleRescheduleEventManager.warn("Google Calendar not installed but using Google Meet as location");
        results.push({
          ...googleMeetResult,
          success: false,
          calWarnings: [t("google_meet_warning")],
        });
      }

      const googleHangoutLink = Array.isArray(googleCalResult?.updatedEvent)
        ? googleCalResult.updatedEvent[0]?.hangoutLink
        : googleCalResult?.updatedEvent?.hangoutLink ?? googleCalResult?.createdEvent?.hangoutLink;

      if (googleHangoutLink) {
        results.push({
          ...googleMeetResult,
          success: true,
        });

        // Add google_meet to referencesToCreate in the same index as google_calendar
        updateManager.referencesToCreate[googleCalIndex] = {
          ...updateManager.referencesToCreate[googleCalIndex],
          meetingUrl: googleHangoutLink,
        };

        // Also create a new referenceToCreate with type video for google_meet
        updateManager.referencesToCreate.push({
          type: "google_meet_video",
          meetingUrl: googleHangoutLink,
          uid: googleCalResult.uid,
          credentialId: updateManager.referencesToCreate[googleCalIndex].credentialId,
        });
      } else if (googleCalResult && !googleHangoutLink) {
        results.push({
          ...googleMeetResult,
          success: false,
        });
      }
    }
    const createdOrUpdatedEvent = Array.isArray(results[0]?.updatedEvent)
      ? results[0]?.updatedEvent[0]
      : results[0]?.updatedEvent ?? results[0]?.createdEvent;
    metadata.hangoutLink = createdOrUpdatedEvent?.hangoutLink;
    metadata.conferenceData = createdOrUpdatedEvent?.conferenceData;
    metadata.entryPoints = createdOrUpdatedEvent?.entryPoints;

    videoCallUrl =
      metadata.hangoutLink || createdOrUpdatedEvent?.url || getVideoCallUrlFromCalEvent(evt) || videoCallUrl;

    const calendarResult = results.find((result) => result.type.includes("_calendar"));

    evt.iCalUID = Array.isArray(calendarResult?.updatedEvent)
      ? calendarResult?.updatedEvent[0]?.iCalUID
      : calendarResult?.updatedEvent?.iCalUID || undefined;
  }
  const newReferencesToCreate = structuredClone(updateManager.referencesToCreate);

  await BookingReferenceRepository.replaceBookingReferences({
    bookingId,
    newReferencesToCreate,
  });

  try {
    if (bookingLocation?.startsWith("http")) {
      videoCallUrl = bookingLocation;
    }

    const newBookingMetaData = videoCallUrl
      ? {
          videoCallUrl: getVideoCallUrlFromCalEvent(evt) || videoCallUrl,
        }
      : undefined;

    await prisma.booking.update({
      where: {
        id: bookingId,
      },
      data: {
        location: bookingLocation,
        iCalUID: evt.iCalUID !== bookingICalUID ? evt.iCalUID : bookingICalUID,
        metadata: { ...(typeof bookingMetadata === "object" && bookingMetadata), ...newBookingMetaData },
      },
    });
  } catch (error) {
    handleRescheduleEventManager.error("Error while updating booking metadata", JSON.stringify({ error }));
  }

  const evtWithAdditionalInfo = {
    ...evt,
    additionalInformation: metadata,
  };

  return { evtWithAdditionalInfo };
};
