/**
 * In-App Notification Service
 *
 * Creates and manages in-app notifications for users.
 * Optionally sends email notifications based on admin settings.
 */

import { prisma } from '@/lib/prisma'
import { sendStyledNotificationEmail } from '@/lib/email'

// Notification priority levels
export type NotificationPriority = 'low' | 'normal' | 'high' | 'urgent'

// Notification type constants
export const NotificationTypes = {
  // Admin notifications
  FILTERING_COMPLETE: 'FILTERING_COMPLETE',
  FILTERING_FAILED: 'FILTERING_FAILED',
  NEW_APPLICATION: 'NEW_APPLICATION',
  BULK_APPLICATIONS: 'BULK_APPLICATIONS',
  DOCUMENTS_UPLOADED: 'DOCUMENTS_UPLOADED',
  EVALUATION_MILESTONE: 'EVALUATION_MILESTONE',
  ALL_EVALUATIONS_DONE: 'ALL_EVALUATIONS_DONE',
  JURY_INACTIVE: 'JURY_INACTIVE',
  DEADLINE_24H: 'DEADLINE_24H',
  DEADLINE_1H: 'DEADLINE_1H',
  ROUND_AUTO_CLOSED: 'ROUND_AUTO_CLOSED',
  EXPORT_READY: 'EXPORT_READY',
  SYSTEM_ERROR: 'SYSTEM_ERROR',

  // Jury notifications
  ASSIGNED_TO_PROJECT: 'ASSIGNED_TO_PROJECT',
  BATCH_ASSIGNED: 'BATCH_ASSIGNED',
  PROJECT_UPDATED: 'PROJECT_UPDATED',
  ROUND_NOW_OPEN: 'ROUND_NOW_OPEN',
  REMINDER_3_DAYS: 'REMINDER_3_DAYS',
  REMINDER_24H: 'REMINDER_24H',
  REMINDER_1H: 'REMINDER_1H',
  ROUND_EXTENDED: 'ROUND_EXTENDED',
  ROUND_CLOSED: 'ROUND_CLOSED',
  THANK_YOU: 'THANK_YOU',
  RESULTS_AVAILABLE: 'RESULTS_AVAILABLE',

  // Jury - Award specific
  AWARD_JURY_SELECTED: 'AWARD_JURY_SELECTED',
  AWARD_VOTING_OPEN: 'AWARD_VOTING_OPEN',
  AWARD_REMINDER: 'AWARD_REMINDER',
  AWARD_RESULTS: 'AWARD_RESULTS',

  // Mentor notifications
  MENTEE_ASSIGNED: 'MENTEE_ASSIGNED',
  MENTEE_BATCH_ASSIGNED: 'MENTEE_BATCH_ASSIGNED',
  MENTEE_INTRO: 'MENTEE_INTRO',
  MENTEE_UPLOADED_DOCS: 'MENTEE_UPLOADED_DOCS',
  MENTEE_UPDATED_PROJECT: 'MENTEE_UPDATED_PROJECT',
  MENTEE_ADVANCED: 'MENTEE_ADVANCED',
  MENTEE_FINALIST: 'MENTEE_FINALIST',
  MENTEE_WON: 'MENTEE_WON',
  MENTEE_ELIMINATED: 'MENTEE_ELIMINATED',
  MENTORSHIP_TIP: 'MENTORSHIP_TIP',
  NEW_RESOURCE: 'NEW_RESOURCE',

  // Team/Applicant notifications
  APPLICATION_SUBMITTED: 'APPLICATION_SUBMITTED',
  APPLICATION_INCOMPLETE: 'APPLICATION_INCOMPLETE',
  TEAM_INVITE_RECEIVED: 'TEAM_INVITE_RECEIVED',
  TEAM_MEMBER_JOINED: 'TEAM_MEMBER_JOINED',
  TEAM_MEMBER_LEFT: 'TEAM_MEMBER_LEFT',
  DOCUMENTS_RECEIVED: 'DOCUMENTS_RECEIVED',
  REVIEW_IN_PROGRESS: 'REVIEW_IN_PROGRESS',
  ADVANCED_SEMIFINAL: 'ADVANCED_SEMIFINAL',
  ADVANCED_FINAL: 'ADVANCED_FINAL',
  MENTOR_ASSIGNED: 'MENTOR_ASSIGNED',
  MENTOR_MESSAGE: 'MENTOR_MESSAGE',
  NOT_SELECTED: 'NOT_SELECTED',
  FEEDBACK_AVAILABLE: 'FEEDBACK_AVAILABLE',
  EVENT_INVITATION: 'EVENT_INVITATION',
  WINNER_ANNOUNCEMENT: 'WINNER_ANNOUNCEMENT',
  CERTIFICATE_READY: 'CERTIFICATE_READY',
  PROGRAM_NEWSLETTER: 'PROGRAM_NEWSLETTER',

  // Observer notifications
  ROUND_STARTED: 'ROUND_STARTED',
  ROUND_PROGRESS: 'ROUND_PROGRESS',
  ROUND_COMPLETED: 'ROUND_COMPLETED',
  FINALISTS_ANNOUNCED: 'FINALISTS_ANNOUNCED',
  WINNERS_ANNOUNCED: 'WINNERS_ANNOUNCED',
  REPORT_AVAILABLE: 'REPORT_AVAILABLE',
} as const

export type NotificationType = (typeof NotificationTypes)[keyof typeof NotificationTypes]

// Notification icons by type
export const NotificationIcons: Record<string, string> = {
  [NotificationTypes.FILTERING_COMPLETE]: 'Brain',
  [NotificationTypes.FILTERING_FAILED]: 'AlertTriangle',
  [NotificationTypes.NEW_APPLICATION]: 'FileText',
  [NotificationTypes.BULK_APPLICATIONS]: 'Files',
  [NotificationTypes.DOCUMENTS_UPLOADED]: 'Upload',
  [NotificationTypes.ASSIGNED_TO_PROJECT]: 'ClipboardList',
  [NotificationTypes.ROUND_NOW_OPEN]: 'PlayCircle',
  [NotificationTypes.REMINDER_24H]: 'Clock',
  [NotificationTypes.REMINDER_1H]: 'AlertCircle',
  [NotificationTypes.ROUND_CLOSED]: 'Lock',
  [NotificationTypes.MENTEE_ASSIGNED]: 'Users',
  [NotificationTypes.MENTEE_ADVANCED]: 'TrendingUp',
  [NotificationTypes.MENTEE_WON]: 'Trophy',
  [NotificationTypes.APPLICATION_SUBMITTED]: 'CheckCircle',
  [NotificationTypes.ADVANCED_SEMIFINAL]: 'TrendingUp',
  [NotificationTypes.ADVANCED_FINAL]: 'Star',
  [NotificationTypes.MENTOR_ASSIGNED]: 'GraduationCap',
  [NotificationTypes.WINNER_ANNOUNCEMENT]: 'Trophy',
  [NotificationTypes.AWARD_VOTING_OPEN]: 'Vote',
  [NotificationTypes.AWARD_RESULTS]: 'Trophy',
}

// Priority by notification type
export const NotificationPriorities: Record<string, NotificationPriority> = {
  [NotificationTypes.FILTERING_COMPLETE]: 'high',
  [NotificationTypes.FILTERING_FAILED]: 'urgent',
  [NotificationTypes.DEADLINE_1H]: 'urgent',
  [NotificationTypes.REMINDER_1H]: 'urgent',
  [NotificationTypes.SYSTEM_ERROR]: 'urgent',
  [NotificationTypes.ASSIGNED_TO_PROJECT]: 'high',
  [NotificationTypes.ROUND_NOW_OPEN]: 'high',
  [NotificationTypes.DEADLINE_24H]: 'high',
  [NotificationTypes.REMINDER_24H]: 'high',
  [NotificationTypes.MENTEE_ASSIGNED]: 'high',
  [NotificationTypes.APPLICATION_SUBMITTED]: 'high',
  [NotificationTypes.ADVANCED_SEMIFINAL]: 'high',
  [NotificationTypes.ADVANCED_FINAL]: 'high',
  [NotificationTypes.WINNER_ANNOUNCEMENT]: 'high',
  [NotificationTypes.AWARD_VOTING_OPEN]: 'high',
}

interface CreateNotificationParams {
  userId: string
  type: string
  title: string
  message: string
  linkUrl?: string
  linkLabel?: string
  icon?: string
  priority?: NotificationPriority
  metadata?: Record<string, unknown>
  groupKey?: string
  expiresAt?: Date
}

/**
 * Create a single in-app notification
 */
export async function createNotification(
  params: CreateNotificationParams
): Promise<void> {
  const {
    userId,
    type,
    title,
    message,
    linkUrl,
    linkLabel,
    icon,
    priority,
    metadata,
    groupKey,
    expiresAt,
  } = params

  // Determine icon and priority if not provided
  const finalIcon = icon || NotificationIcons[type] || 'Bell'
  const finalPriority = priority || NotificationPriorities[type] || 'normal'

  // Check for existing notification with same groupKey (for batching)
  if (groupKey) {
    const existingNotification = await prisma.inAppNotification.findFirst({
      where: {
        userId,
        groupKey,
        isRead: false,
        createdAt: {
          gte: new Date(Date.now() - 60 * 60 * 1000), // Within last hour
        },
      },
    })

    if (existingNotification) {
      // Update existing notification instead of creating new one
      const existingMeta = existingNotification.metadata as Record<string, unknown> || {}
      const currentCount = (existingMeta.count as number) || 1
      await prisma.inAppNotification.update({
        where: { id: existingNotification.id },
        data: {
          message,
          metadata: { ...existingMeta, ...metadata, count: currentCount + 1 },
          createdAt: new Date(), // Bump to top
        },
      })
      return
    }
  }

  // Create the in-app notification
  await prisma.inAppNotification.create({
    data: {
      userId,
      type,
      title,
      message,
      linkUrl,
      linkLabel,
      icon: finalIcon,
      priority: finalPriority,
      metadata: metadata as object | undefined,
      groupKey,
      expiresAt,
    },
  })

  // Check if we should also send an email
  await maybeSendEmail(userId, type, title, message, linkUrl, metadata)
}

/**
 * Create notifications for multiple users
 */
export async function createBulkNotifications(params: {
  userIds: string[]
  type: string
  title: string
  message: string
  linkUrl?: string
  linkLabel?: string
  icon?: string
  priority?: NotificationPriority
  metadata?: Record<string, unknown>
}): Promise<void> {
  const {
    userIds,
    type,
    title,
    message,
    linkUrl,
    linkLabel,
    icon,
    priority,
    metadata,
  } = params

  const finalIcon = icon || NotificationIcons[type] || 'Bell'
  const finalPriority = priority || NotificationPriorities[type] || 'normal'

  // Create notifications in bulk
  await prisma.inAppNotification.createMany({
    data: userIds.map((userId) => ({
      userId,
      type,
      title,
      message,
      linkUrl,
      linkLabel,
      icon: finalIcon,
      priority: finalPriority,
      metadata: metadata as object | undefined,
    })),
  })

  // Check email settings and send emails
  for (const userId of userIds) {
    await maybeSendEmail(userId, type, title, message, linkUrl, metadata)
  }
}

/**
 * Notify all admin users
 */
export async function notifyAdmins(params: {
  type: string
  title: string
  message: string
  linkUrl?: string
  linkLabel?: string
  icon?: string
  priority?: NotificationPriority
  metadata?: Record<string, unknown>
}): Promise<void> {
  const admins = await prisma.user.findMany({
    where: {
      role: { in: ['SUPER_ADMIN', 'PROGRAM_ADMIN'] },
      status: 'ACTIVE',
    },
    select: { id: true },
  })

  if (admins.length === 0) return

  await createBulkNotifications({
    ...params,
    userIds: admins.map((a) => a.id),
  })
}

/**
 * Notify all jury members for a specific round
 */
export async function notifyRoundJury(
  roundId: string,
  params: Omit<CreateNotificationParams, 'userId'>
): Promise<void> {
  const assignments = await prisma.assignment.findMany({
    where: { roundId },
    select: { userId: true },
    distinct: ['userId'],
  })

  if (assignments.length === 0) return

  await createBulkNotifications({
    ...params,
    userIds: assignments.map((a) => a.userId),
  })
}

/**
 * Notify team members of a project
 */
export async function notifyProjectTeam(
  projectId: string,
  params: Omit<CreateNotificationParams, 'userId'>
): Promise<void> {
  const teamMembers = await prisma.teamMember.findMany({
    where: { projectId },
    include: { user: { select: { id: true } } },
  })

  const userIds = teamMembers
    .filter((tm) => tm.user)
    .map((tm) => tm.user!.id)

  if (userIds.length === 0) return

  await createBulkNotifications({
    ...params,
    userIds,
  })
}

/**
 * Notify assigned mentors of a project
 */
export async function notifyProjectMentors(
  projectId: string,
  params: Omit<CreateNotificationParams, 'userId'>
): Promise<void> {
  const mentorAssignments = await prisma.mentorAssignment.findMany({
    where: { projectId },
    select: { mentorId: true },
  })

  if (mentorAssignments.length === 0) return

  await createBulkNotifications({
    ...params,
    userIds: mentorAssignments.map((ma) => ma.mentorId),
  })
}

/**
 * Check email settings and send email if enabled
 */
async function maybeSendEmail(
  userId: string,
  type: string,
  title: string,
  message: string,
  linkUrl?: string,
  metadata?: Record<string, unknown>
): Promise<void> {
  try {
    // Check if email is enabled for this notification type
    const emailSetting = await prisma.notificationEmailSetting.findUnique({
      where: { notificationType: type },
    })

    // If no setting exists, don't send email by default
    if (!emailSetting || !emailSetting.sendEmail) {
      return
    }

    // Check user's notification preference
    const user = await prisma.user.findUnique({
      where: { id: userId },
      select: { email: true, name: true, notificationPreference: true },
    })

    if (!user || user.notificationPreference === 'NONE') {
      return
    }

    // Send styled email with full context
    // The styled template will use metadata for rich content
    // Subject can be overridden by admin settings
    await sendStyledNotificationEmail(
      user.email,
      user.name || 'User',
      type,
      {
        title,
        message,
        linkUrl,
        metadata,
      },
      emailSetting.emailSubject || undefined
    )
  } catch (error) {
    // Log but don't fail the notification creation
    console.error('[Notification] Failed to send email:', error)
  }
}

/**
 * Mark a notification as read
 */
export async function markNotificationAsRead(
  notificationId: string,
  userId: string
): Promise<void> {
  await prisma.inAppNotification.updateMany({
    where: { id: notificationId, userId },
    data: { isRead: true, readAt: new Date() },
  })
}

/**
 * Mark all notifications as read for a user
 */
export async function markAllNotificationsAsRead(userId: string): Promise<void> {
  await prisma.inAppNotification.updateMany({
    where: { userId, isRead: false },
    data: { isRead: true, readAt: new Date() },
  })
}

/**
 * Get unread notification count for a user
 */
export async function getUnreadCount(userId: string): Promise<number> {
  return prisma.inAppNotification.count({
    where: { userId, isRead: false },
  })
}

/**
 * Delete expired notifications
 */
export async function deleteExpiredNotifications(): Promise<number> {
  const result = await prisma.inAppNotification.deleteMany({
    where: {
      expiresAt: { lt: new Date() },
    },
  })
  return result.count
}

/**
 * Delete old read notifications (cleanup job)
 */
export async function deleteOldNotifications(olderThanDays: number): Promise<number> {
  const cutoffDate = new Date()
  cutoffDate.setDate(cutoffDate.getDate() - olderThanDays)

  const result = await prisma.inAppNotification.deleteMany({
    where: {
      isRead: true,
      createdAt: { lt: cutoffDate },
    },
  })
  return result.count
}
