import "server-only"

import { and, asc, count, desc, eq, gte, lte, sql } from "drizzle-orm"

import { db } from "@/shared/db"
import { attendanceRecords, classes, users } from "@/shared/db/schema"
import { safeParseDate } from "@/shared/lib/action-utils"

import type {
  AttendanceListItem,
  AttendanceStats,
  ClassAttendanceSummary,
  StudentAttendanceSummary,
} from "./types"

const EMPTY_STATS: AttendanceStats = {
  total: 0,
  present: 0,
  absent: 0,
  late: 0,
  earlyLeave: 0,
  excused: 0,
  presentRate: 0,
  lateRate: 0,
}

/** 最近记录的默认截取数量（避免一次拉全量） */
const DEFAULT_RECENT_LIMIT = 20

/**
 * 根据考勤记录行计算统计（纯函数，便于测试）。
 */
export const computeStats = (rows: { status: string }[]): AttendanceStats => {
  if (rows.length === 0) return EMPTY_STATS
  const stats: AttendanceStats = { ...EMPTY_STATS, total: rows.length }
  for (const r of rows) {
    if (r.status === "present") stats.present += 1
    else if (r.status === "absent") stats.absent += 1
    else if (r.status === "late") stats.late += 1
    else if (r.status === "early_leave") stats.earlyLeave += 1
    else if (r.status === "excused") stats.excused += 1
  }
  stats.presentRate = Math.round((stats.present / stats.total) * 10000) / 100
  stats.lateRate = Math.round((stats.late / stats.total) * 10000) / 100
  return stats
}

/**
 * 将 SQL 聚合行转换为 AttendanceStats（避免拉全量记录计算统计）。
 */
const statsFromAggregate = (row: {
  total: number
  present: number
  absent: number
  late: number
  earlyLeave: number
  excused: number
}): AttendanceStats => {
  const total = Number(row.total ?? 0)
  const present = Number(row.present ?? 0)
  const late = Number(row.late ?? 0)
  return {
    total,
    present,
    absent: Number(row.absent ?? 0),
    late,
    earlyLeave: Number(row.earlyLeave ?? 0),
    excused: Number(row.excused ?? 0),
    presentRate: total > 0 ? Math.round((present / total) * 10000) / 100 : 0,
    lateRate: total > 0 ? Math.round((late / total) * 10000) / 100 : 0,
  }
}

const serializeDate = (d: Date | string | null): string =>
  d ? new Date(d).toISOString().slice(0, 10) : ""

/**
 * 获取学生考勤汇总。
 * 优化：统计使用 SQL 聚合查询（避免拉全量记录），最近记录使用 LIMIT 分页。
 */
export async function getStudentAttendanceSummary(
  studentId: string,
  startDate?: string,
  endDate?: string,
  recentLimit: number = DEFAULT_RECENT_LIMIT
): Promise<StudentAttendanceSummary | null> {
  const [student] = await db
    .select({ name: users.name })
    .from(users)
    .where(eq(users.id, studentId))
    .limit(1)
  if (!student) return null

  const conditions = [eq(attendanceRecords.studentId, studentId)]
  if (startDate) conditions.push(gte(attendanceRecords.date, safeParseDate(startDate, "开始日期")))
  if (endDate) conditions.push(lte(attendanceRecords.date, safeParseDate(endDate, "结束日期")))
  const where = and(...conditions)

  // 统计使用 SQL 聚合，避免拉全量记录
  const [statsRow] = await db
    .select({
      total: count(),
      present: sql<number>`COALESCE(SUM(CASE WHEN ${attendanceRecords.status} = 'present' THEN 1 ELSE 0 END), 0)`,
      absent: sql<number>`COALESCE(SUM(CASE WHEN ${attendanceRecords.status} = 'absent' THEN 1 ELSE 0 END), 0)`,
      late: sql<number>`COALESCE(SUM(CASE WHEN ${attendanceRecords.status} = 'late' THEN 1 ELSE 0 END), 0)`,
      earlyLeave: sql<number>`COALESCE(SUM(CASE WHEN ${attendanceRecords.status} = 'early_leave' THEN 1 ELSE 0 END), 0)`,
      excused: sql<number>`COALESCE(SUM(CASE WHEN ${attendanceRecords.status} = 'excused' THEN 1 ELSE 0 END), 0)`,
    })
    .from(attendanceRecords)
    .where(where)

  const stats = statsFromAggregate(statsRow ?? {
    total: 0,
    present: 0,
    absent: 0,
    late: 0,
    earlyLeave: 0,
    excused: 0,
  })

  // 最近记录使用 LIMIT 分页，避免拉全量
  const rows = await db
    .select({
      record: attendanceRecords,
      className: classes.name,
    })
    .from(attendanceRecords)
    .leftJoin(classes, eq(classes.id, attendanceRecords.classId))
    .where(where)
    .orderBy(desc(attendanceRecords.date))
    .limit(recentLimit)

  const recentRecords: AttendanceListItem[] = rows.map((r) => ({
    id: r.record.id,
    studentId: r.record.studentId,
    studentName: student.name ?? "Unknown",
    classId: r.record.classId,
    className: r.className ?? "Unknown",
    scheduleId: r.record.scheduleId ?? null,
    date: serializeDate(r.record.date),
    status: r.record.status,
    remark: r.record.remark ?? null,
    recordedBy: r.record.recordedBy,
    recorderName: "Unknown",
    createdAt: r.record.createdAt.toISOString(),
  }))

  return {
    studentId,
    studentName: student.name ?? "Unknown",
    stats,
    recentRecords,
  }
}

export async function getClassAttendanceStats(
  classId: string,
  startDate?: string,
  endDate?: string
): Promise<ClassAttendanceSummary | null> {
  const [classRow] = await db
    .select({ id: classes.id, name: classes.name })
    .from(classes)
    .where(eq(classes.id, classId))
    .limit(1)
  if (!classRow) return null

  const conditions = [eq(attendanceRecords.classId, classId)]
  if (startDate) conditions.push(gte(attendanceRecords.date, safeParseDate(startDate, "开始日期")))
  if (endDate) conditions.push(lte(attendanceRecords.date, safeParseDate(endDate, "结束日期")))

  const rows = await db
    .select({
      record: attendanceRecords,
      studentName: users.name,
    })
    .from(attendanceRecords)
    .leftJoin(users, eq(users.id, attendanceRecords.studentId))
    .where(and(...conditions))
    .orderBy(asc(users.name))

  const stats = computeStats(rows.map((r) => ({ status: r.record.status })))

  const studentRecords: AttendanceListItem[] = rows.map((r) => ({
    id: r.record.id,
    studentId: r.record.studentId,
    studentName: r.studentName ?? "Unknown",
    classId: r.record.classId,
    className: classRow.name,
    scheduleId: r.record.scheduleId ?? null,
    date: serializeDate(r.record.date),
    status: r.record.status,
    remark: r.record.remark ?? null,
    recordedBy: r.record.recordedBy,
    recorderName: "Unknown",
    createdAt: r.record.createdAt.toISOString(),
  }))

  return {
    classId,
    className: classRow.name,
    date: startDate ?? endDate ?? new Date().toISOString().slice(0, 10),
    stats,
    studentRecords,
  }
}
