import "server-only"

/**
 * 通知数据访问层
 *
 * 职责:
 * - createNotification: 创建站内通知记录（message_notifications 表）
 * - getNotifications / markNotificationAsRead / markAllNotificationsAsRead / getUnreadNotificationCount: 站内通知 CRUD
 * - getUserContactInfo: 获取用户联系方式（手机/邮箱，用于渠道发送）
 * - logNotificationSend: 记录发送日志到 notification_logs 表（DB 写入失败时降级为 console）
 *
 * 表所有权:
 * - message_notifications（由 notifications 模块统一管理，P0-4 / P1-5 修复后从 messaging 迁移）
 * - notification_preferences（由 notifications/preferences.ts 管理）
 *
 * 注意: users 表当前无 wechatOpenId 字段，wechatOpenId 暂返回 undefined。
 * 未来扩展 users 表增加 wechat_open_id 列后，此处补充查询即可。
 */

import { cache } from "react"
import { createId } from "@paralleldrive/cuid2"
import { and, count, desc, eq } from "drizzle-orm"

import { db } from "@/shared/db"
import { messageNotifications, notificationLogs, users } from "@/shared/db/schema"
import type { ChannelRecipient } from "./channels/types"
import type {
  ChannelSendResult,
  CreateNotificationInput,
  GetNotificationsParams,
  Notification,
  NotificationPriority,
  NotificationType,
  PaginatedResult,
} from "./types"

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

const isNotificationType = (v: unknown): v is NotificationType =>
  v === "message" || v === "announcement" || v === "homework" || v === "grade" || v === "diagnostic"

const toNotificationType = (v: string): NotificationType =>
  isNotificationType(v) ? v : "message"

const isNotificationPriority = (v: unknown): v is NotificationPriority =>
  v === "low" || v === "normal" || v === "high" || v === "urgent"

const toNotificationPriority = (v: string): NotificationPriority =>
  isNotificationPriority(v) ? v : "normal"

interface NotificationRow {
  id: string
  userId: string
  type: string
  title: string
  content: string | null
  link: string | null
  isRead: boolean
  priority: string
  isArchived: boolean
  createdAt: Date
}

const mapNotification = (r: NotificationRow): Notification => ({
  id: r.id,
  userId: r.userId,
  type: toNotificationType(r.type),
  title: r.title,
  content: r.content,
  link: r.link,
  isRead: r.isRead,
  priority: toNotificationPriority(r.priority),
  isArchived: r.isArchived,
  createdAt: toIsoRequired(r.createdAt),
})

// ---------------------------------------------------------------------------
// 站内通知 CRUD（message_notifications 表）
// ---------------------------------------------------------------------------

export const getNotifications = cache(
  async (userId: string, params?: GetNotificationsParams): Promise<PaginatedResult<Notification>> => {
    const page = Math.max(1, params?.page ?? 1)
    const pageSize = Math.max(1, params?.pageSize ?? 20)
    const offset = (page - 1) * pageSize
    const conds = [eq(messageNotifications.userId, userId)]
    if (params?.unreadOnly) conds.push(eq(messageNotifications.isRead, false))
    // V2-P2-13b: 默认仅返回未归档通知
    const unarchivedOnly = params?.unarchivedOnly ?? true
    if (unarchivedOnly) conds.push(eq(messageNotifications.isArchived, false))
    // V2-P2-13b: 按优先级筛选
    if (params?.priority) conds.push(eq(messageNotifications.priority, params.priority))
    const where = and(...conds)

    const [rows, [totalRow]] = await Promise.all([
      db.select().from(messageNotifications).where(where).orderBy(desc(messageNotifications.createdAt)).limit(pageSize).offset(offset),
      db.select({ value: count() }).from(messageNotifications).where(where),
    ])
    const total = Number(totalRow?.value ?? 0)
    return { items: rows.map(mapNotification), total, page, pageSize, totalPages: Math.ceil(total / pageSize) }
  }
)

export async function createNotification(data: CreateNotificationInput): Promise<string> {
  const id = createId()
  await db.insert(messageNotifications).values({
    id,
    userId: data.userId,
    type: data.type,
    title: data.title,
    content: data.content ?? null,
    link: data.link ?? null,
    priority: data.priority ?? "normal",
  })
  return id
}

export async function markNotificationAsRead(id: string, userId: string): Promise<void> {
  await db
    .update(messageNotifications)
    .set({ isRead: true })
    .where(and(eq(messageNotifications.id, id), eq(messageNotifications.userId, userId)))
}

export async function markAllNotificationsAsRead(userId: string): Promise<void> {
  await db
    .update(messageNotifications)
    .set({ isRead: true })
    .where(and(eq(messageNotifications.userId, userId), eq(messageNotifications.isRead, false)))
}

export async function archiveNotification(id: string, userId: string): Promise<void> {
  await db
    .update(messageNotifications)
    .set({ isArchived: true })
    .where(and(eq(messageNotifications.id, id), eq(messageNotifications.userId, userId)))
}

export async function unarchiveNotification(id: string, userId: string): Promise<void> {
  await db
    .update(messageNotifications)
    .set({ isArchived: false })
    .where(and(eq(messageNotifications.id, id), eq(messageNotifications.userId, userId)))
}

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

// ---------------------------------------------------------------------------
// 用户联系方式（用于 SMS / Email / WeChat 渠道发送）
// ---------------------------------------------------------------------------

/**
 * 获取用户联系方式（手机号、邮箱）。
 * wechatOpenId 暂不支持（users 表无此字段），返回 undefined。
 */
export const getUserContactInfo = cache(
  async (userId: string): Promise<ChannelRecipient> => {
    const [row] = await db
      .select({
        id: users.id,
        phone: users.phone,
        email: users.email,
      })
      .from(users)
      .where(eq(users.id, userId))
      .limit(1)

    if (!row) {
      return { userId }
    }

    return {
      userId: row.id,
      phone: row.phone ?? undefined,
      email: row.email ?? undefined,
      // users 表暂无 wechat_open_id 字段；扩展 schema 后在此补充
      wechatOpenId: undefined,
    }
  }
)

// ---------------------------------------------------------------------------
// 发送日志
// ---------------------------------------------------------------------------

/**
 * 记录通知发送日志到数据库（notification_logs 表）。
 *
 * 持久化日志用于：
 * - 通知发送失败告警与排查
 * - 通知送达率统计
 * - 渠道健康度监控
 *
 * DB 写入失败时降级为 console.info，不阻塞通知发送流程。
 */
export async function logNotificationSend(
  result: ChannelSendResult,
  payload?: { userId: string; title: string }
): Promise<void> {
  const status = result.success ? "success" : "failure"
  const errorPart = result.error ? ` error="${result.error}"` : ""

  // 始终输出 console 日志（便于开发调试）
  // TODO V3-P2-8: 接入统一日志服务（shared/lib/logger），替换 console.info
  console.info(
    `[NotificationLog] ${result.success ? "OK" : "FAIL"} channel=${result.channel} messageId=${result.messageId ?? "-"}${errorPart}`
  )

  // 持久化到 DB（需要 payload 提供 userId 和 title）
  if (payload) {
    try {
      const logId = createId()
      await db.insert(notificationLogs).values({
        id: logId,
        userId: payload.userId,
        title: payload.title,
        channel: result.channel,
        status,
        messageId: result.messageId ?? null,
        error: result.error ?? null,
        sentAt: result.sentAt,
      })
    } catch (dbError) {
      // DB 写入失败不阻塞通知流程，仅记录错误
      // TODO V3-P2-8: 接入统一日志服务（shared/lib/logger），替换 console.error
      console.error("[NotificationLog] Failed to persist log:", dbError)
    }
  }
}

/**
 * 批量记录发送日志。
 */
export async function logNotificationSendBatch(
  results: ChannelSendResult[],
  payload?: { userId: string; title: string }
): Promise<void> {
  await Promise.all(results.map((result) => logNotificationSend(result, payload)))
}
