import { prisma } from '../../config/database';
import type { NotificationType, Prisma } from '@prisma/client';
import { sseService } from './sse.service';

/** Maps NotificationType → NotificationPreferences field name */
const TYPE_TO_PREF: Record<string, string> = {
  friend_request: 'enableFriendRequests',
  friend_accepted: 'enableFriendRequests',
  poke: 'enableFriendRequests',
  comment: 'enableComments',
  upload_approved: 'enableUploadApprovals',
  upload_rejected: 'enableUploadApprovals',
  achievement: 'enableAchievements',
  system: 'enableSystemUpdates',
  group_call: 'enableSystemUpdates',
  impact_story: 'enableSystemUpdates',
  referral_completed: 'enableSystemUpdates',
  challenge_update: 'enableSystemUpdates',
  shared_view_invite: 'enableFriendRequests',
  shared_view_accepted: 'enableFriendRequests',
  calendar_event_invite: 'enableFriendRequests',
  // Operational notification types
  shift_signup_confirmed: 'enableSystemUpdates',
  shift_reminder: 'enableSystemUpdates',
  shift_cancelled: 'enableSystemUpdates',
  canvass_session_summary: 'enableSystemUpdates',
  reengagement: 'enableSystemUpdates',
  // Straw poll notification types
  poll_closed: 'enableSystemUpdates',
  poll_results_available: 'enableSystemUpdates',
  poll_challenge: 'enableFriendRequests',
};

export const notificationService = {
  /** Create a notification (respects user preferences) */
  async createNotification(
    userId: string,
    type: NotificationType,
    title: string,
    message: string,
    metadata?: Record<string, unknown>,
  ) {
    // Check user preferences — skip if they've disabled this type
    const prefs = await prisma.notificationPreferences.findUnique({ where: { userId } });
    if (prefs) {
      const prefField = TYPE_TO_PREF[type];
      if (prefField && (prefs as Record<string, unknown>)[prefField] === false) {
        return null; // User opted out
      }
    }

    const notification = await prisma.notification.create({
      data: {
        userId,
        type,
        title,
        message,
        metadata: metadata ? (metadata as Prisma.InputJsonValue) : undefined,
      },
    });

    // Push via SSE if user is connected (real-time delivery)
    sseService.sendToUser(userId, 'notification', {
      id: notification.id,
      type: notification.type,
      title: notification.title,
      message: notification.message,
      metadata: notification.metadata,
      createdAt: notification.createdAt,
    });

    return notification;
  },

  /** List notifications (paginated, optional unread filter) */
  async listNotifications(userId: string, page: number, limit: number, unreadOnly: boolean) {
    const where: Prisma.NotificationWhereInput = { userId };
    if (unreadOnly) where.isRead = false;

    const skip = (page - 1) * limit;

    const [notifications, total] = await Promise.all([
      prisma.notification.findMany({
        where,
        orderBy: { createdAt: 'desc' },
        skip,
        take: limit,
      }),
      prisma.notification.count({ where }),
    ]);

    return {
      notifications,
      pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
    };
  },

  /** Get unread notification count */
  async getUnreadCount(userId: string): Promise<number> {
    return prisma.notification.count({
      where: { userId, isRead: false },
    });
  },

  /** Mark a single notification as read */
  async markAsRead(userId: string, notificationId: number) {
    const notification = await prisma.notification.findFirst({
      where: { id: notificationId, userId },
    });
    if (!notification) {
      throw Object.assign(new Error('Notification not found'), { statusCode: 404 });
    }

    return prisma.notification.update({
      where: { id: notificationId },
      data: { isRead: true, readAt: new Date() },
    });
  },

  /** Mark all notifications as read */
  async markAllAsRead(userId: string) {
    const result = await prisma.notification.updateMany({
      where: { userId, isRead: false },
      data: { isRead: true, readAt: new Date() },
    });
    return { marked: result.count };
  },

  /** Delete a notification */
  async deleteNotification(userId: string, notificationId: number) {
    const notification = await prisma.notification.findFirst({
      where: { id: notificationId, userId },
    });
    if (!notification) {
      throw Object.assign(new Error('Notification not found'), { statusCode: 404 });
    }

    await prisma.notification.delete({ where: { id: notificationId } });
    return { success: true };
  },

  /** Get notification preferences (auto-create with defaults) */
  async getPreferences(userId: string) {
    let prefs = await prisma.notificationPreferences.findUnique({ where: { userId } });
    if (!prefs) {
      prefs = await prisma.notificationPreferences.create({
        data: { userId },
      });
    }
    return prefs;
  },

  /** Update notification preferences */
  async updatePreferences(userId: string, data: Record<string, boolean | string>) {
    return prisma.notificationPreferences.upsert({
      where: { userId },
      update: { ...data, updatedAt: new Date() },
      create: { userId, ...data },
    });
  },
};
