/**
 * Notifications Service
 *
 * Comprehensive notification management for user notifications:
 * - Create notifications for various events
 * - Fetch user notifications with pagination
 * - Mark notifications as read (single or all)
 * - Get unread count
 * - Delete notifications
 * - Optional email notifications based on user preferences
 *
 * @module lib/services/notifications.service
 */

import { v7 as uuidv7 } from 'uuid';
import { prisma } from '@/lib/db';
import { notificationsAdapter } from '@/lib/adapters/notifications';
import { sendEmail } from '@/lib/email';
import { logger } from '@/lib/logger';
/** JSON-compatible input value (replaces InputJsonValue) */
type InputJsonValue = string | number | boolean | null | InputJsonValue[] | { [key: string]: InputJsonValue };
/** Represents JSON null (replaces JsonNull) */
const JsonNull = null;

/** Notification type enum (replaces Prisma-generated NotificationType) */
type NotificationType = 'new_booking' | 'deposit_received' | 'new_conversation' | 'booking_reminder' | 'system_alert' | 'integration_error' | 'new_lead';

const scopedLogger = logger.scope('NotificationsService');
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || 'https://mawidi.com';

/**
 * Notification data structure
 */
export interface Notification {
  id: string;
  userId: string;
  type: NotificationType;
  title: string;
  message: string;
  actionUrl: string | null;
  isRead: boolean;
  readAt: Date | null;
  metadata: Record<string, unknown> | null;
  createdAt: Date;
}

/**
 * Pagination options for fetching notifications
 */
export interface NotificationPaginationOptions {
  limit?: number;
  offset?: number;
  unreadOnly?: boolean;
  type?: NotificationType;
}

/**
 * Result from fetching notifications with pagination
 */
export interface NotificationFetchResult {
  notifications: Notification[];
  total: number;
  unreadCount: number;
}

/**
 * Data for creating a new notification
 */
export interface CreateNotificationData {
  userId: string;
  type: NotificationType;
  title: string;
  message: string;
  actionUrl?: string;
  metadata?: Record<string, unknown>;
}

/**
 * Notifications Service Class
 */
class NotificationsService {
  /**
   * Get notifications for a user with pagination
   *
   * @param userId - User ID to fetch notifications for
   * @param options - Pagination and filter options
   * @returns Notifications, total count, and unread count
   */
  async getNotifications(
    userId: string,
    options: NotificationPaginationOptions = {}
  ): Promise<NotificationFetchResult> {
    const { limit = 20, offset = 0, unreadOnly = false, type } = options;

    try {
      // Build where clause
      const whereClause: {
        userId: string;
        isRead?: boolean;
        type?: NotificationType;
      } = { userId };

      if (unreadOnly) {
        whereClause.isRead = false;
      }

      if (type) {
        whereClause.type = type;
      }

      // Fetch notifications with pagination
      const [notifications, total, unreadCount] = await Promise.all([
        notificationsAdapter.findMany({
          where: whereClause,
          orderBy: { createdAt: 'desc' },
          take: Math.min(limit, 100), // Cap at 100
          skip: offset,
        }),
        notificationsAdapter.count({ where: whereClause }),
        notificationsAdapter.count({
          where: { userId, isRead: false },
        }),
      ]);

      scopedLogger.info('Notifications fetched', {
        userId,
        count: notifications.length,
        total,
        unreadCount,
      });

      return {
        notifications: (notifications as any[]).map((n) => ({
          ...n,
          metadata: n.metadata as Record<string, unknown> | null,
        })) as Notification[],
        total,
        unreadCount,
      };
    } catch (error) {
      scopedLogger.error('Failed to fetch notifications', { userId, error });
      throw error;
    }
  }

  /**
   * Get a single notification by ID
   *
   * @param notificationId - Notification ID
   * @param userId - User ID (for ownership verification)
   * @returns Notification or null if not found
   */
  async getNotificationById(
    notificationId: string,
    userId: string
  ): Promise<Notification | null> {
    try {
      const notification = await notificationsAdapter.findFirst({
        where: { id: notificationId, userId },
      }) as any;

      if (!notification) {
        return null;
      }

      return {
        ...notification,
        metadata: notification.metadata as Record<string, unknown> | null,
      } as Notification;
    } catch (error) {
      scopedLogger.error('Failed to get notification', { notificationId, userId, error });
      throw error;
    }
  }

  /**
   * Mark specific notifications as read
   *
   * @param userId - User ID (for ownership verification)
   * @param notificationIds - Array of notification IDs to mark as read
   * @returns Number of notifications updated
   */
  async markAsRead(userId: string, notificationIds: string[]): Promise<number> {
    try {
      const result = await notificationsAdapter.updateMany({
        where: {
          id: { in: notificationIds },
          userId,
          isRead: false,
        },
        data: {
          isRead: true,
          readAt: new Date(),
        },
      });

      scopedLogger.info('Notifications marked as read', {
        userId,
        count: result.count,
        requestedIds: notificationIds.length,
      });

      return result.count;
    } catch (error) {
      scopedLogger.error('Failed to mark notifications as read', { userId, error });
      throw error;
    }
  }

  /**
   * Mark all notifications as read for a user
   *
   * @param userId - User ID
   * @returns Number of notifications updated
   */
  async markAllAsRead(userId: string): Promise<number> {
    try {
      const result = await notificationsAdapter.updateMany({
        where: {
          userId,
          isRead: false,
        },
        data: {
          isRead: true,
          readAt: new Date(),
        },
      });

      scopedLogger.info('All notifications marked as read', {
        userId,
        count: result.count,
      });

      return result.count;
    } catch (error) {
      scopedLogger.error('Failed to mark all notifications as read', { userId, error });
      throw error;
    }
  }

  /**
   * Mark a single notification as read
   *
   * @param notificationId - Notification ID
   * @param userId - User ID (for ownership verification)
   * @returns Updated notification or null if not found
   */
  async markSingleAsRead(
    notificationId: string,
    userId: string
  ): Promise<Notification | null> {
    try {
      // First verify ownership
      const existing = await notificationsAdapter.findFirst({
        where: { id: notificationId, userId },
      });

      if (!existing) {
        return null;
      }

      const updated = await notificationsAdapter.update({
        where: { id: notificationId },
        data: {
          isRead: true,
          readAt: new Date(),
        },
      }) as any;

      scopedLogger.info('Notification marked as read', {
        notificationId,
        userId,
      });

      if (!updated) return null;

      return {
        ...updated,
        metadata: updated.metadata as Record<string, unknown> | null,
      } as Notification;
    } catch (error) {
      scopedLogger.error('Failed to mark notification as read', {
        notificationId,
        userId,
        error,
      });
      throw error;
    }
  }

  /**
   * Delete a notification
   *
   * @param notificationId - Notification ID
   * @param userId - User ID (for ownership verification)
   * @returns True if deleted, false if not found
   */
  async deleteNotification(notificationId: string, userId: string): Promise<boolean> {
    try {
      // First verify ownership
      const existing = await notificationsAdapter.findFirst({
        where: { id: notificationId, userId },
      });

      if (!existing) {
        return false;
      }

      await notificationsAdapter.delete({
        where: { id: notificationId },
      });

      scopedLogger.info('Notification deleted', {
        notificationId,
        userId,
      });

      return true;
    } catch (error) {
      scopedLogger.error('Failed to delete notification', {
        notificationId,
        userId,
        error,
      });
      throw error;
    }
  }

  /**
   * Create a new notification
   *
   * @param data - Notification data
   * @returns Created notification
   */
  async createNotification(data: CreateNotificationData): Promise<Notification> {
    try {
      const notification = await notificationsAdapter.create({
        data: {
          id: uuidv7(),
          userId: data.userId,
          type: data.type,
          title: data.title,
          message: data.message,
          actionUrl: data.actionUrl || null,
          metadata: data.metadata ? (data.metadata as InputJsonValue) : JsonNull,
          isRead: false,
        },
      }) as any;

      scopedLogger.info('Notification created', {
        notificationId: notification.id,
        userId: data.userId,
        type: data.type,
      });

      return {
        ...notification,
        metadata: notification.metadata as Record<string, unknown> | null,
      } as Notification;
    } catch (error) {
      scopedLogger.error('Failed to create notification', { data, error });
      throw error;
    }
  }

  /**
   * Get unread notification count for a user
   *
   * @param userId - User ID
   * @returns Unread count
   */
  async getUnreadCount(userId: string): Promise<number> {
    try {
      return await notificationsAdapter.count({
        where: { userId, isRead: false },
      });
    } catch (error) {
      scopedLogger.error('Failed to get unread count', { userId, error });
      throw error;
    }
  }

  /**
   * Delete old read notifications (cleanup)
   *
   * @param userId - User ID
   * @param olderThanDays - Delete notifications older than this many days
   * @returns Number of deleted notifications
   */
  async cleanupOldNotifications(userId: string, olderThanDays: number = 30): Promise<number> {
    try {
      const cutoffDate = new Date();
      cutoffDate.setDate(cutoffDate.getDate() - olderThanDays);

      const result = await notificationsAdapter.deleteMany({
        where: {
          userId,
          isRead: true,
          createdAt: { lt: cutoffDate },
        },
      });

      if (result.count > 0) {
        scopedLogger.info('Old notifications cleaned up', {
          userId,
          count: result.count,
          olderThanDays,
        });
      }

      return result.count;
    } catch (error) {
      scopedLogger.error('Failed to cleanup old notifications', { userId, error });
      throw error;
    }
  }

  /**
   * Send email notification based on user preferences
   *
   * @param userId - User ID
   * @param notification - Notification data for the email
   * @returns True if email was sent or user opted out (not an error)
   */
  async sendEmailNotification(
    userId: string,
    notification: {
      id: string;
      type: NotificationType;
      title: string;
      message: string;
      actionUrl?: string | null;
    }
  ): Promise<boolean> {
    try {
      // Fetch user with preferences
      const user = await prisma.users.findUnique({
        where: { id: userId },
        select: {
          id: true,
          email: true,
          name: true,
          user_preferences: {
            select: {
              language: true,
              emailNotifications: true,
            },
          },
        },
      });

      if (!user) {
        scopedLogger.warn('User not found for email notification', { userId });
        return false;
      }

      // Check if user has opted into email notifications for this type
      const emailPrefs = user.user_preferences?.emailNotifications as Record<string, boolean> | undefined;
      const notificationTypeToPreference: Record<NotificationType, string> = {
        new_booking: 'bookings',
        deposit_received: 'payments',
        new_conversation: 'bookings',
        booking_reminder: 'reminders',
        system_alert: 'security',
        integration_error: 'security',
        new_lead: 'leads',
      };

      const preferenceKey = notificationTypeToPreference[notification.type];
      const shouldSendEmail = emailPrefs?.[preferenceKey] ?? true; // Default to true if not set

      if (!shouldSendEmail) {
        scopedLogger.info('Email notification skipped - user opted out', {
          userId,
          notificationType: notification.type,
        });
        return true; // Not an error, just skipped
      }

      // Generate email template
      const lang = (user.user_preferences?.language as 'ar' | 'en') || 'en';
      const template = this.generateNotificationEmail(notification, user.name, lang);

      // Send email
      const sent = await sendEmail(user.email, template);

      if (!sent) {
        scopedLogger.error('Failed to send email notification', {
          userId,
          notificationType: notification.type,
        });
        return false;
      }

      scopedLogger.info('Email notification sent', {
        userId,
        notificationType: notification.type,
      });

      return true;
    } catch (error) {
      scopedLogger.error('Error sending email notification', {
        userId,
        notificationType: notification.type,
        error,
      });
      return false;
    }
  }

  /**
   * Create notification and optionally send email
   *
   * @param data - Notification data
   * @param sendEmailNotification - Whether to send email (default: true)
   * @returns Created notification
   */
  async createNotificationWithEmail(
    data: CreateNotificationData,
    sendEmailNotification: boolean = true
  ): Promise<Notification> {
    const notification = await this.createNotification(data);

    if (sendEmailNotification) {
      // Send email asynchronously - don't wait for it
      this.sendEmailNotification(data.userId, {
        id: notification.id,
        type: data.type,
        title: data.title,
        message: data.message,
        actionUrl: data.actionUrl,
      }).catch((error) => {
        scopedLogger.error('Background email send failed', { error });
      });
    }

    return notification;
  }

  /**
   * Generate email template for notifications
   */
  private generateNotificationEmail(
    notification: {
      type: NotificationType;
      title: string;
      message: string;
      actionUrl?: string | null;
    },
    recipientName: string,
    lang: 'ar' | 'en'
  ): { subject: string; html: string; text: string } {
    const isAr = lang === 'ar';
    const actionUrl = notification.actionUrl
      ? (notification.actionUrl.startsWith('http') ? notification.actionUrl : `${BASE_URL}${notification.actionUrl}`)
      : `${BASE_URL}/${lang}/dashboard`;

    // Type-specific styling
    const typeStyles: Record<NotificationType, { color: string; icon: string }> = {
      new_booking: { color: '#10b981', icon: '📅' },
      deposit_received: { color: '#3b82f6', icon: '💰' },
      new_conversation: { color: '#8b5cf6', icon: '💬' },
      booking_reminder: { color: '#f59e0b', icon: '⏰' },
      system_alert: { color: '#ef4444', icon: '⚠️' },
      integration_error: { color: '#ef4444', icon: '🔌' },
      new_lead: { color: '#06b6d4', icon: '🎯' },
    };

    const style = typeStyles[notification.type] || { color: '#10b981', icon: '🔔' };

    const subject = `${style.icon} ${notification.title} - Mawidi`;

    const html = isAr
      ? `
<!DOCTYPE html>
<html dir="rtl" lang="ar">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>${notification.title}</title>
</head>
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f3f4f6; direction: rtl;">
  <div style="max-width: 600px; margin: 0 auto; padding: 40px 20px;">
    <div style="background: white; border-radius: 16px; overflow: hidden; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);">
      <div style="background: linear-gradient(135deg, ${style.color} 0%, ${style.color}dd 100%); padding: 30px; text-align: center;">
        <div style="font-size: 48px; margin-bottom: 10px;">${style.icon}</div>
        <h1 style="margin: 0; color: white; font-size: 24px; font-weight: 600;">${notification.title}</h1>
      </div>
      <div style="padding: 30px;">
        <p style="margin: 0 0 20px; color: #374151; font-size: 16px; line-height: 1.6;">
          مرحباً ${recipientName}،
        </p>
        <div style="background: #f9fafb; border-radius: 12px; padding: 20px; margin: 20px 0; border-right: 4px solid ${style.color};">
          <p style="margin: 0; color: #374151; font-size: 16px; line-height: 1.6;">
            ${notification.message}
          </p>
        </div>
        <div style="text-align: center; margin: 30px 0;">
          <a href="${actionUrl}" style="display: inline-block; background: linear-gradient(135deg, ${style.color} 0%, ${style.color}dd 100%); color: white; padding: 14px 32px; text-decoration: none; border-radius: 10px; font-weight: 600; font-size: 16px;">
            عرض التفاصيل
          </a>
        </div>
      </div>
      <div style="padding: 20px 30px; background: #f9fafb; text-align: center;">
        <p style="margin: 0; color: #9ca3af; font-size: 12px;">فريق Mawidi</p>
        <p style="margin: 8px 0 0; color: #9ca3af; font-size: 11px;">يمكنك إدارة إعدادات الإشعارات من لوحة التحكم الخاصة بك</p>
      </div>
    </div>
  </div>
</body>
</html>`
      : `
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>${notification.title}</title>
</head>
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f3f4f6;">
  <div style="max-width: 600px; margin: 0 auto; padding: 40px 20px;">
    <div style="background: white; border-radius: 16px; overflow: hidden; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);">
      <div style="background: linear-gradient(135deg, ${style.color} 0%, ${style.color}dd 100%); padding: 30px; text-align: center;">
        <div style="font-size: 48px; margin-bottom: 10px;">${style.icon}</div>
        <h1 style="margin: 0; color: white; font-size: 24px; font-weight: 600;">${notification.title}</h1>
      </div>
      <div style="padding: 30px;">
        <p style="margin: 0 0 20px; color: #374151; font-size: 16px; line-height: 1.6;">
          Hello ${recipientName},
        </p>
        <div style="background: #f9fafb; border-radius: 12px; padding: 20px; margin: 20px 0; border-left: 4px solid ${style.color};">
          <p style="margin: 0; color: #374151; font-size: 16px; line-height: 1.6;">
            ${notification.message}
          </p>
        </div>
        <div style="text-align: center; margin: 30px 0;">
          <a href="${actionUrl}" style="display: inline-block; background: linear-gradient(135deg, ${style.color} 0%, ${style.color}dd 100%); color: white; padding: 14px 32px; text-decoration: none; border-radius: 10px; font-weight: 600; font-size: 16px;">
            View Details
          </a>
        </div>
      </div>
      <div style="padding: 20px 30px; background: #f9fafb; text-align: center;">
        <p style="margin: 0; color: #9ca3af; font-size: 12px;">Mawidi Team</p>
        <p style="margin: 8px 0 0; color: #9ca3af; font-size: 11px;">You can manage your notification settings from your dashboard</p>
      </div>
    </div>
  </div>
</body>
</html>`;

    const text = isAr
      ? `مرحباً ${recipientName}،\n\n${notification.message}\n\nلعرض التفاصيل: ${actionUrl}\n\nفريق Mawidi`
      : `Hello ${recipientName},\n\n${notification.message}\n\nView details: ${actionUrl}\n\nMawidi Team`;

    return { subject, html, text };
  }

  // ============================================================================
  // Convenience Functions for Common Notification Types
  // ============================================================================

  /**
   * Create a new booking notification
   */
  async notifyNewBooking(
    userId: string,
    customerName: string,
    appointmentDate: string,
    serviceName: string,
    bookingId: string
  ): Promise<Notification> {
    return this.createNotificationWithEmail({
      userId,
      type: 'new_booking',
      title: 'New Booking',
      message: `${customerName} has booked ${serviceName} for ${appointmentDate}`,
      actionUrl: `/dashboard/bookings/${bookingId}`,
      metadata: { bookingId, customerName, appointmentDate, serviceName },
    });
  }

  /**
   * Create a deposit received notification
   */
  async notifyDepositReceived(
    userId: string,
    customerName: string,
    amount: number,
    currency: string,
    bookingId: string
  ): Promise<Notification> {
    return this.createNotificationWithEmail({
      userId,
      type: 'deposit_received',
      title: 'Deposit Received',
      message: `${customerName} has paid a deposit of ${currency} ${amount.toFixed(2)}`,
      actionUrl: `/dashboard/bookings/${bookingId}`,
      metadata: { bookingId, customerName, amount, currency },
    });
  }

  /**
   * Create a new conversation notification
   */
  async notifyNewConversation(
    userId: string,
    customerName: string,
    customerPhone: string,
    conversationId: string
  ): Promise<Notification> {
    return this.createNotificationWithEmail({
      userId,
      type: 'new_conversation',
      title: 'New Conversation',
      message: `${customerName || customerPhone} started a new conversation`,
      actionUrl: `/dashboard/conversations/${conversationId}`,
      metadata: { conversationId, customerName, customerPhone },
    });
  }

  /**
   * Create a booking reminder notification
   */
  async notifyBookingReminder(
    userId: string,
    customerName: string,
    appointmentDate: string,
    appointmentTime: string,
    bookingId: string
  ): Promise<Notification> {
    return this.createNotificationWithEmail({
      userId,
      type: 'booking_reminder',
      title: 'Upcoming Appointment',
      message: `Reminder: ${customerName} has an appointment on ${appointmentDate} at ${appointmentTime}`,
      actionUrl: `/dashboard/bookings/${bookingId}`,
      metadata: { bookingId, customerName, appointmentDate, appointmentTime },
    });
  }

  /**
   * Create a system alert notification
   */
  async notifySystemAlert(
    userId: string,
    title: string,
    message: string,
    actionUrl?: string,
    metadata?: Record<string, unknown>
  ): Promise<Notification> {
    return this.createNotificationWithEmail({
      userId,
      type: 'system_alert',
      title,
      message,
      actionUrl,
      metadata,
    });
  }

  /**
   * Create an integration error notification
   */
  async notifyIntegrationError(
    userId: string,
    integrationName: string,
    errorMessage: string,
    integrationId?: string
  ): Promise<Notification> {
    return this.createNotificationWithEmail({
      userId,
      type: 'integration_error',
      title: `${integrationName} Integration Error`,
      message: errorMessage,
      actionUrl: '/dashboard/integrations',
      metadata: { integrationName, integrationId, errorMessage },
    });
  }

  /**
   * Delete multiple notifications
   */
  async deleteNotifications(notificationIds: string[], userId: string): Promise<number> {
    try {
      const result = await notificationsAdapter.deleteMany({
        where: {
          id: { in: notificationIds },
          userId,
        },
      });

      scopedLogger.info('Notifications deleted', {
        userId,
        count: result.count,
      });

      return result.count;
    } catch (error) {
      scopedLogger.error('Failed to delete notifications', {
        userId,
        count: notificationIds.length,
        error,
      });
      throw error;
    }
  }

  /**
   * Clear all notifications for a user
   */
  async clearAllNotifications(userId: string): Promise<number> {
    try {
      const result = await notificationsAdapter.deleteMany({
        where: { userId },
      });

      scopedLogger.info('All notifications cleared', {
        userId,
        count: result.count,
      });

      return result.count;
    } catch (error) {
      scopedLogger.error('Failed to clear all notifications', {
        userId,
        error,
      });
      throw error;
    }
  }
}

/**
 * Singleton instance
 */
export const notificationsService = new NotificationsService();

// ============================================================================
// Standalone Function Exports (for convenience)
// ============================================================================

/**
 * Create a notification
 */
export const createNotification = (data: CreateNotificationData) =>
  notificationsService.createNotification(data);

/**
 * Get notifications for a user
 */
export const getNotifications = (userId: string, options?: NotificationPaginationOptions) =>
  notificationsService.getNotifications(userId, options);

/**
 * Mark a single notification as read
 */
export const markAsRead = (notificationId: string, userId: string) =>
  notificationsService.markSingleAsRead(notificationId, userId);

/**
 * Mark all notifications as read
 */
export const markAllAsRead = (userId: string) =>
  notificationsService.markAllAsRead(userId);

/**
 * Get unread notification count
 */
export const getUnreadCount = (userId: string) =>
  notificationsService.getUnreadCount(userId);

/**
 * Delete a notification
 */
export const deleteNotification = (notificationId: string, userId: string) =>
  notificationsService.deleteNotification(notificationId, userId);

/**
 * Send email notification
 */
export const sendEmailNotification = (
  userId: string,
  notification: {
    id: string;
    type: NotificationType;
    title: string;
    message: string;
    actionUrl?: string | null;
  }
) => notificationsService.sendEmailNotification(userId, notification);
