import { and, count, desc, eq, inArray, lte } from "drizzle-orm";
import { getDb } from "../config/database.js";
import {
  notifications,
  type NewNotification,
  type Notification,
} from "./notifications.schema.js";

/**
 * NotificationsRepository —— 通知数据访问层。
 *
 * 职责：封装 MySQL 读写，与业务逻辑解耦。
 * 仲裁依据 G10：使用 getDb() 函数式获取 db 实例。
 */

export async function insertNotification(
  row: NewNotification,
): Promise<Notification> {
  const db = getDb();
  await db.insert(notifications).values(row);
  return row as Notification;
}

export async function insertNotifications(
  rows: NewNotification[],
): Promise<void> {
  if (rows.length === 0) return;
  const db = getDb();
  await db.insert(notifications).values(rows);
}

export async function findById(id: string): Promise<Notification | undefined> {
  const db = getDb();
  const [row] = await db
    .select()
    .from(notifications)
    .where(eq(notifications.id, id))
    .limit(1);
  return row;
}

export async function findByEventId(
  eventId: string,
): Promise<Notification | undefined> {
  const db = getDb();
  const [row] = await db
    .select()
    .from(notifications)
    .where(eq(notifications.eventId, eventId))
    .limit(1);
  return row;
}

export async function listByUser(
  userId: string,
  options: {
    onlyUnread?: boolean;
    type?: string;
    page: number;
    pageSize: number;
  },
): Promise<{ items: Notification[]; total: number }> {
  const db = getDb();
  const conditions = [eq(notifications.userId, userId)];

  if (options.onlyUnread) {
    conditions.push(eq(notifications.isRead, false));
  }
  if (options.type) {
    conditions.push(
      eq(notifications.type, options.type as Notification["type"]),
    );
  }

  const where = and(...conditions);
  const offset = (options.page - 1) * options.pageSize;

  const items = await db
    .select()
    .from(notifications)
    .where(where)
    .orderBy(desc(notifications.createdAt))
    .limit(options.pageSize)
    .offset(offset);

  const [countRow] = await db
    .select({ value: count() })
    .from(notifications)
    .where(where);

  return { items, total: countRow?.value ?? 0 };
}

export async function getUnreadCount(userId: string): Promise<number> {
  const db = getDb();
  const [row] = await db
    .select({ value: count() })
    .from(notifications)
    .where(
      and(eq(notifications.userId, userId), eq(notifications.isRead, false)),
    );
  return row?.value ?? 0;
}

export async function markAsRead(id: string, userId: string): Promise<void> {
  const db = getDb();
  await db
    .update(notifications)
    .set({ isRead: true, readAt: new Date(), status: "read" })
    .where(and(eq(notifications.id, id), eq(notifications.userId, userId)));
}

export async function batchMarkAsRead(
  ids: string[],
  userId: string,
): Promise<void> {
  const db = getDb();
  await db
    .update(notifications)
    .set({ isRead: true, readAt: new Date(), status: "read" })
    .where(
      and(inArray(notifications.id, ids), eq(notifications.userId, userId)),
    );
}

export async function markAllAsRead(
  userId: string,
  before?: number,
): Promise<number> {
  const db = getDb();
  const conditions = [
    eq(notifications.userId, userId),
    eq(notifications.isRead, false),
  ];
  if (before !== undefined) {
    conditions.push(lte(notifications.createdAt, new Date(before)));
  }
  const result = await db
    .update(notifications)
    .set({ isRead: true, readAt: new Date(), status: "read" })
    .where(and(...conditions));

  // mysql2 affectedRows
  return (result as unknown as { affectedRows?: number }).affectedRows ?? 0;
}

export async function recallByGroup(groupId: string): Promise<number> {
  const db = getDb();
  const result = await db
    .update(notifications)
    .set({ status: "recalled" })
    .where(eq(notifications.groupId, groupId));

  return (result as unknown as { affectedRows?: number }).affectedRows ?? 0;
}

export async function deleteById(id: string): Promise<void> {
  const db = getDb();
  await db.delete(notifications).where(eq(notifications.id, id));
}

/** 用于测试/重置 */
export async function deleteAllByUserId(userId: string): Promise<void> {
  const db = getDb();
  await db.delete(notifications).where(eq(notifications.userId, userId));
}
