/**
 * Booking Service
 *
 * Centralized business logic for demo bookings and appointment management.
 * Handles booking creation, validation, status updates, and calendar integration.
 *
 * @module lib/services/booking.service
 */

import { logger } from '@/lib/logger';
import { createId } from '@paralleldrive/cuid2';
import { bookingsAdapter as demoBookings } from '@/lib/adapters/bookings';

// ============================================================================
// Types
// ============================================================================

export type BookingStatus = 'pending' | 'confirmed' | 'cancelled' | 'completed' | 'no_show';

export interface CreateDemoBookingInput {
  fullName: string;
  email: string;
  telephone: string;
  appointmentDate: Date;
  appointmentTime: string;
  duration: string;
  specialRequirements?: string | null;
  language: 'ar' | 'en';
  timezone?: string;
}

export interface BookingResult {
  success: boolean;
  bookingId?: string;
  error?: string;
  errorCode?: 'DUPLICATE' | 'INVALID_DATE' | 'DATABASE_ERROR' | 'VALIDATION_ERROR';
}

export interface BookingDetails {
  id: string;
  fullName: string;
  email: string;
  telephone: string;
  appointmentDate: Date;
  appointmentTime: string;
  duration: string;
  status: BookingStatus;
  specialRequirements?: string | null;
  language: string;
  customerTimezone?: string | null;
  meetingLink?: string | null;
  googleCalendarEventId?: string | null;
  createdAt: Date;
  updatedAt: Date;
}

export interface BookingListQuery {
  status?: BookingStatus | 'all';
  dateFrom?: Date;
  dateTo?: Date;
  email?: string;
  page?: number;
  limit?: number;
  sortBy?: 'appointmentDate' | 'createdAt';
  sortOrder?: 'asc' | 'desc';
}

export interface BookingListResult {
  bookings: BookingDetails[];
  total: number;
  page: number;
  pageSize: number;
  totalPages: number;
}

// ============================================================================
// Validation
// ============================================================================

/**
 * Validate appointment date is in the future
 */
function validateAppointmentDate(date: Date): { valid: boolean; error?: string } {
  const now = new Date();
  now.setHours(0, 0, 0, 0);

  if (date < now) {
    return { valid: false, error: 'Appointment date must be in the future' };
  }

  // Max 90 days in future
  const maxDate = new Date();
  maxDate.setDate(maxDate.getDate() + 90);

  if (date > maxDate) {
    return { valid: false, error: 'Appointment date cannot be more than 90 days in the future' };
  }

  return { valid: true };
}

/**
 * Check for duplicate booking (same email + date + time)
 */
async function checkDuplicateBooking(
  email: string,
  appointmentDate: Date,
  appointmentTime: string
): Promise<boolean> {
  const dateOnly = appointmentDate.toISOString().split('T')[0];

  const existing = await demoBookings.findFirst({
    where: {
      email: email.toLowerCase(),
      appointmentDate: new Date(`${dateOnly}T00:00:00.000Z`),
      appointmentTime,
      status: { notIn: ['cancelled'] },
    },
  });

  return !!existing;
}

// ============================================================================
// Service Class
// ============================================================================

export class BookingService {
  /**
   * Create a new demo booking
   */
  async createDemoBooking(input: CreateDemoBookingInput): Promise<BookingResult> {
    try {
      // Validate date
      const dateValidation = validateAppointmentDate(input.appointmentDate);
      if (!dateValidation.valid) {
        return {
          success: false,
          error: dateValidation.error,
          errorCode: 'INVALID_DATE',
        };
      }

      // Check for duplicates
      const isDuplicate = await checkDuplicateBooking(
        input.email,
        input.appointmentDate,
        input.appointmentTime
      );

      if (isDuplicate) {
        return {
          success: false,
          error: 'A booking already exists for this time slot',
          errorCode: 'DUPLICATE',
        };
      }

      // Extract date-only string to prevent timezone issues
      const dateOnly = input.appointmentDate.toISOString().split('T')[0];
      const bookingId = createId();
      const now = new Date();

      // Create booking via adapter (routes to Prisma or Convex)
      const booking = await demoBookings.create({
        data: {
          id: bookingId,
          fullName: input.fullName.trim(),
          email: input.email.toLowerCase().trim(),
          telephone: input.telephone.trim(),
          appointmentDate: new Date(`${dateOnly}T00:00:00.000Z`),
          appointmentTime: input.appointmentTime,
          duration: input.duration,
          specialRequirements: input.specialRequirements?.trim() || null,
          language: input.language,
          customerTimezone: input.timezone || 'Asia/Qatar',
          status: 'pending',
          webhookStatus: 'pending',
          webhookRetryCount: 0,
          createdAt: now,
          updatedAt: now,
        },
      });

      logger.info('[BookingService] Booking created', {
        bookingId,
        email: input.email.slice(-10),
        date: dateOnly,
      });

      return { success: true, bookingId };
    } catch (error) {
      logger.error('[BookingService] Create booking failed', error);
      return {
        success: false,
        error: 'Failed to create booking',
        errorCode: 'DATABASE_ERROR',
      };
    }
  }

  /**
   * Get booking by ID
   */
  async getBookingById(bookingId: string): Promise<BookingDetails | null> {
    const booking = await demoBookings.findUnique({
      where: { id: bookingId },
    });

    if (!booking) return null;

    return this.mapToBookingDetails(booking as any);
  }

  /**
   * List bookings with filters and pagination
   */
  async listBookings(query: BookingListQuery): Promise<BookingListResult> {
    const {
      status = 'all',
      dateFrom,
      dateTo,
      email,
      page = 1,
      limit = 20,
      sortBy = 'appointmentDate',
      sortOrder = 'desc',
    } = query;

    // Build where clause
    const where: Record<string, unknown> = {};

    if (status !== 'all') {
      where.status = status;
    }

    if (dateFrom || dateTo) {
      where.appointmentDate = {};
      if (dateFrom) (where.appointmentDate as Record<string, Date>).gte = dateFrom;
      if (dateTo) (where.appointmentDate as Record<string, Date>).lte = dateTo;
    }

    if (email) {
      where.email = { contains: email.toLowerCase(), mode: 'insensitive' };
    }

    // Get total count via adapter
    const total = await demoBookings.count({ where });

    // Get paginated results via adapter
    const bookings = await demoBookings.findMany({
      where,
      orderBy: { [sortBy]: sortOrder },
      skip: (page - 1) * limit,
      take: limit,
    });

    return {
      bookings: (bookings as any[]).map(this.mapToBookingDetails),
      total,
      page,
      pageSize: limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  /**
   * Update booking status
   */
  async updateStatus(
    bookingId: string,
    status: BookingStatus,
    reason?: string
  ): Promise<BookingResult> {
    try {
      const booking = await demoBookings.findUnique({
        where: { id: bookingId },
      });

      if (!booking) {
        return { success: false, error: 'Booking not found' };
      }

      const bookingRec = booking as Record<string, unknown>;
      await demoBookings.update({
        where: { id: bookingId },
        data: {
          status,
          updatedAt: new Date(),
          // Store cancellation reason in special requirements if cancelled
          specialRequirements:
            status === 'cancelled' && reason
              ? `[Cancelled: ${reason}] ${(bookingRec.specialRequirements as string) || ''}`
              : (bookingRec.specialRequirements as string | null),
        },
      });

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

      return { success: true, bookingId };
    } catch (error) {
      logger.error('[BookingService] Update status failed', error);
      return { success: false, error: 'Failed to update booking status' };
    }
  }

  /**
   * Update booking with meeting link
   */
  async updateMeetingLink(
    bookingId: string,
    meetingLink: string,
    googleCalendarEventId?: string,
    googleConferenceId?: string
  ): Promise<BookingResult> {
    try {
      await demoBookings.update({
        where: { id: bookingId },
        data: {
          meetingLink,
          googleCalendarEventId: googleCalendarEventId || undefined,
          googleConferenceId: googleConferenceId || undefined,
          googleJoinUrl: meetingLink,
          updatedAt: new Date(),
        },
      });

      return { success: true, bookingId };
    } catch (error) {
      logger.error('[BookingService] Update meeting link failed', error);
      return { success: false, error: 'Failed to update meeting link' };
    }
  }

  /**
   * Get upcoming bookings for reminders
   */
  async getUpcomingBookingsForReminders(
    hoursAhead: number = 24
  ): Promise<BookingDetails[]> {
    const now = new Date();
    const reminderCutoff = new Date(now.getTime() + hoursAhead * 60 * 60 * 1000);

    const bookings = await demoBookings.findMany({
      where: {
        status: 'confirmed',
        appointmentDate: {
          gte: now,
          lte: reminderCutoff,
        },
        reminderSentAt: null,
      },
      orderBy: { appointmentDate: 'asc' },
    });

    return (bookings as any[]).map(this.mapToBookingDetails);
  }

  /**
   * Mark reminder as sent
   */
  async markReminderSent(bookingId: string): Promise<void> {
    await demoBookings.update({
      where: { id: bookingId },
      data: {
        reminderSentAt: new Date(),
        reminderEmailSent: true,
      },
    });
  }

  // ============================================================================
  // Private Methods
  // ============================================================================

  private mapToBookingDetails(booking: {
    id: string;
    fullName: string;
    email: string;
    telephone: string;
    appointmentDate: Date;
    appointmentTime: string;
    duration: string;
    status: string;
    specialRequirements: string | null;
    language: string;
    customerTimezone: string | null;
    meetingLink: string | null;
    googleCalendarEventId: string | null;
    createdAt: Date;
    updatedAt: Date;
  }): BookingDetails {
    return {
      id: booking.id,
      fullName: booking.fullName,
      email: booking.email,
      telephone: booking.telephone,
      appointmentDate: booking.appointmentDate,
      appointmentTime: booking.appointmentTime,
      duration: booking.duration,
      status: booking.status as BookingStatus,
      specialRequirements: booking.specialRequirements,
      language: booking.language,
      customerTimezone: booking.customerTimezone,
      meetingLink: booking.meetingLink,
      googleCalendarEventId: booking.googleCalendarEventId,
      createdAt: booking.createdAt,
      updatedAt: booking.updatedAt,
    };
  }

}

// Export singleton instance
export const bookingService = new BookingService();
