import "server-only"

import { db } from "@/shared/db"
import { examProctoringEvents } from "@/shared/db/schema"
import { and, desc, eq, gte, lte, sql, inArray } from "drizzle-orm"
import { cache } from "react"
import { createId } from "@paralleldrive/cuid2"

import {
  getExamForProctoringCrossModule,
  getExamSubmissionForProctoringCrossModule,
  getExamSubmissionsForExam,
  getExamTitleById,
} from "@/modules/exams/data-access"
import { getUserNamesByIds } from "@/modules/users/data-access"
import { safeParseDate } from "@/shared/lib/action-utils"

import type {
  ProctoringEvent,
  ProctoringEventWithDetails,
  ExamProctoringSummary,
  StudentProctoringStatus,
  RecordProctoringEventInput,
  GetProctoringEventsFilters,
  ExamModeConfig,
  ProctoringEventType,
  ExamMode,
  SubmissionStatus,
} from "./types"
import { ABNORMAL_EVENT_THRESHOLD } from "./types"

const ALL_EVENT_TYPES: ProctoringEventType[] = [
  "tab_switch",
  "window_blur",
  "copy_attempt",
  "paste_attempt",
  "right_click",
  "devtools_open",
  "fullscreen_exit",
  "idle_timeout",
]

const emptyEventsByType = (): Record<ProctoringEventType, number> => ({
  tab_switch: 0,
  window_blur: 0,
  copy_attempt: 0,
  paste_attempt: 0,
  right_click: 0,
  devtools_open: 0,
  fullscreen_exit: 0,
  idle_timeout: 0,
})

const toExamMode = (value: unknown): ExamMode => {
  if (value === "homework" || value === "timed" || value === "proctored") {
    return value
  }
  return "homework"
}

const isSubmissionStatus = (v: unknown): v is SubmissionStatus =>
  v === "started" || v === "submitted" || v === "graded"

const toSubmissionStatusNullable = (
  v: string | null,
): SubmissionStatus | null => (isSubmissionStatus(v) ? v : null)

/**
 * 校验提交记录归属（监考事件上报前的安全校验）
 * 仅当提交记录存在且属于该学生时返回必要字段，否则返回 null
 */
export async function getExamSubmissionForProctoring(
  submissionId: string,
  studentId: string,
): Promise<{ id: string; examId: string; studentId: string } | null> {
  return getExamSubmissionForProctoringCrossModule(submissionId, studentId)
}

/**
 * 记录一条监考事件
 */
export async function recordProctoringEvent(
  input: RecordProctoringEventInput,
): Promise<ProctoringEvent> {
  const eventId = createId()
  const now = new Date()

  await db.insert(examProctoringEvents).values({
    id: eventId,
    submissionId: input.submissionId,
    studentId: input.studentId,
    examId: input.examId,
    eventType: input.eventType,
    eventDetail: input.eventDetail ?? null,
    occurredAt: now,
  })

  return {
    id: eventId,
    submissionId: input.submissionId,
    studentId: input.studentId,
    examId: input.examId,
    eventType: input.eventType,
    eventDetail: input.eventDetail ?? null,
    occurredAt: now.toISOString(),
    createdAt: now.toISOString(),
  }
}

/**
 * 查询某场考试的监考事件（含学生姓名、考试标题）
 */
export const getProctoringEvents = cache(
  async (
    examId: string,
    filters?: GetProctoringEventsFilters,
  ): Promise<ProctoringEventWithDetails[]> => {
    const conditions = [eq(examProctoringEvents.examId, examId)]

    if (filters?.studentId) {
      conditions.push(eq(examProctoringEvents.studentId, filters.studentId))
    }
    if (filters?.eventType) {
      conditions.push(eq(examProctoringEvents.eventType, filters.eventType))
    }
    if (filters?.startedAt) {
      conditions.push(
        gte(
          examProctoringEvents.occurredAt,
          safeParseDate(filters.startedAt, "开始时间"),
        ),
      )
    }
    if (filters?.endedAt) {
      conditions.push(
        lte(
          examProctoringEvents.occurredAt,
          safeParseDate(filters.endedAt, "结束时间"),
        ),
      )
    }

    const rows = await db
      .select({
        event: examProctoringEvents,
      })
      .from(examProctoringEvents)
      .where(and(...conditions))
      .orderBy(desc(examProctoringEvents.occurredAt))

    if (rows.length === 0) return []

    const studentIds = Array.from(new Set(rows.map((r) => r.event.studentId)))
    const [userMap, examTitle] = await Promise.all([
      getUserNamesByIds(studentIds),
      getExamTitleById(examId),
    ])
    const resolvedExamTitle = examTitle ?? "未知考试"

    return rows.map((row) => ({
      id: row.event.id,
      submissionId: row.event.submissionId,
      studentId: row.event.studentId,
      examId: row.event.examId,
      eventType: row.event.eventType,
      eventDetail: row.event.eventDetail,
      occurredAt: row.event.occurredAt.toISOString(),
      createdAt: row.event.createdAt.toISOString(),
      studentName: userMap.get(row.event.studentId)?.name ?? "未知学生",
      examTitle: resolvedExamTitle,
    }))
  },
)

/**
 * 查询某次提交的监考事件
 */
export const getProctoringEventsBySubmission = cache(
  async (submissionId: string): Promise<ProctoringEvent[]> => {
    const rows = await db.query.examProctoringEvents.findMany({
      where: eq(examProctoringEvents.submissionId, submissionId),
      orderBy: [desc(examProctoringEvents.occurredAt)],
    })

    return rows.map((row) => ({
      id: row.id,
      submissionId: row.submissionId,
      studentId: row.studentId,
      examId: row.examId,
      eventType: row.eventType,
      eventDetail: row.eventDetail,
      occurredAt: row.occurredAt.toISOString(),
      createdAt: row.createdAt.toISOString(),
    }))
  },
)

/**
 * 获取考试监考摘要
 */
export const getExamProctoringSummary = cache(
  async (examId: string): Promise<ExamProctoringSummary> => {
    // 考试信息与提交记录相互独立，并行拉取
    const [exam, submissions] = await Promise.all([
      getExamForProctoringCrossModule(examId),
      getExamSubmissionsForExam(examId),
    ])

    const examTitle = exam?.title ?? "未知考试"
    const examMode = toExamMode(exam?.examMode)

    const totalStudents = submissions.length
    // 单次遍历统计 started / submitted
    let startedStudents = 0
    let submittedStudents = 0
    for (const s of submissions) {
      if (s.status === "started") startedStudents += 1
      if (s.status === "submitted" || s.status === "graded") submittedStudents += 1
    }

    // 按事件类型分组统计 与 按学生分组统计 相互独立，并行拉取
    const [eventStats, studentEventCounts] = await Promise.all([
      db
        .select({
          eventType: examProctoringEvents.eventType,
          count: sql<number>`count(*)::int`,
        })
        .from(examProctoringEvents)
        .where(eq(examProctoringEvents.examId, examId))
        .groupBy(examProctoringEvents.eventType),
      db
        .select({
          studentId: examProctoringEvents.studentId,
          count: sql<number>`count(*)::int`,
        })
        .from(examProctoringEvents)
        .where(eq(examProctoringEvents.examId, examId))
        .groupBy(examProctoringEvents.studentId),
    ])

    const eventsByType = emptyEventsByType()
    let totalEvents = 0
    for (const stat of eventStats) {
      const type = stat.eventType
      if (eventsByType[type] !== undefined) {
        eventsByType[type] = stat.count
        totalEvents += stat.count
      }
    }

    const abnormalStudents = studentEventCounts.filter(
      (s) => s.count >= ABNORMAL_EVENT_THRESHOLD,
    ).length

    return {
      examId,
      examTitle,
      examMode,
      totalStudents,
      startedStudents,
      submittedStudents,
      totalEvents,
      abnormalStudents,
      eventsByType,
    }
  },
)

/**
 * 获取所有学生监考状态
 */
export const getStudentProctoringStatuses = cache(
  async (examId: string): Promise<StudentProctoringStatus[]> => {
    // 1. 拉取所有提交记录
    const submissions = await getExamSubmissionsForExam(examId)

    if (submissions.length === 0) return []

    const studentIds = submissions.map((s) => s.studentId)

    // 2. 批量获取学生姓名 与 拉取事件记录 相互独立，并行拉取
    const [userMap, eventRows] = await Promise.all([
      getUserNamesByIds(studentIds),
      db
        .select({
          studentId: examProctoringEvents.studentId,
          eventType: examProctoringEvents.eventType,
          occurredAt: examProctoringEvents.occurredAt,
        })
        .from(examProctoringEvents)
        .where(
          and(
            eq(examProctoringEvents.examId, examId),
            inArray(examProctoringEvents.studentId, studentIds),
          ),
        )
        .orderBy(desc(examProctoringEvents.occurredAt)),
    ])

    // 4. 按学生聚合
    const statsByStudent = new Map<
      string,
      {
        count: number
        lastEventAt: Date | null
        byType: Record<ProctoringEventType, number>
      }
    >()

    for (const row of eventRows) {
      const sid = row.studentId
      const type = row.eventType
      const existing = statsByStudent.get(sid) ?? {
        count: 0,
        lastEventAt: null,
        byType: emptyEventsByType(),
      }
      existing.count += 1
      if (existing.byType[type] !== undefined) {
        existing.byType[type] += 1
      }
      if (!existing.lastEventAt || row.occurredAt > existing.lastEventAt) {
        existing.lastEventAt = row.occurredAt
      }
      statsByStudent.set(sid, existing)
    }

    return submissions.map((submission) => {
      const studentId = submission.studentId
      const stats = statsByStudent.get(studentId)
      return {
        studentId,
        studentName: userMap.get(studentId)?.name ?? "未知学生",
        submissionId: submission.id,
        submissionStatus: toSubmissionStatusNullable(submission.status ?? null),
        eventCount: stats?.count ?? 0,
        lastEventAt: stats?.lastEventAt ? stats.lastEventAt.toISOString() : null,
        isAbnormal: (stats?.count ?? 0) >= ABNORMAL_EVENT_THRESHOLD,
        eventsByType: stats?.byType ?? emptyEventsByType(),
      }
    })
  },
)

/**
 * 获取考试信息（含监考模式相关字段）
 */
export const getExamForProctoring = cache(
  async (examId: string): Promise<{
    id: string
    title: string
    examMode: ExamMode
    config: ExamModeConfig
  } | null> => {
    const exam = await getExamForProctoringCrossModule(examId)

    if (!exam) return null

    return {
      id: exam.id,
      title: exam.title,
      examMode: toExamMode(exam.examMode),
      config: {
        examMode: toExamMode(exam.examMode),
        durationMinutes: exam.durationMinutes ?? null,
        shuffleQuestions: exam.shuffleQuestions ?? false,
        allowLateStart: exam.allowLateStart ?? false,
        lateStartGraceMinutes: exam.lateStartGraceMinutes ?? 0,
        antiCheatEnabled: exam.antiCheatEnabled ?? false,
      },
    }
  },
)

/**
 * 获取最近 N 条监考事件（用于面板实时展示）
 */
export const getRecentProctoringEvents = cache(
  async (examId: string, limit = 20): Promise<ProctoringEventWithDetails[]> => {
    const rows = await db
      .select({
        event: examProctoringEvents,
      })
      .from(examProctoringEvents)
      .where(eq(examProctoringEvents.examId, examId))
      .orderBy(desc(examProctoringEvents.occurredAt))
      .limit(limit)

    if (rows.length === 0) return []

    const studentIds = Array.from(new Set(rows.map((r) => r.event.studentId)))
    const [userMap, examTitle] = await Promise.all([
      getUserNamesByIds(studentIds),
      getExamTitleById(examId),
    ])
    const resolvedExamTitle = examTitle ?? "未知考试"

    return rows.map((row) => ({
      id: row.event.id,
      submissionId: row.event.submissionId,
      studentId: row.event.studentId,
      examId: row.event.examId,
      eventType: row.event.eventType,
      eventDetail: row.event.eventDetail,
      occurredAt: row.event.occurredAt.toISOString(),
      createdAt: row.event.createdAt.toISOString(),
      studentName: userMap.get(row.event.studentId)?.name ?? "未知学生",
      examTitle: resolvedExamTitle,
    }))
  },
)

export { ALL_EVENT_TYPES }
