import "server-only"

/**
 * 私信数据访问层
 *
 * 职责:
 * - getMessages / getMessageById / getMessageThread: 私信查询
 * - createMessage / markMessageAsRead / deleteMessage: 私信 CRUD
 * - getUnreadMessageCount: 未读私信计数
 * - getRecipients: 获取收件人列表（按 DataScope 过滤）
 * - isReceiverAllowed: 校验收件人是否在 sender 的 DataScope 内（P0-1 安全二次校验）
 *
 * 通知相关函数（createNotification / getNotifications /
 * markNotificationAsRead / markAllNotificationsAsRead / getUnreadNotificationCount）
 * 已迁移到 notifications/data-access.ts，请直接从该模块导入。
 *
 * 变更历史：
 * - P0-2: getMessageThread 增加 userId 参数，做权限校验
 * - P0-3: getMessagesPageData 编排迁出（移至 messages/page.tsx 页面层）
 */

import { cache } from "react"
import { createId } from "@paralleldrive/cuid2"
import { and, count, desc, eq, gte, inArray, isNull, like, lte, or, type SQL } from "drizzle-orm"

import { db } from "@/shared/db"
import {
  messages,
  messageDrafts,
  messageTemplates,
  messageReports,
  userBlocks,
  users,
} from "@/shared/db/schema"
import {
  getClassesByGradeId,
  getStudentIdsByClassIds,
  getTeacherIdsByClassIds,
  getStudentActiveClassId,
} from "@/modules/classes/data-access"
import { getParentIdsByStudentIds } from "@/modules/parent/data-access"
import { getUserNamesByIds } from "@/modules/users/data-access"
import { getFileAttachmentsByTarget } from "@/modules/files/data-access"
import type { DataScope } from "@/shared/types/permissions"
import type { PaginatedResult } from "@/modules/notifications/types"
import type {
  Message,
  GetMessagesParams,
  CreateMessageInput,
  RecipientOption,
  RecipientRole,
  MessageDraft,
  CreateMessageDraftInput,
  UpdateMessageDraftInput,
  MessageTemplate,
  CreateMessageTemplateInput,
  UpdateMessageTemplateInput,
  RecipientResolverContext,
  MessagingRoleConfig,
  MessageReport,
  CreateMessageReportInput,
  MessageReportReason,
  UserBlock,
  MessageAttachment,
} from "./types"

const toIso = (d: Date | null | undefined): string | null => (d ? d.toISOString() : null)

const toIsoRequired = (d: Date): string => d.toISOString()

interface MessageRow {
  id: string
  senderId: string
  receiverId: string
  subject: string | null
  content: string
  isRead: boolean
  isStarred: boolean
  recalledAt: Date | null
  readAt: Date | null
  parentMessageId: string | null
  groupMessageId: string | null
  createdAt: Date
}

async function resolveUserNames(userIds: string[]): Promise<Map<string, string>> {
  const uniqueIds = [...new Set(userIds)].filter(Boolean)
  if (uniqueIds.length === 0) return new Map()
  const rows = await db
    .select({ id: users.id, name: users.name })
    .from(users)
    .where(inArray(users.id, uniqueIds))
  return new Map(rows.map((r) => [r.id, r.name ?? r.id]))
}

const mapMessage = (r: MessageRow, nameMap: Map<string, string>): Message => ({
  id: r.id,
  senderId: r.senderId,
  senderName: nameMap.get(r.senderId) ?? null,
  receiverId: r.receiverId,
  receiverName: nameMap.get(r.receiverId) ?? null,
  subject: r.subject,
  content: r.content,
  isRead: r.isRead,
  isStarred: r.isStarred,
  recalledAt: toIso(r.recalledAt),
  readAt: toIso(r.readAt),
  parentMessageId: r.parentMessageId,
  groupMessageId: r.groupMessageId,
  createdAt: toIsoRequired(r.createdAt),
})

export const getMessages = cache(
  async (params: GetMessagesParams): Promise<PaginatedResult<Message>> => {
    const page = Math.max(1, params.page ?? 1)
    const pageSize = Math.max(1, params.pageSize ?? 20)
    const offset = (page - 1) * pageSize

    const conds: SQL[] = []
    if (params.type === "inbox") {
      conds.push(eq(messages.receiverId, params.userId))
      conds.push(isNull(messages.receiverDeletedAt))
    } else if (params.type === "sent") {
      conds.push(eq(messages.senderId, params.userId))
      conds.push(isNull(messages.senderDeletedAt))
    } else {
      // all: 仅返回当前用户未删除的消息（发送方未删 或 接收方未删）
      const cond = or(
        and(eq(messages.receiverId, params.userId), isNull(messages.receiverDeletedAt)),
        and(eq(messages.senderId, params.userId), isNull(messages.senderDeletedAt))
      )
      if (cond) conds.push(cond)
    }

    // 关键词搜索（匹配 subject 或 content）
    if (params.keyword && params.keyword.trim().length > 0) {
      const kw = `%${params.keyword.trim()}%`
      const kwCond = or(like(messages.subject, kw), like(messages.content, kw))
      if (kwCond) conds.push(kwCond)
    }

    // V2-P2-13c: 仅返回星标消息
    if (params.starredOnly) {
      conds.push(eq(messages.isStarred, true))
    }

    // P2-11: 按发件人 ID 筛选
    if (params.senderId) {
      conds.push(eq(messages.senderId, params.senderId))
    }

    // P2-11: 按日期范围筛选
    if (params.dateFrom) {
      const fromDate = new Date(params.dateFrom)
      if (!isNaN(fromDate.getTime())) {
        conds.push(gte(messages.createdAt, fromDate))
      }
    }
    if (params.dateTo) {
      const toDate = new Date(params.dateTo)
      if (!isNaN(toDate.getTime())) {
        conds.push(lte(messages.createdAt, toDate))
      }
    }

    // P2-11: 按已读状态筛选
    if (params.isRead === true) {
      conds.push(eq(messages.isRead, true))
    } else if (params.isRead === false) {
      conds.push(eq(messages.isRead, false))
    }

    const where = and(...conds)
    const [rows, [totalRow]] = await Promise.all([
      db.select().from(messages).where(where).orderBy(desc(messages.createdAt)).limit(pageSize).offset(offset),
      db.select({ value: count() }).from(messages).where(where),
    ])

    const userIds = rows.flatMap((r) => [r.senderId, r.receiverId])
    const nameMap = await resolveUserNames(userIds)
    const total = Number(totalRow?.value ?? 0)
    return { items: rows.map((r) => mapMessage(r, nameMap)), total, page, pageSize, totalPages: Math.ceil(total / pageSize) }
  }
)

export const getMessageById = cache(
  async (id: string, userId: string): Promise<Message | null> => {
    const [row] = await db
      .select()
      .from(messages)
      .where(
        and(
          eq(messages.id, id),
          or(
            and(eq(messages.senderId, userId), isNull(messages.senderDeletedAt)),
            and(eq(messages.receiverId, userId), isNull(messages.receiverDeletedAt))
          )
        )
      )
      .limit(1)
    if (!row) return null
    const nameMap = await resolveUserNames([row.senderId, row.receiverId])
    return mapMessage(row, nameMap)
  }
)

export const getMessageThread = cache(async (messageId: string, userId: string): Promise<Message[]> => {
  // P0-2: 校验当前用户对根消息有访问权（必须是发送方或接收方）
  const [root] = await db
    .select()
    .from(messages)
    .where(
      and(
        eq(messages.id, messageId),
        or(eq(messages.senderId, userId), eq(messages.receiverId, userId))
      )
    )
    .limit(1)
  if (!root) return []

  const replies = await db
    .select()
    .from(messages)
    .where(eq(messages.parentMessageId, messageId))
    .orderBy(desc(messages.createdAt))

  const allRows = [root, ...replies]
  const nameMap = await resolveUserNames(allRows.flatMap((r) => [r.senderId, r.receiverId]))
  return allRows.map((r) => mapMessage(r, nameMap))
})

export async function createMessage(data: CreateMessageInput): Promise<string> {
  const id = createId()
  await db.insert(messages).values({
    id,
    senderId: data.senderId,
    receiverId: data.receiverId,
    subject: data.subject ?? null,
    content: data.content,
    parentMessageId: data.parentMessageId ?? null,
  })
  return id
}

// ---------------------------------------------------------------------------
// P2-2: 群组消息（教师→全班家长，fan-out on write）
// ---------------------------------------------------------------------------

export interface SendGroupMessageInput {
  senderId: string
  classId: string
  subject: string | null
  content: string
}

export interface SendGroupMessageResult {
  groupMessageId: string
  recipientIds: string[]
}

/**
 * P2-2: 群发消息给指定班级的所有家长。
 *
 * 采用 fan-out on write 策略：为每个家长创建独立的 messages 行，
 * 共享同一 groupMessageId 便于后续聚合查询（如"已发送群组消息"列表）。
 *
 * 通过 classes data-access 获取学生 ID，再通过 parent data-access
 * 获取家长 ID，避免直接 JOIN classEnrollments / parentStudentRelations 表。
 */
export async function sendGroupMessage(
  data: SendGroupMessageInput
): Promise<SendGroupMessageResult> {
  // 1. 获取班级所有学生 ID
  const studentIds = await getStudentIdsByClassIds([data.classId])
  // 2. 获取这些学生的所有家长 ID（去重）
  const parentIds = await getParentIdsByStudentIds(studentIds)
  // 过滤掉发送者自己（避免教师给自己发消息）
  const recipientIds = Array.from(new Set(parentIds)).filter((id) => id !== data.senderId)

  if (recipientIds.length === 0) {
    return { groupMessageId: "", recipientIds: [] }
  }

  // 3. 生成 groupMessageId 并批量插入
  const groupMessageId = createId()
  const now = new Date()
  const rows = recipientIds.map((receiverId) => ({
    id: createId(),
    senderId: data.senderId,
    receiverId,
    subject: data.subject ?? null,
    content: data.content,
    parentMessageId: null,
    groupMessageId,
    createdAt: now,
  }))

  await db.insert(messages).values(rows)

  return { groupMessageId, recipientIds }
}

export async function markMessageAsRead(id: string, userId: string): Promise<void> {
  await db
    .update(messages)
    .set({ isRead: true, readAt: new Date() })
    .where(and(eq(messages.id, id), eq(messages.receiverId, userId), eq(messages.isRead, false)))
}

export async function deleteMessage(id: string, userId: string): Promise<void> {
  const now = new Date()
  // 软删除：发送方删除设置 senderDeletedAt，接收方删除设置 receiverDeletedAt，互不影响
  // 使用事务保证两次 UPDATE 的原子性，避免部分失败导致数据不一致
  await db.transaction(async (tx) => {
    await tx
      .update(messages)
      .set({ senderDeletedAt: now })
      .where(and(eq(messages.id, id), eq(messages.senderId, userId)))
    await tx
      .update(messages)
      .set({ receiverDeletedAt: now })
      .where(and(eq(messages.id, id), eq(messages.receiverId, userId)))
  })
}

export async function toggleMessageStar(id: string, userId: string): Promise<void> {
  // 查询当前星标状态
  const [row] = await db
    .select({ isStarred: messages.isStarred })
    .from(messages)
    .where(and(eq(messages.id, id), eq(messages.receiverId, userId)))
    .limit(1)

  if (!row) return

  await db
    .update(messages)
    .set({ isStarred: !row.isStarred })
    .where(and(eq(messages.id, id), eq(messages.receiverId, userId)))
}

// ---------------------------------------------------------------------------
// P2-1: 批量操作
// ---------------------------------------------------------------------------

/** P2-1: 批量标记已读（仅当前用户为接收方的消息） */
export async function bulkMarkMessagesAsRead(ids: string[], userId: string): Promise<number> {
  if (ids.length === 0) return 0
  const result = await db
    .update(messages)
    .set({ isRead: true, readAt: new Date() })
    .where(and(
      inArray(messages.id, ids),
      eq(messages.receiverId, userId),
      eq(messages.isRead, false)
    ))
  // MySqlRawQueryResult 是 [ResultSetHeader, FieldPacket[]] 元组，affectedRows 在 [0]
  return result[0]?.affectedRows ?? 0
}

/** P2-1: 批量删除（软删除，发送方/接收方各自标记） */
export async function bulkDeleteMessages(ids: string[], userId: string): Promise<void> {
  if (ids.length === 0) return
  const now = new Date()
  await db.transaction(async (tx) => {
    await tx
      .update(messages)
      .set({ senderDeletedAt: now })
      .where(and(inArray(messages.id, ids), eq(messages.senderId, userId)))
    await tx
      .update(messages)
      .set({ receiverDeletedAt: now })
      .where(and(inArray(messages.id, ids), eq(messages.receiverId, userId)))
  })
}

/** P2-1: 批量切换星标（仅当前用户为接收方的消息） */
export async function bulkToggleMessagesStar(ids: string[], userId: string): Promise<void> {
  if (ids.length === 0) return
  // 查询当前状态
  const rows = await db
    .select({ id: messages.id, isStarred: messages.isStarred })
    .from(messages)
    .where(and(inArray(messages.id, ids), eq(messages.receiverId, userId)))

  if (rows.length === 0) return

  // 按目标状态分组
  const toStar = rows.filter((r) => !r.isStarred).map((r) => r.id)
  const toUnstar = rows.filter((r) => r.isStarred).map((r) => r.id)

  if (toStar.length > 0) {
    await db
      .update(messages)
      .set({ isStarred: true })
      .where(and(inArray(messages.id, toStar), eq(messages.receiverId, userId)))
  }
  if (toUnstar.length > 0) {
    await db
      .update(messages)
      .set({ isStarred: false })
      .where(and(inArray(messages.id, toUnstar), eq(messages.receiverId, userId)))
  }
}

// ---------------------------------------------------------------------------
// P2-4: 消息撤回
// ---------------------------------------------------------------------------

/** P2-4: 撤回时间窗口（毫秒），发送后 2 分钟内可撤回 */
export const MESSAGE_RECALL_WINDOW_MS = 2 * 60 * 1000

/**
 * P2-4: 撤回消息。
 *
 * 规则：
 * - 仅发送方可以撤回自己的消息
 * - 必须在 MESSAGE_RECALL_WINDOW_MS 时间窗口内
 * - 已撤回的消息（recalledAt 非空）不能重复撤回
 *
 * 返回值：
 * - "ok": 撤回成功
 * - "not_found": 消息不存在或不属于当前用户
 * - "expired": 超过撤回时间窗口
 * - "already_recalled": 消息已被撤回
 */
export async function recallMessage(
  id: string,
  userId: string
): Promise<"ok" | "not_found" | "expired" | "already_recalled"> {
  const [row] = await db
    .select({
      id: messages.id,
      senderId: messages.senderId,
      recalledAt: messages.recalledAt,
      createdAt: messages.createdAt,
    })
    .from(messages)
    .where(and(eq(messages.id, id), eq(messages.senderId, userId)))
    .limit(1)

  if (!row) return "not_found"
  if (row.recalledAt) return "already_recalled"

  const elapsed = Date.now() - row.createdAt.getTime()
  if (elapsed > MESSAGE_RECALL_WINDOW_MS) return "expired"

  await db
    .update(messages)
    .set({ recalledAt: new Date() })
    .where(eq(messages.id, id))

  return "ok"
}

export const getUnreadMessageCount = cache(async (userId: string): Promise<number> => {
  const [row] = await db
    .select({ value: count() })
    .from(messages)
    .where(and(eq(messages.receiverId, userId), eq(messages.isRead, false), isNull(messages.receiverDeletedAt)))
  return Number(row?.value ?? 0)
})

// ---------------------------------------------------------------------------
// P2-7: 配置驱动的角色收件人解析器
//
// 每个 DataScope.type 对应一个 RecipientResolver，将解析逻辑与主流程解耦。
// 新增角色或调整 DataScope 时仅需在 RECIPIENT_RESOLVERS 中增/改配置项，
// 无需修改 getRecipients 主流程（开闭原则）。
// ---------------------------------------------------------------------------

const resolveAdminRecipients = async (ctx: RecipientResolverContext): Promise<RecipientOption[]> => {
  const { userId } = ctx
  const all = await db.select({ id: users.id, name: users.name, email: users.email }).from(users)
  // P1-6: admin 角色分支补 role
  return all
    .filter((r) => r.id !== userId)
    .map((r) => ({ ...r, name: r.name ?? r.email, role: "admin" as RecipientRole }))
}

const resolveClassTaughtRecipients = async (ctx: RecipientResolverContext): Promise<RecipientOption[]> => {
  const { userId, scope } = ctx
  if (scope.type !== "class_taught" || scope.classIds.length === 0) return []
  // 通过 classes data-access 获取学生 ID，避免直接 JOIN classEnrollments 表
  const studentIds = await getStudentIdsByClassIds(scope.classIds)
  const userMap = await getUserNamesByIds(studentIds)
  return Array.from(userMap.values())
    .filter((u) => u.id !== userId)
    .map((u) => ({ id: u.id, name: u.name ?? u.email, email: u.email, role: "student" as RecipientRole }))
}

const resolveGradeManagedRecipients = async (ctx: RecipientResolverContext): Promise<RecipientOption[]> => {
  const { userId, scope } = ctx
  if (scope.type !== "grade_managed" || scope.gradeIds.length === 0) return []
  // 通过 classes data-access 获取年级下所有班级，再获取学生 ID，
  // 避免直接 JOIN classes / classEnrollments 表
  const classLists = await Promise.all(scope.gradeIds.map((g) => getClassesByGradeId(g)))
  const classIds = classLists.flat().map((c) => c.id)
  const studentIds = await getStudentIdsByClassIds(classIds)
  const userMap = await getUserNamesByIds(studentIds)
  return Array.from(userMap.values())
    .filter((u) => u.id !== userId)
    .map((u) => ({ id: u.id, name: u.name ?? u.email, email: u.email, role: "student" as RecipientRole }))
}

const resolveClassMembersRecipients = async (ctx: RecipientResolverContext): Promise<RecipientOption[]> => {
  const { userId, scope } = ctx
  if (scope.type !== "class_members" || scope.classIds.length === 0) return []
  // 学生可以给自己班级的任课教师/班主任发消息
  const teacherIds = await getTeacherIdsByClassIds(scope.classIds)
  const userMap = await getUserNamesByIds(teacherIds)
  return Array.from(userMap.values())
    .filter((u) => u.id !== userId)
    .map((u) => ({ id: u.id, name: u.name ?? u.email, email: u.email, role: "teacher" as RecipientRole }))
}

const resolveChildrenRecipients = async (ctx: RecipientResolverContext): Promise<RecipientOption[]> => {
  const { userId, scope } = ctx
  if (scope.type !== "children" || scope.childrenIds.length === 0) return []
  // 家长可以给孩子的班主任/任课教师发消息
  const classIds = await Promise.all(scope.childrenIds.map((id) => getStudentActiveClassId(id)))
  const validClassIds = classIds.filter((id): id is string => id !== null)
  const teacherIds = await getTeacherIdsByClassIds(validClassIds)
  const userMap = await getUserNamesByIds(teacherIds)
  return Array.from(userMap.values())
    .filter((u) => u.id !== userId)
    .map((u) => ({ id: u.id, name: u.name ?? u.email, email: u.email, role: "teacher" as RecipientRole }))
}

/** P2-7: 收件人解析器注册表，按 DataScope.type 索引 */
const RECIPIENT_RESOLVERS: Record<DataScope["type"], MessagingRoleConfig> = {
  all: { scopeType: "all", resolve: resolveAdminRecipients },
  owned: { scopeType: "owned", resolve: async () => [] },
  class_taught: { scopeType: "class_taught", resolve: resolveClassTaughtRecipients },
  grade_managed: { scopeType: "grade_managed", resolve: resolveGradeManagedRecipients },
  class_members: { scopeType: "class_members", resolve: resolveClassMembersRecipients },
  children: { scopeType: "children", resolve: resolveChildrenRecipients },
}

/**
 * P2-7: 获取收件人列表（配置驱动）。
 *
 * 根据 DataScope.type 从 RECIPIENT_RESOLVERS 注册表查找对应解析器并调用。
 * 新增角色或 DataScope 类型仅需：
 *   1. 实现新的 RecipientResolver
 *   2. 在 RECIPIENT_RESOLVERS 中新增配置项
 * 无需修改本函数。
 */
export const getRecipients = cache(
  async (userId: string, scope: DataScope): Promise<RecipientOption[]> => {
    const config = RECIPIENT_RESOLVERS[scope.type]
    if (!config) return []
    return config.resolve({ userId, scope })
  }
)

/**
 * P0-1: 校验收件人是否在 sender 的 DataScope 允许范围内。
 *
 * 在 sendMessageAction 中作为 Server Action 二次校验，防止绕过前端 Select
 * 直接构造 FormData 提交任意 receiverId 的越权发送。
 *
 * 实现复用 getRecipients 的结果，避免重复解析 DataScope。
 */
export async function isReceiverAllowed(
  userId: string,
  receiverId: string,
  scope: DataScope
): Promise<boolean> {
  const recipients = await getRecipients(userId, scope)
  return recipients.some((r) => r.id === receiverId)
}

/**
 * 消息详情页编排函数：获取消息详情，并自动标记为已读（若当前用户为接收方且未读）。
 * 使用 next/server 的 after() 实现非阻塞标记，避免阻塞页面渲染。
 *
 * 注：消息首页编排（getMessagesPageData）已迁出至 messages/page.tsx 页面层，
 * 由页面通过 Promise.all 调用 messaging.data-access 和 notifications.data-access
 * 保持模块独立性（P0-3 修复，避免 data-access 层跨模块动态 import）。
 */
export async function getMessageDetailPageData(
  id: string,
  userId: string
): Promise<Message | null> {
  const message = await getMessageById(id, userId)
  if (!message) return null

  // 自动标记已读：仅当当前用户为接收方且消息未读时
  if (!message.isRead && message.receiverId === userId) {
    await markMessageAsRead(id, userId)
  }

  return message
}

// ---------------------------------------------------------------------------
// P2-5: 消息举报 + 用户屏蔽
// ---------------------------------------------------------------------------

/**
 * P2-5: 举报消息。
 *
 * 防重复：同一用户对同一消息只能举报一次。已举报则返回 "already_reported"。
 */
export async function reportMessage(
  data: CreateMessageReportInput
): Promise<"ok" | "already_reported"> {
  // 检查是否已举报过该消息
  const [existing] = await db
    .select({ id: messageReports.id })
    .from(messageReports)
    .where(
      and(
        eq(messageReports.reporterId, data.reporterId),
        eq(messageReports.messageId, data.messageId)
      )
    )
    .limit(1)

  if (existing) return "already_reported"

  const id = createId()
  await db.insert(messageReports).values({
    id,
    reporterId: data.reporterId,
    messageId: data.messageId,
    reportedUserId: data.reportedUserId,
    reason: data.reason,
    description: data.description ?? null,
  })
  return "ok"
}

/**
 * P2-5: 检查用户是否已举报某条消息（用于 UI 按钮禁用状态）。
 */
export async function hasUserReportedMessage(
  reporterId: string,
  messageId: string
): Promise<boolean> {
  const [row] = await db
    .select({ id: messageReports.id })
    .from(messageReports)
    .where(
      and(
        eq(messageReports.reporterId, reporterId),
        eq(messageReports.messageId, messageId)
      )
    )
    .limit(1)
  return !!row
}

/**
 * P2-5: 屏蔽用户。
 *
 * 利用 unique 索引防止重复屏蔽：插入冲突时视为"已屏蔽"。
 */
export async function blockUser(
  blockerId: string,
  blockedId: string
): Promise<"ok" | "already_blocked" | "self_block"> {
  if (blockerId === blockedId) return "self_block"

  // 检查是否已屏蔽
  const [existing] = await db
    .select({ id: userBlocks.id })
    .from(userBlocks)
    .where(
      and(
        eq(userBlocks.blockerId, blockerId),
        eq(userBlocks.blockedId, blockedId)
      )
    )
    .limit(1)

  if (existing) return "already_blocked"

  const id = createId()
  await db.insert(userBlocks).values({ id, blockerId, blockedId })
  return "ok"
}

/**
 * P2-5: 取消屏蔽用户。
 */
export async function unblockUser(
  blockerId: string,
  blockedId: string
): Promise<void> {
  await db
    .delete(userBlocks)
    .where(
      and(
        eq(userBlocks.blockerId, blockerId),
        eq(userBlocks.blockedId, blockedId)
      )
    )
}

/**
 * P2-5: 检查 blocker 是否屏蔽了 blocked（用于发送消息前校验）。
 */
export async function isUserBlocked(
  blockerId: string,
  blockedId: string
): Promise<boolean> {
  const [row] = await db
    .select({ id: userBlocks.id })
    .from(userBlocks)
    .where(
      and(
        eq(userBlocks.blockerId, blockerId),
        eq(userBlocks.blockedId, blockedId)
      )
    )
    .limit(1)
  return !!row
}

/**
 * P2-5: 判断两个用户之间是否存在屏蔽关系（任一方向）。
 * 用于 sendMessage 校验：发送方或接收方任一屏蔽对方，则禁止发送。
 */
export async function isEitherUserBlocked(
  userIdA: string,
  userIdB: string
): Promise<boolean> {
  const [row] = await db
    .select({ id: userBlocks.id })
    .from(userBlocks)
    .where(
      or(
        and(eq(userBlocks.blockerId, userIdA), eq(userBlocks.blockedId, userIdB)),
        and(eq(userBlocks.blockerId, userIdB), eq(userBlocks.blockedId, userIdA))
      )
    )
    .limit(1)
  return !!row
}

/**
 * P2-5: 获取用户屏蔽的所有用户 ID（用于收件人列表过滤）。
 */
export async function getBlockedUserIds(blockerId: string): Promise<string[]> {
  const rows = await db
    .select({ blockedId: userBlocks.blockedId })
    .from(userBlocks)
    .where(eq(userBlocks.blockerId, blockerId))
  return rows.map((r) => r.blockedId)
}

/** P2-5: 映射举报行到接口类型 */
const mapMessageReport = (r: {
  id: string
  reporterId: string
  messageId: string
  reportedUserId: string
  reason: string
  description: string | null
  status: string
  createdAt: Date
  updatedAt: Date
}): MessageReport => ({
  id: r.id,
  reporterId: r.reporterId,
  messageId: r.messageId,
  reportedUserId: r.reportedUserId,
  reason: r.reason as MessageReportReason,
  description: r.description,
  status: r.status as MessageReport["status"],
  createdAt: r.createdAt.toISOString(),
  updatedAt: r.updatedAt.toISOString(),
})

/**
 * P2-5: 获取举报列表（admin 管理用，按状态筛选）。
 */
export async function getMessageReports(
  status?: MessageReport["status"]
): Promise<MessageReport[]> {
  const where = status ? eq(messageReports.status, status) : undefined
  const query = db
    .select()
    .from(messageReports)
    .orderBy(desc(messageReports.createdAt))
  const rows = where ? await query.where(where) : await query
  return rows.map(mapMessageReport)
}

/** P2-5: 映射屏蔽行到接口类型 */
const mapUserBlock = (r: {
  id: string
  blockerId: string
  blockedId: string
  createdAt: Date
}): UserBlock => ({
  id: r.id,
  blockerId: r.blockerId,
  blockedId: r.blockedId,
  createdAt: r.createdAt.toISOString(),
})

/**
 * P2-5: 获取用户屏蔽列表。
 */
export async function getUserBlocks(blockerId: string): Promise<UserBlock[]> {
  const rows = await db
    .select()
    .from(userBlocks)
    .where(eq(userBlocks.blockerId, blockerId))
    .orderBy(desc(userBlocks.createdAt))
  return rows.map(mapUserBlock)
}

// ---------------------------------------------------------------------------
// P2-3: 消息附件（复用 fileAttachments 表，targetType="message"）
// ---------------------------------------------------------------------------

/** P2-3: 附件 target 类型常量，用于 fileAttachments 表关联 */
export const MESSAGE_ATTACHMENT_TARGET_TYPE = "message"

/**
 * P2-3: 获取指定消息的所有附件。
 *
 * 复用 files 模块的 getFileAttachmentsByTarget，避免直接查询 fileAttachments 表。
 */
export async function getMessageAttachments(messageId: string): Promise<MessageAttachment[]> {
  const files = await getFileAttachmentsByTarget(MESSAGE_ATTACHMENT_TARGET_TYPE, messageId)
  return files.map((f) => ({
    id: f.id,
    filename: f.filename,
    originalName: f.originalName,
    mimeType: f.mimeType,
    size: f.size,
    url: f.url,
    // files 模块 FileAttachment.createdAt 已为 ISO 字符串
    createdAt: f.createdAt,
  }))
}

// ---------------------------------------------------------------------------
// V2-P2-13c: 消息草稿 CRUD（message_drafts 表）
// ---------------------------------------------------------------------------

const mapDraft = (
  r: {
    id: string
    userId: string
    receiverId: string | null
    subject: string | null
    content: string | null
    parentMessageId: string | null
    deviceId: string | null
    version: number
    createdAt: Date
    updatedAt: Date
  },
  nameMap: Map<string, string>
): MessageDraft => ({
  id: r.id,
  userId: r.userId,
  receiverId: r.receiverId,
  receiverName: r.receiverId ? (nameMap.get(r.receiverId) ?? null) : null,
  subject: r.subject,
  content: r.content,
  parentMessageId: r.parentMessageId,
  deviceId: r.deviceId,
  version: r.version,
  createdAt: toIsoRequired(r.createdAt),
  updatedAt: toIsoRequired(r.updatedAt),
})

export const getMessageDrafts = cache(
  async (userId: string): Promise<MessageDraft[]> => {
    const rows = await db
      .select()
      .from(messageDrafts)
      .where(eq(messageDrafts.userId, userId))
      .orderBy(desc(messageDrafts.updatedAt))

    const receiverIds = rows.map((r) => r.receiverId).filter((id): id is string => id !== null)
    const nameMap = await resolveUserNames(receiverIds)

    return rows.map((r) => mapDraft(r, nameMap))
  }
)

export async function createMessageDraft(data: CreateMessageDraftInput): Promise<string> {
  const id = createId()
  await db.insert(messageDrafts).values({
    id,
    userId: data.userId,
    receiverId: data.receiverId ?? null,
    subject: data.subject ?? null,
    content: data.content ?? null,
    parentMessageId: data.parentMessageId ?? null,
    // P2-10: 记录来源设备
    deviceId: data.deviceId ?? null,
    // P2-10: 初始版本号 1
    version: 1,
  })
  return id
}

/**
 * P2-10: 更新草稿（带乐观锁）。
 *
 * - 客户端传入 expectedVersion，与数据库当前 version 比对
 * - 匹配则更新并 version+1
 * - 不匹配（冲突）返回 "conflict"，客户端应拉取最新版本并提示用户
 *
 * 返回值：
 * - "ok": 更新成功
 * - "not_found": 草稿不存在或不属于当前用户
 * - "conflict": 版本冲突（其他设备已更新）
 */
export async function updateMessageDraft(
  id: string,
  userId: string,
  data: UpdateMessageDraftInput
): Promise<"ok" | "not_found" | "conflict"> {
  // P2-10: 乐观锁 - 先查询当前版本
  const [existing] = await db
    .select({ id: messageDrafts.id, version: messageDrafts.version })
    .from(messageDrafts)
    .where(and(eq(messageDrafts.id, id), eq(messageDrafts.userId, userId)))
    .limit(1)

  if (!existing) return "not_found"

  // P2-10: 版本冲突检测
  if (data.expectedVersion !== undefined && data.expectedVersion !== existing.version) {
    return "conflict"
  }

  await db
    .update(messageDrafts)
    .set({
      ...(data.receiverId !== undefined && { receiverId: data.receiverId }),
      ...(data.subject !== undefined && { subject: data.subject }),
      ...(data.content !== undefined && { content: data.content }),
      ...(data.parentMessageId !== undefined && { parentMessageId: data.parentMessageId }),
      ...(data.deviceId !== undefined && { deviceId: data.deviceId }),
      // P2-10: 版本号自增
      version: existing.version + 1,
    })
    .where(and(eq(messageDrafts.id, id), eq(messageDrafts.userId, userId)))

  return "ok"
}

export async function deleteMessageDraft(id: string, userId: string): Promise<void> {
  await db
    .delete(messageDrafts)
    .where(and(eq(messageDrafts.id, id), eq(messageDrafts.userId, userId)))
}

export async function getMessageDraftById(id: string, userId: string): Promise<MessageDraft | null> {
  const [row] = await db
    .select()
    .from(messageDrafts)
    .where(and(eq(messageDrafts.id, id), eq(messageDrafts.userId, userId)))
    .limit(1)

  if (!row) return null

  const nameMap = row.receiverId ? await resolveUserNames([row.receiverId]) : new Map<string, string>()
  return mapDraft(row, nameMap)
}

// ---------------------------------------------------------------------------
// P2-6: 快捷回复模板
// ---------------------------------------------------------------------------

interface MessageTemplateRow {
  id: string
  userId: string
  title: string
  content: string
  sortOrder: number
  updatedAt: Date
  createdAt: Date
}

const mapTemplate = (r: MessageTemplateRow): MessageTemplate => ({
  id: r.id,
  userId: r.userId,
  title: r.title,
  content: r.content,
  sortOrder: r.sortOrder,
  updatedAt: toIsoRequired(r.updatedAt),
  createdAt: toIsoRequired(r.createdAt),
})

export const getMessageTemplates = cache(
  async (userId: string): Promise<MessageTemplate[]> => {
    const rows = await db
      .select()
      .from(messageTemplates)
      .where(eq(messageTemplates.userId, userId))
      .orderBy(messageTemplates.sortOrder, desc(messageTemplates.createdAt))
    return rows.map(mapTemplate)
  }
)

export async function createMessageTemplate(data: CreateMessageTemplateInput): Promise<string> {
  const id = createId()
  await db.insert(messageTemplates).values({
    id,
    userId: data.userId,
    title: data.title,
    content: data.content,
    sortOrder: data.sortOrder ?? 0,
  })
  return id
}

export async function updateMessageTemplate(
  id: string,
  userId: string,
  data: UpdateMessageTemplateInput
): Promise<void> {
  const updateData: Partial<{ title: string; content: string; sortOrder: number }> = {}
  if (data.title !== undefined) updateData.title = data.title
  if (data.content !== undefined) updateData.content = data.content
  if (data.sortOrder !== undefined) updateData.sortOrder = data.sortOrder

  if (Object.keys(updateData).length === 0) return

  await db
    .update(messageTemplates)
    .set(updateData)
    .where(and(eq(messageTemplates.id, id), eq(messageTemplates.userId, userId)))
}

export async function deleteMessageTemplate(id: string, userId: string): Promise<void> {
  await db
    .delete(messageTemplates)
    .where(and(eq(messageTemplates.id, id), eq(messageTemplates.userId, userId)))
}
