import prisma from "../db.server";

export interface CreateNotificationInput {
  shopId?: string | null;
  userId?: string | null;
  type: string;
  title: string;
  message: string;
  actionUrl?: string | null;
  metadata?: Record<string, any> | null;
}

export interface GetNotificationsParams {
  shopId?: string | null;
  page?: number;
  pageSize?: number;
  isRead?: boolean;
}

/**
 * Creates a persistent system/tenant notification in the database.
 */
export async function createNotification(input: CreateNotificationInput) {
  const { shopId, userId, type, title, message, actionUrl, metadata } = input;

  return await prisma.notification.create({
    data: {
      shopId: shopId || null,
      userId: userId || null,
      type: type || "SYSTEM_ALERT",
      title,
      message,
      actionUrl: actionUrl || null,
      metadata: metadata || undefined,
      isRead: false,
    },
  });
}

/**
 * Retrieves paginated notifications with optional tenant and read status filters.
 * Scoped by shopId to enforce strict multi-tenant isolation.
 */
export async function getNotifications(params: GetNotificationsParams = {}) {
  const { shopId, page = 1, pageSize = 20, isRead } = params;

  // Strict tenant scoping: If shopId is missing, empty, or undefined, require explicit scoping
  if (!shopId) {
    throw new Error("Tenant shopId is required to retrieve notifications.");
  }

  const whereClause: any = {};

  if (shopId !== "global") {
    whereClause.OR = [{ shopId }, { shopId: null }];
  }

  if (typeof isRead === "boolean") {
    whereClause.isRead = isRead;
  }

  const totalCount = await prisma.notification.count({ where: whereClause });
  const totalPages = Math.max(1, Math.ceil(totalCount / pageSize));
  const skip = (page - 1) * pageSize;

  const notifications = await prisma.notification.findMany({
    where: whereClause,
    orderBy: { createdAt: "desc" },
    skip,
    take: pageSize,
  });

  return {
    notifications,
    totalCount,
    page,
    pageSize,
    totalPages,
  };
}

/**
 * Retrieves total unread notification count for a tenant/shop.
 */
export async function getUnreadCount(shopId?: string | null): Promise<number> {
  if (!shopId) {
    return 0;
  }

  const whereClause: any = { isRead: false };

  if (shopId !== "global") {
    whereClause.OR = [{ shopId }, { shopId: null }];
  }

  return await prisma.notification.count({ where: whereClause });
}

/**
 * Marks a single notification as read.
 * Scopes by shopId to prevent cross-tenant modification.
 */
export async function markNotificationAsRead(id: string, shopId?: string | null) {
  if (!shopId) {
    throw new Error("Tenant shopId or 'global' scope is required.");
  }

  const existing = await prisma.notification.findUnique({ where: { id } });

  if (!existing) {
    throw new Error("Notification not found.");
  }

  // Security check for tenant ownership
  if (existing.shopId && existing.shopId !== shopId && shopId !== "global") {
    throw new Error("Unauthorized to modify this notification.");
  }

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

/**
 * Marks all notifications for a tenant/shop as read.
 */
export async function markAllNotificationsAsRead(shopId?: string | null) {
  if (!shopId) {
    throw new Error("Tenant shopId or 'global' scope is required to mark notifications as read.");
  }

  const whereClause: any = { isRead: false };

  if (shopId !== "global") {
    whereClause.OR = [{ shopId }, { shopId: null }];
  }

  return await prisma.notification.updateMany({
    where: whereClause,
    data: {
      isRead: true,
      readAt: new Date(),
    },
  });
}

/**
 * Deletes a notification by ID with security checks.
 */
export async function deleteNotification(id: string, shopId?: string | null) {
  const existing = await prisma.notification.findUnique({ where: { id } });

  if (!existing) {
    throw new Error("Notification not found.");
  }

  if (shopId && existing.shopId && existing.shopId !== shopId && shopId !== "global") {
    throw new Error("Unauthorized to delete this notification.");
  }

  return await prisma.notification.delete({ where: { id } });
}
