/**
 * Notifications Domain Adapter
 *
 * Routes notifications CRUD to Prisma or Convex based on data-source config.
 * Maintains Prisma-compatible method signatures so service layer changes are minimal.
 */

import { prisma } from "@/lib/db";
import { convexQuery, convexMutation } from "@/lib/convex-client";
import { getDataSource } from "@/lib/data-source";
import {
  convexToPrismaShape,
  convexToPrismaShapeMany,
  applyPagination,
  type ConvexDoc,
  type BatchResult,
} from "./base";

const DOMAIN = "notifications" as const;
const TIMESTAMP_FIELDS = ["createdAt", "readAt"];

function isConvex(): boolean {
  return getDataSource(DOMAIN) === "convex";
}

// ─── findMany ────────────────────────────────────────────────────────────

export async function findMany(args: {
  where?: Record<string, unknown>;
  orderBy?: Record<string, string> | Record<string, string>[];
  take?: number;
  skip?: number;
  select?: Record<string, boolean>;
}) {
  if (!isConvex()) {
    return prisma.notifications.findMany(args as any);
  }

  const where = (args.where || {}) as Record<string, unknown>;
  const userId = where.userId as string | undefined;
  const isRead = where.isRead as boolean | undefined;

  // Convex query
  const docs =
    (await convexQuery<ConvexDoc[]>("notifications/queries:getNotifications", {
      userId: userId || "",
      limit: (args.take || 100) + (args.skip || 0), // Over-fetch for skip
      ...(isRead !== undefined ? { isRead } : {}),
    })) || [];

  // Apply skip pagination (Convex doesn't support skip natively)
  const paginated = applyPagination(docs, args.skip, args.take);

  return convexToPrismaShapeMany(paginated, TIMESTAMP_FIELDS);
}

// ─── findFirst ───────────────────────────────────────────────────────────

export async function findFirst(args: { where?: Record<string, unknown> }) {
  if (!isConvex()) {
    return prisma.notifications.findFirst(args as any);
  }

  const where = (args.where || {}) as Record<string, unknown>;
  const userId = where.userId as string;
  const docs =
    (await convexQuery<ConvexDoc[]>("notifications/queries:getNotifications", {
      userId,
      limit: 100,
    })) || [];

  // Filter by all where conditions
  const match = docs.find((doc) => {
    for (const [key, value] of Object.entries(where)) {
      const docVal = key === "id" ? doc.prismaId || doc._id : doc[key];
      if (docVal !== value) return false;
    }
    return true;
  });

  return match ? convexToPrismaShape(match, TIMESTAMP_FIELDS) : null;
}

// ─── findUnique ──────────────────────────────────────────────────────────

export async function findUnique(args: { where: { id: string } }) {
  if (!isConvex()) {
    return prisma.notifications.findUnique(args as any);
  }

  // Convex doesn't have a direct getById for notifications, use findFirst pattern
  return findFirst({ where: args.where });
}

// ─── count ───────────────────────────────────────────────────────────────

export async function count(args?: { where?: Record<string, unknown> }) {
  if (!isConvex()) {
    return prisma.notifications.count(args as any);
  }

  const where = (args?.where || {}) as Record<string, unknown>;
  const userId = where.userId as string;
  const isRead = where.isRead as boolean | undefined;

  // If counting unread specifically, use the dedicated query
  if (isRead === false) {
    return (
      (await convexQuery<number>("notifications/queries:getUnreadCount", {
        userId,
      })) ?? 0
    );
  }

  // Otherwise, fetch and count
  const docs =
    (await convexQuery<ConvexDoc[]>("notifications/queries:getNotifications", {
      userId,
      limit: 10000,
      ...(isRead !== undefined ? { isRead } : {}),
    })) || [];

  return docs.length;
}

// ─── create ──────────────────────────────────────────────────────────────

export async function create(args: {
  data: {
    id?: string;
    userId: string;
    type: string;
    title: string;
    message: string;
    actionUrl?: string | null;
    isRead?: boolean;
    metadata?: unknown;
  };
}) {
  if (!isConvex()) {
    return prisma.notifications.create(args as any);
  }

  const d = args.data;
  const convexId = await convexMutation<string>(
    "notifications/mutations:create",
    {
      userId: d.userId,
      type: d.type,
      title: d.title,
      message: d.message,
      ...(d.actionUrl ? { actionUrl: d.actionUrl } : {}),
      ...(d.metadata !== undefined ? { metadata: d.metadata } : {}),
    },
  );

  // Return Prisma-compatible shape
  return {
    id: d.id || convexId || "",
    userId: d.userId,
    type: d.type,
    title: d.title,
    message: d.message,
    actionUrl: d.actionUrl ?? null,
    isRead: d.isRead ?? false,
    readAt: null,
    metadata: d.metadata ?? null,
    createdAt: new Date(),
  };
}

// ─── update ──────────────────────────────────────────────────────────────

export async function update(args: {
  where: { id: string };
  data: Record<string, unknown>;
}) {
  if (!isConvex()) {
    return prisma.notifications.update(args as any);
  }

  // For marking as read
  if (args.data.isRead === true) {
    await convexMutation("notifications/mutations:markRead", {
      id: args.where.id,
    });
  }

  // Return updated shape (best-effort since Convex mutations don't return docs)
  const existing = await findFirst({ where: { id: args.where.id } });
  return existing ? { ...existing, ...args.data } : null;
}

// ─── updateMany ──────────────────────────────────────────────────────────

export async function updateMany(args: {
  where: Record<string, unknown>;
  data: Record<string, unknown>;
}): Promise<BatchResult> {
  if (!isConvex()) {
    return prisma.notifications.updateMany(args as any);
  }

  const where = args.where;
  const userId = where.userId as string;

  // Special case: mark all as read
  if (args.data.isRead === true && userId) {
    const result = await convexMutation<{ updated: number }>(
      "notifications/mutations:markAllRead",
      { userId },
    );
    return { count: result?.updated ?? 0 };
  }

  // General updateMany: fetch matching docs, update each
  const docs =
    (await convexQuery<ConvexDoc[]>("notifications/queries:getNotifications", {
      userId,
      limit: 10000,
    })) || [];

  // Filter by where conditions
  const inIds = (where.id as { in?: string[] })?.in;
  let matching = docs;
  if (inIds) {
    const idSet = new Set(inIds);
    matching = docs.filter((d) => idSet.has((d.prismaId as string) || d._id));
  }
  if (where.isRead !== undefined) {
    matching = matching.filter((d) => d.isRead === where.isRead);
  }

  // Update each
  let updatedCount = 0;
  for (const doc of matching) {
    if (args.data.isRead === true) {
      await convexMutation("notifications/mutations:markRead", { id: doc._id });
      updatedCount++;
    }
  }

  return { count: updatedCount };
}

// ─── delete ──────────────────────────────────────────────────────────────

async function deleteOne(args: { where: { id: string } }) {
  if (!isConvex()) {
    return prisma.notifications.delete(args as any);
  }

  await convexMutation("notifications/mutations:deleteNotification", {
    id: args.where.id,
  });

  return { id: args.where.id };
}

export { deleteOne as delete };

// ─── deleteMany ──────────────────────────────────────────────────────────

export async function deleteMany(args: {
  where: Record<string, unknown>;
}): Promise<BatchResult> {
  if (!isConvex()) {
    return prisma.notifications.deleteMany(args as any);
  }

  const where = args.where;
  const userId = where.userId as string;

  // Fetch matching docs
  const docs =
    (await convexQuery<ConvexDoc[]>("notifications/queries:getNotifications", {
      userId,
      limit: 10000,
    })) || [];

  // Filter
  let matching = docs;
  const inIds = (where.id as { in?: string[] })?.in;
  if (inIds) {
    const idSet = new Set(inIds);
    matching = docs.filter((d) => idSet.has((d.prismaId as string) || d._id));
  }
  if (where.isRead !== undefined) {
    matching = matching.filter((d) => d.isRead === where.isRead);
  }
  if (where.createdAt && typeof where.createdAt === "object") {
    const lt = (where.createdAt as { lt?: Date }).lt;
    if (lt) {
      const ltMs = lt.getTime();
      matching = matching.filter((d) => (d.createdAt as number) < ltMs);
    }
  }

  // Delete each
  for (const doc of matching) {
    await convexMutation("notifications/mutations:deleteNotification", {
      id: doc._id,
    });
  }

  return { count: matching.length };
}

// ─── Namespace export ────────────────────────────────────────────────────

export const notificationsAdapter = {
  findMany,
  findFirst,
  findUnique,
  count,
  create,
  update,
  updateMany,
  delete: deleteOne,
  deleteMany,
};
