import 'server-only'
import {prismaClient} from './prismaClient'
import type {MeetingWithContact} from '@/models/meetings'
import {meetingWithContactInclude} from '@/models/meetings'
import type {Prisma} from '@/generated/prisma/client'

// Prisma exporteert interfaces voor elke CRUD operatie op elke tabel in de database.
// De interfaces worden in verschillende vormen aangeboden, zoals Prisma.MeetingCreateInput,
// Prisma.MeetingCreateWithoutContactInput en Prisma.MeetingUncheckedCreateInput die allemaal gebruikt worden om een
// meeting aan te maken.
//
// De MeetingCreateWithoutContainInput bevat geen input voor de relatie met een contact.
// De MeetingCreateInput bevat wel input voor de relatie met een contact, deze interface verwacht echter specifieke
// prisma syntax, wat het moeilijker maakt om later te switchen naar een ander ORM.
//
// De MeetingUncheckedCreateInput bevat wel input voor de relatie met een contact, maar verwacht geen specifieke prisma
// syntax.
// In plaats daarvan bevat deze interface een contactId property.
export type CreateMeetingParams = Prisma.MeetingUncheckedCreateInput

/**
 * Add a new meeting to the database.
 *
 * @param contactId The id of the contact.
 * @param meeting The meeting information.
 * @param userId The id of the user to whom the meeting belongs.
 */
export function createMeeting({contactId, userId, ...meeting}: CreateMeetingParams): Promise<MeetingWithContact> {
  return prismaClient.meeting.create({
    data: {
      ...meeting,
      // Alhoewel je het contactId rechtstreeks kan meegeven aan de data property, is het beter om de connect syntax
      // te gebruiken.
      // Via de connect property geeft Prisma duidelijkere foutmeldingen als er iets fout gaat in de insert en als deze
      // fout te wijten is aan de relatie.
      contact: {
        connect: {id: contactId},
      },
      user: {
        connect: {id: userId},
      },
    },
    include: meetingWithContactInclude,
  })
}

/**
 * Retrieve all the planned meetings and basic information about the associated contact.
 */
export function getMeetings(userId: string): Promise<MeetingWithContact[]> {
  return prismaClient.meeting.findMany({
    orderBy: {date: 'asc'},
    where: {date: {gte: new Date()}, userId},
    include: meetingWithContactInclude,
  })
}

/**
 * Retrieve a planned meeting and basic information about the associated contact.
 */
export function getMeeting(id: string, userId: string): Promise<MeetingWithContact> {
  return prismaClient.meeting.findUniqueOrThrow({
    where: {id, userId},
    include: meetingWithContactInclude,
  })
}

export interface UpdateMeetingParams extends CreateMeetingParams {
  id: string
}

/**
 * Update an existing meeting.
 *
 * @param contactId The id of the contact with whom the meeting is scheduled.
 * @param userId The id of the user to whom the meeting belongs.
 * @param meeting Information about the meeting.
 */
export function updateMeeting({contactId, userId, ...meeting}: UpdateMeetingParams): Promise<MeetingWithContact> {
  return prismaClient.meeting.update({
    where: {id: meeting.id},
    data: {
      ...meeting,
      contact: {
        connect: {id: contactId},
      },
      user: {
        connect: {id: userId},
      },
    },
    include: meetingWithContactInclude,
  })
}

/**
 * Delete a meeting from the database.
 *
 * @param id The id of the meeting to delete.
 * @param userId The id of the user to whom the meeting belongs.
 */
export async function deleteMeeting(id: string, userId: string): Promise<void> {
  await prismaClient.meeting.delete({where: {id, userId}})
}
