/**
 * Interview Bookings Service
 *
 * Business logic layer for interview booking operations.
 * Handles booking creation, validation, duplicate prevention, and status updates.
 *
 * @module lib/services/interview-bookings.service
 */

import { prisma } from '@/lib/db';
import { logger } from '@/lib/logger';
import { createId } from '@paralleldrive/cuid2';

/**
 * Interview booking data structure
 */
export interface InterviewBookingData {
  applicationId: string;
  jobId: string;
  tokenId: string;
  applicantName: string;
  applicantEmail: string;
  applicantPhone?: string;
  preferredDate: string; // YYYY-MM-DD
  preferredTime: string; // HH:MM
  timezone: string;
  durationMinutes?: number;
  applicantNotes?: string;
}

/**
 * Booking status type
 */
export type BookingStatus = 'pending' | 'confirmed' | 'rescheduled' | 'cancelled' | 'completed';

/**
 * Create interview booking result
 */
interface CreateBookingResult {
  success: boolean;
  bookingId?: string;
  error?: string;
  errorCode?: 'DUPLICATE' | 'INVALID_DATA' | 'DATABASE_ERROR';
}

/**
 * Booking validation result
 */
interface BookingValidationResult {
  valid: boolean;
  error?: string;
}

/**
 * Check if booking already exists for this application
 *
 * Prevents duplicate bookings from same applicant.
 *
 * @param applicationId - Application ID
 * @returns Whether duplicate exists
 */
export async function checkDuplicateBooking(
  applicationId: string
): Promise<boolean> {
  try {
    const existingBooking = await prisma.interview_bookings.findFirst({
      where: {
        applicationId,
        status: {
          in: ['pending', 'confirmed'], // Only check active bookings
        },
      },
    });

    return !!existingBooking;
  } catch (error) {
    logger.error('[InterviewBookings] Duplicate check error:', error);
    // Fail safe - assume no duplicate if check fails
    return false;
  }
}

/**
 * Validate booking time rules
 *
 * Additional validation beyond input validation:
 * - Check if time slot is not in the past
 * - Future: Check availability against calendar
 *
 * @param preferredDate - Date string (YYYY-MM-DD)
 * @param preferredTime - Time string (HH:MM)
 * @param timezone - IANA timezone
 * @returns Validation result
 */
export function validateBookingTime(
  preferredDate: string,
  preferredTime: string,
  timezone: string
): BookingValidationResult {
  try {
    // Combine date and time
    const dateTimeString = `${preferredDate}T${preferredTime}:00`;
    const bookingDateTime = new Date(dateTimeString);
    const now = new Date();

    // Check if in past
    if (bookingDateTime <= now) {
      return {
        valid: false,
        error: 'Interview time cannot be in the past',
      };
    }

    // Future enhancement: Check against calendar availability
    // const isAvailable = await checkCalendarAvailability(bookingDateTime, timezone);

    return { valid: true };
  } catch (error) {
    logger.error('[InterviewBookings] Time validation error:', error);
    return {
      valid: false,
      error: 'Invalid date/time format',
    };
  }
}

/**
 * Create interview booking
 *
 * Saves booking to the database.
 * Uses transaction to ensure token is marked as used atomically.
 *
 * @param data - Booking data
 * @returns Creation result
 */
export async function createBooking(
  data: InterviewBookingData
): Promise<CreateBookingResult> {
  try {
    // Validate input
    if (!data.applicationId || !data.jobId || !data.tokenId) {
      return {
        success: false,
        error: 'Missing required fields',
        errorCode: 'INVALID_DATA',
      };
    }

    // Check for duplicate booking
    const isDuplicate = await checkDuplicateBooking(data.applicationId);
    if (isDuplicate) {
      return {
        success: false,
        error: 'An active interview booking already exists for this application',
        errorCode: 'DUPLICATE',
      };
    }

    // Validate booking time
    const timeValidation = validateBookingTime(
      data.preferredDate,
      data.preferredTime,
      data.timezone
    );

    if (!timeValidation.valid) {
      return {
        success: false,
        error: timeValidation.error,
        errorCode: 'INVALID_DATA',
      };
    }

    // Generate booking ID
    const bookingId = createId();
    const now = new Date();

    // Parse date for database storage
    const preferredDateObj = new Date(data.preferredDate);

    // Create booking with transaction (mark token as used atomically)
    const booking = await prisma.$transaction(async (tx) => {
      // Mark token as used
      await tx.interview_booking_tokens.update({
        where: { id: data.tokenId },
        data: {
          usedAt: now,
          updatedAt: now,
        },
      });

      // Create booking
      return await tx.interview_bookings.create({
        data: {
          id: bookingId,
          applicationId: data.applicationId,
          jobId: data.jobId,
          tokenId: data.tokenId,
          applicantName: data.applicantName,
          applicantEmail: data.applicantEmail,
          applicantPhone: data.applicantPhone || null,
          preferredDate: preferredDateObj,
          preferredTime: data.preferredTime,
          timezone: data.timezone,
          durationMinutes: data.durationMinutes || 60,
          status: 'pending',
          applicantNotes: data.applicantNotes || null,
          createdAt: now,
          updatedAt: now,
        },
      });
    });

    logger.info('[InterviewBookings] Booking created:', {
      bookingId: booking.id,
      applicationId: booking.applicationId,
    });

    return {
      success: true,
      bookingId: booking.id,
    };
  } catch (error) {
    logger.error('[InterviewBookings] Create booking error:', error);

    // Check for specific errors
    if (error instanceof Error) {
      if (error.message.includes('Foreign key constraint')) {
        return {
          success: false,
          error: 'Invalid application, job, or token ID',
          errorCode: 'INVALID_DATA',
        };
      }
    }

    return {
      success: false,
      error: 'Failed to create booking. Please try again.',
      errorCode: 'DATABASE_ERROR',
    };
  }
}

/**
 * Get booking by ID
 *
 * @param bookingId - Booking ID
 * @returns Booking record or null
 */
export async function getBookingById(bookingId: string) {
  try {
    return await prisma.interview_bookings.findUnique({
      where: { id: bookingId },
      include: {
        // Include related records if needed
        job_applications: {
          select: {
            applicantName: true,
            applicantEmail: true,
            positionApplied: true,
          },
        },
        jobs: {
          select: {
            title: true,
            titleAr: true,
            department: true,
            location: true,
          },
        },
      },
    });
  } catch (error) {
    logger.error('[InterviewBookings] Get booking error:', error);
    return null;
  }
}

/**
 * Update booking status
 *
 * Updates the booking status.
 *
 * @param bookingId - Booking ID
 * @param status - New status
 * @param metadata - Additional metadata (confirmed_by, cancelled_reason, etc.)
 * @returns Success status
 */
export async function updateBookingStatus(
  bookingId: string,
  status: BookingStatus,
  metadata?: {
    confirmedDate?: Date;
    confirmedTime?: string;
    confirmedBy?: string;
    meetingLink?: string;
    meetingPlatform?: string;
    staffNotes?: string;
    cancelledBy?: string;
    cancellationReason?: string;
  }
): Promise<boolean> {
  try {
    const now = new Date();
    const updateData: any = {
      status,
      updatedAt: now,
    };

    // Add metadata fields
    if (status === 'confirmed' && metadata) {
      updateData.confirmedDate = metadata.confirmedDate;
      updateData.confirmedTime = metadata.confirmedTime;
      updateData.confirmedBy = metadata.confirmedBy;
      updateData.confirmedAt = now;
      updateData.meetingLink = metadata.meetingLink;
      updateData.meetingPlatform = metadata.meetingPlatform;
      updateData.staffNotes = metadata.staffNotes;
    }

    if (status === 'cancelled' && metadata) {
      updateData.cancelledBy = metadata.cancelledBy;
      updateData.cancelledAt = now;
      updateData.cancellationReason = metadata.cancellationReason;
    }

    // Update in Prisma
    const updated = await prisma.interview_bookings.update({
      where: { id: bookingId },
      data: updateData,
    });

    logger.info('[InterviewBookings] Status updated:', {
      bookingId,
      status,
    });

    return true;
  } catch (error) {
    logger.error('[InterviewBookings] Update status error:', error);
    return false;
  }
}

/**
 * Get all bookings for an application
 *
 * @param applicationId - Application ID
 * @returns List of bookings
 */
export async function getBookingsByApplication(applicationId: string) {
  try {
    return await prisma.interview_bookings.findMany({
      where: { applicationId },
      orderBy: { createdAt: 'desc' },
    });
  } catch (error) {
    logger.error('[InterviewBookings] Get bookings by application error:', error);
    return [];
  }
}
