import { Prisma, type PrismaClient } from '../generated/prisma/client.js';
import { jakartaTimestamp, loadDemoRooms, parseEventDate, splitSpeaker } from './seed-data.js';

export const DEMO_EVENT_NAME = 'DEMO — UNAIR Panel Room Schedule';

export async function seedDemo(prisma: PrismaClient, dateValue: string | undefined) {
  const eventDate = parseEventDate(dateValue);
  const date = eventDate.toISOString().slice(0, 10);
  const rooms = loadDemoRooms().sort((a, b) => a.id.localeCompare(b.id));

  return prisma.$transaction(async tx => {
    const existing = await tx.event.findFirst({ where: { name: DEMO_EVENT_NAME, startDate: eventDate } });
    if (existing) throw new Error(`Demo event already exists for ${date}; no data was changed.`);

    const event = await tx.event.create({
      data: { name: DEMO_EVENT_NAME, startDate: eventDate, endDate: eventDate },
    });
    const seededRooms = [];
    for (const demo of rooms) {
      seededRooms.push(await tx.room.upsert({
        where: { code: demo.id }, update: {}, create: { code: demo.id, name: demo.roomTitle },
      }));
    }
    // The new event is private to this transaction. Lock all existing rooms in
    // the same ID order as Express, even when rooms were created out of code order.
    const roomIds = seededRooms.map(room => room.id).sort((a, b) => a - b);
    await tx.$queryRaw(Prisma.sql`SELECT id FROM rooms WHERE id IN (${Prisma.join(roomIds)}) ORDER BY id FOR UPDATE`);
    for (const [index, demo] of rooms.entries()) {
      const room = seededRooms[index];
      const assignment = await tx.eventRoom.create({
        data: { eventId: event.id, roomId: room.id, category: demo.categoryBadge },
      });
      for (const demoSession of demo.sessions) {
        const startsAt = jakartaTimestamp(date, demoSession.start);
        const endsAt = jakartaTimestamp(date, demoSession.end);
        if (endsAt <= startsAt) throw new Error('Demo sessions must end after they start on the seed date.');
        const conflict = await tx.session.findFirst({
          where: { eventRoom: { roomId: room.id }, startsAt: { lt: endsAt }, endsAt: { gt: startsAt } },
        });
        if (conflict) throw new Error(`Room ${room.code} already has an overlapping session; no data was changed.`);
        const session = await tx.session.create({
          data: { eventRoomId: assignment.id, title: demoSession.mainTheme, startsAt, endsAt },
        });
        for (const [index, subtheme] of demoSession.subThemes.entries()) {
          const identity = splitSpeaker(subtheme.speaker);
          const speaker = await tx.speaker.findFirst({ where: identity })
            ?? await tx.speaker.create({ data: identity });
          await tx.topic.create({
            data: {
              sessionId: session.id, title: subtheme.title, position: index + 1,
              topicSpeakers: { create: { speakerId: speaker.id, position: 1 } },
            },
          });
        }
      }
    }
    return event;
  }, { timeout: 30_000, isolationLevel: Prisma.TransactionIsolationLevel.ReadCommitted });
}
