/**
 * Calendar Sync EventBus Listener
 *
 * Auto-populates CalendarItems from Shifts, Meetings, and TicketedEvents.
 * Creates items on a system "Platform Events" layer, giving volunteers a
 * unified timeline of all scheduled activities.
 *
 * Uses the existing CalendarItem.sourceType + sourceId fields for tracking
 * which external entity each calendar item came from.
 *
 * No feature guard — always active if enableSocialCalendar is true (checked per-event).
 */

import { eventBus } from '../event-bus.service';
import { logger } from '../../utils/logger';

// Lazy-import prisma
let prismaPromise: ReturnType<typeof getPrisma> | null = null;
async function getPrisma() {
  const { prisma } = await import('../../config/database');
  return prisma;
}
function lazyPrisma() {
  if (!prismaPromise) prismaPromise = getPrisma();
  return prismaPromise;
}

/**
 * Check if the social calendar feature is enabled in site settings.
 */
async function isCalendarEnabled(): Promise<boolean> {
  try {
    const prisma = await lazyPrisma();
    const settings = await prisma.siteSettings.findFirst({ select: { enableSocialCalendar: true } });
    return settings?.enableSocialCalendar ?? false;
  } catch {
    return false;
  }
}

/**
 * Find or create the system "Platform Events" calendar layer.
 * Uses a well-known layer name so all sync items land in one place.
 */
async function getSystemLayer(userId: string): Promise<string | null> {
  try {
    const prisma = await lazyPrisma();

    // Look for existing system layer for this user
    const existing = await prisma.calendarLayer.findFirst({
      where: { userId, name: 'Platform Events', layerType: 'SYSTEM' },
      select: { id: true },
    });
    if (existing) return existing.id;

    // Create a new one
    const layer = await prisma.calendarLayer.create({
      data: {
        userId,
        name: 'Platform Events',
        color: '#3498db',
        layerType: 'SYSTEM',
        visibility: 'PRIVATE',
        isEnabled: true,
      },
    });
    return layer.id;
  } catch (err) {
    logger.debug('Calendar sync: failed to get/create system layer:', err);
    return null;
  }
}

/**
 * Upsert a calendar item linked to an external source.
 */
async function upsertCalendarItem(
  userId: string,
  sourceType: string,
  sourceId: string,
  data: {
    title: string;
    date: string;
    startTime: string;
    endTime: string;
    description?: string;
    location?: string;
  },
): Promise<void> {
  try {
    if (!(await isCalendarEnabled())) return;

    const prisma = await lazyPrisma();
    const layerId = await getSystemLayer(userId);
    if (!layerId) return;

    const dateObj = new Date(data.date + 'T00:00:00Z');

    // Check if calendar item already exists for this source
    const existing = await prisma.calendarItem.findFirst({
      where: { sourceType: sourceType as any, sourceId },
      select: { id: true },
    });

    if (existing) {
      // Update existing item
      await prisma.calendarItem.update({
        where: { id: existing.id },
        data: {
          title: data.title,
          date: dateObj,
          startTime: data.startTime,
          endTime: data.endTime,
          description: data.description,
          location: data.location,
        },
      });
    } else {
      // Create new item
      await prisma.calendarItem.create({
        data: {
          userId,
          layerId,
          title: data.title,
          date: dateObj,
          startTime: data.startTime,
          endTime: data.endTime,
          description: data.description,
          location: data.location,
          sourceType: sourceType as any,
          sourceId,
          itemType: 'EVENT',
          busyStatus: 'BUSY',
        },
      });
    }
  } catch (err) {
    logger.debug(`Calendar sync: upsert failed for ${sourceType}:${sourceId}:`, err);
  }
}

/**
 * Delete a calendar item by its source reference.
 */
async function deleteBySource(sourceType: string, sourceId: string): Promise<void> {
  try {
    const prisma = await lazyPrisma();
    await prisma.calendarItem.deleteMany({
      where: { sourceType: sourceType as any, sourceId },
    });
  } catch (err) {
    logger.debug(`Calendar sync: delete failed for ${sourceType}:${sourceId}:`, err);
  }
}

export function registerCalendarSyncListener(): void {
  // Shift created → Calendar item
  eventBus.subscribe('shift.created', 'calendar:shift-created', async (payload) => {
    await upsertCalendarItem(payload.createdByUserId, 'SHIFT', payload.shiftId, {
      title: `Shift: ${payload.title}`,
      date: payload.date,
      startTime: payload.startTime,
      endTime: payload.endTime,
      location: payload.cutName ?? undefined,
    });
  });

  // Shift updated → Update calendar item
  eventBus.subscribe('shift.updated', 'calendar:shift-updated', async (payload) => {
    // We don't know who created the shift — find existing calendar item
    try {
      const prisma = await lazyPrisma();
      const existing = await prisma.calendarItem.findFirst({
        where: { sourceId: payload.shiftId },
        select: { userId: true },
      });
      if (!existing) return;
      await upsertCalendarItem(existing.userId, 'SHIFT', payload.shiftId, {
        title: `Shift: ${payload.title}`,
        date: payload.date,
        startTime: payload.startTime,
        endTime: payload.endTime,
        location: payload.cutName ?? undefined,
      });
    } catch {
      // silent
    }
  });

  // Shift deleted → Remove calendar item
  eventBus.subscribe('shift.deleted', 'calendar:shift-deleted', async (payload) => {
    await deleteBySource('SHIFT', payload.shiftId);
  });

  // Meeting created → Calendar item
  eventBus.subscribe('meeting.created', 'calendar:meeting-created', async (payload) => {
    const date = payload.scheduledAt.split('T')[0];
    const time = payload.scheduledAt.split('T')[1]?.slice(0, 5) ?? '00:00';
    const endHour = parseInt(time.split(':')[0]) + 1;
    const endTime = `${String(endHour).padStart(2, '0')}:${time.split(':')[1]}`;

    await upsertCalendarItem(payload.createdByUserId, 'MEETING', payload.meetingId, {
      title: `Meeting: ${payload.title}`,
      date,
      startTime: time,
      endTime,
      description: payload.jitsiRoomName ? `Jitsi room: ${payload.jitsiRoomName}` : undefined,
    });
  });

  // Meeting updated → Update calendar item
  eventBus.subscribe('meeting.updated', 'calendar:meeting-updated', async (payload) => {
    try {
      const prisma = await lazyPrisma();
      const existing = await prisma.calendarItem.findFirst({
        where: { sourceId: payload.meetingId },
        select: { userId: true },
      });
      if (!existing) return;

      const date = payload.scheduledAt.split('T')[0];
      const time = payload.scheduledAt.split('T')[1]?.slice(0, 5) ?? '00:00';
      const endHour = parseInt(time.split(':')[0]) + 1;
      const endTime = `${String(endHour).padStart(2, '0')}:${time.split(':')[1]}`;

      await upsertCalendarItem(existing.userId, 'MEETING', payload.meetingId, {
        title: `Meeting: ${payload.title}`,
        date,
        startTime: time,
        endTime,
      });
    } catch {
      // silent
    }
  });

  // Meeting deleted → Remove calendar item
  eventBus.subscribe('meeting.deleted', 'calendar:meeting-deleted', async (payload) => {
    await deleteBySource('MEETING', payload.meetingId);
  });

  // Ticketed event published → Calendar item
  eventBus.subscribe('ticketed-event.published', 'calendar:ticketed-event', async (payload) => {
    // Find who created this event
    try {
      const prisma = await lazyPrisma();
      const event = await prisma.ticketedEvent.findUnique({
        where: { id: payload.eventId },
        select: { createdByUserId: true },
      });
      if (!event) return;
      await upsertCalendarItem(event.createdByUserId, 'TICKETED_EVENT', payload.eventId, {
        title: `Event: ${payload.title}`,
        date: payload.date,
        startTime: payload.startTime,
        endTime: payload.endTime ?? payload.startTime,
        location: payload.location,
      });
    } catch {
      // silent
    }
  });

  // Ticketed event cancelled → Remove calendar item
  eventBus.subscribe('ticketed-event.cancelled', 'calendar:ticketed-event-cancel', async (payload) => {
    await deleteBySource('TICKETED_EVENT', payload.eventId);
  });
}
