import "server-only"

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

import { db } from "@/shared/db"
import { attendanceRecords } from "@/shared/db/schema"
import { getClassNameById, getClassNamesByIds, getClassesByGradeId } from "@/modules/classes/data-access"
import { getUserNamesByIds } from "@/modules/users/data-access"
import { getGradeNameById } from "@/modules/school/data-access"
import { safeParseDate } from "@/shared/lib/action-utils"

import { getAttendanceRules } from "./data-access"
import {
  computeAttendanceWarnings,
  createEmptyWarningSummary,
  DEFAULT_ATTENDANCE_RATE_THRESHOLD,
  DEFAULT_CONSECUTIVE_ABSENCE_THRESHOLD,
} from "./warning-compute"
import {
  computeTrendPoints,
  type TrendGranularity,
} from "./trend-compute"
import type {
  AttendanceListItem,
  AttendanceStats,
  AttendanceTrendSummary,
  AttendanceWarningSummary,
  ClassAttendanceSummary,
  ClassComparisonSummary,
  ClassComparisonItem,
  StudentAttendanceSummary,
} from "./types"

const EMPTY_STATS: AttendanceStats = {
  total: 0,
  present: 0,
  absent: 0,
  late: 0,
  earlyLeave: 0,
  excused: 0,
  schoolActivity: 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
    else if (r.status === "school_activity") stats.schoolActivity += 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
  schoolActivity: 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),
    schoolActivity: Number(row.schoolActivity ?? 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> {
  // 委托 users data-access 查询学生姓名，避免跨模块直查 users 表
  const userMap = await getUserNamesByIds([studentId])
  const student = userMap.get(studentId)
  if (!student) return null
  const studentName = student.name ?? "Unknown"

  const conditions = [eq(attendanceRecords.studentId, studentId)]
  if (startDate) conditions.push(gte(attendanceRecords.date, safeParseDate(startDate, "startDate")))
  if (endDate) conditions.push(lte(attendanceRecords.date, safeParseDate(endDate, "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)`,
      schoolActivity: sql<number>`COALESCE(SUM(CASE WHEN ${attendanceRecords.status} = 'school_activity' 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,
    schoolActivity: 0,
  })

  // 最近记录：仅查询 attendanceRecords 表，className 通过 data-access 委托查询
  const rows = await db
    .select({
      record: attendanceRecords,
    })
    .from(attendanceRecords)
    .where(where)
    .orderBy(desc(attendanceRecords.date))
    .limit(recentLimit)

  // 批量解析 className（委托 classes data-access）
  const classIds = Array.from(new Set(rows.map((r) => r.record.classId)))
  const classNameMap = await getClassNamesByIds(classIds)

  const recentRecords: AttendanceListItem[] = rows.map((r) => ({
    id: r.record.id,
    studentId: r.record.studentId,
    studentName,
    classId: r.record.classId,
    className: classNameMap.get(r.record.classId) ?? "Unknown",
    scheduleId: r.record.scheduleId ?? null,
    date: serializeDate(r.record.date),
    status: r.record.status,
    // L-6：节次考勤，null 视为 full_day
    period: r.record.period ?? "full_day",
    remark: r.record.remark ?? null,
    reason: r.record.reason ?? null,
    recordedBy: r.record.recordedBy,
    recorderName: "Unknown",
    createdAt: r.record.createdAt.toISOString(),
  }))

  return {
    studentId,
    studentName,
    stats,
    recentRecords,
  }
}

export async function getClassAttendanceStats(
  classId: string,
  startDate?: string,
  endDate?: string
): Promise<ClassAttendanceSummary | null> {
  // 委托 classes data-access 查询班级名称，避免跨模块直查 classes 表
  const className = await getClassNameById(classId)
  if (!className) return null

  const conditions = [eq(attendanceRecords.classId, classId)]
  if (startDate) conditions.push(gte(attendanceRecords.date, safeParseDate(startDate, "startDate")))
  if (endDate) conditions.push(lte(attendanceRecords.date, safeParseDate(endDate, "endDate")))
  const where = and(...conditions)

  // 统计使用 SQL 聚合（P1-11 修复：避免全量查询 + 内存 computeStats）
  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)`,
      schoolActivity: sql<number>`COALESCE(SUM(CASE WHEN ${attendanceRecords.status} = 'school_activity' 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,
    schoolActivity: 0,
  })

  // 学生明细记录：仅查询 attendanceRecords 表，studentName 通过 data-access 委托查询
  const rows = await db
    .select({
      record: attendanceRecords,
    })
    .from(attendanceRecords)
    .where(where)
    .orderBy(asc(attendanceRecords.studentId))

  // 批量解析 studentName（委托 users data-access）
  const studentIds = Array.from(new Set(rows.map((r) => r.record.studentId)))
  const studentNameMap = await getUserNamesByIds(studentIds)

  const studentRecords: AttendanceListItem[] = rows.map((r) => ({
    id: r.record.id,
    studentId: r.record.studentId,
    studentName: studentNameMap.get(r.record.studentId)?.name ?? "Unknown",
    classId: r.record.classId,
    className,
    scheduleId: r.record.scheduleId ?? null,
    date: serializeDate(r.record.date),
    status: r.record.status,
    // L-6：节次考勤，null 视为 full_day
    period: r.record.period ?? "full_day",
    remark: r.record.remark ?? null,
    reason: r.record.reason ?? null,
    recordedBy: r.record.recordedBy,
    recorderName: "Unknown",
    createdAt: r.record.createdAt.toISOString(),
  }))

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

/**
 * L-3：获取班级所有学生的出勤率预警汇总。
 * 按学生聚合统计 + 连续缺勤段，调用纯函数 computeAttendanceWarnings 计算预警。
 */
export async function getClassAttendanceWarnings(
  classId: string,
  startDate?: string,
  endDate?: string
): Promise<AttendanceWarningSummary | null> {
  const className = await getClassNameById(classId)
  if (!className) return null

  // 读取班级规则（取第一条匹配的规则或使用默认值）
  const rules = await getAttendanceRules(classId)
  const classRule = rules.find((r) => r.classId === classId)
  const attendanceRateThreshold = classRule?.attendanceRateThreshold ?? DEFAULT_ATTENDANCE_RATE_THRESHOLD
  const consecutiveAbsenceThreshold = classRule?.consecutiveAbsenceThreshold ?? DEFAULT_CONSECUTIVE_ABSENCE_THRESHOLD

  // 拉取该班级所有考勤记录，按 studentId 聚合
  const conditions = [eq(attendanceRecords.classId, classId)]
  if (startDate) conditions.push(gte(attendanceRecords.date, safeParseDate(startDate, "startDate")))
  if (endDate) conditions.push(lte(attendanceRecords.date, safeParseDate(endDate, "endDate")))
  const where = and(...conditions)

  const rows = await db
    .select({
      studentId: attendanceRecords.studentId,
      date: attendanceRecords.date,
      status: attendanceRecords.status,
    })
    .from(attendanceRecords)
    .where(where)
    .orderBy(asc(attendanceRecords.studentId), asc(attendanceRecords.date))

  if (rows.length === 0) {
    return createEmptyWarningSummary(classId, className)
  }

  // 按 studentId 聚合
  const studentMap = new Map<string, { date: string; status: string }[]>()
  for (const r of rows) {
    const list = studentMap.get(r.studentId) ?? []
    list.push({ date: serializeDate(r.date), status: r.status })
    studentMap.set(r.studentId, list)
  }

  // 批量查询学生姓名（委托 users data-access）
  const studentIds = Array.from(studentMap.keys())
  const studentNameMap = await getUserNamesByIds(studentIds)

  const studentStats = studentIds.map((id) => {
    const records = studentMap.get(id) ?? []
    const total = records.length
    const present = records.filter((r) => r.status === "present").length
    return {
      studentId: id,
      studentName: studentNameMap.get(id)?.name ?? "Unknown",
      total,
      present,
      records,
    }
  })

  const warnings = computeAttendanceWarnings(
    studentStats,
    attendanceRateThreshold,
    consecutiveAbsenceThreshold
  )

  return {
    classId,
    className,
    attendanceRateThreshold,
    consecutiveAbsenceThreshold,
    warnings,
  }
}

/**
 * L-4：获取班级考勤趋势（按日/周/月聚合）。
 * 使用纯函数 computeTrendPoints 计算趋势点，便于测试。
 */
export async function getAttendanceTrend(
  classId: string,
  granularity: TrendGranularity,
  startDate?: string,
  endDate?: string
): Promise<AttendanceTrendSummary | null> {
  const className = await getClassNameById(classId)
  if (!className) return null

  const conditions = [eq(attendanceRecords.classId, classId)]
  if (startDate) conditions.push(gte(attendanceRecords.date, safeParseDate(startDate, "startDate")))
  if (endDate) conditions.push(lte(attendanceRecords.date, safeParseDate(endDate, "endDate")))
  const where = and(...conditions)

  const rows = await db
    .select({
      date: attendanceRecords.date,
      status: attendanceRecords.status,
    })
    .from(attendanceRecords)
    .where(where)
    .orderBy(asc(attendanceRecords.date))

  const records = rows.map((r) => ({
    date: serializeDate(r.date),
    status: r.status,
  }))

  const points = computeTrendPoints(records, granularity)

  return {
    classId,
    className,
    granularity,
    startDate: startDate ?? (points[0]?.date ?? new Date().toISOString().slice(0, 10)),
    endDate: endDate ?? (points[points.length - 1]?.date ?? new Date().toISOString().slice(0, 10)),
    points,
  }
}

/**
 * L-7：获取同年级所有班级的出勤率对比。
 * 通过 SQL 聚合按 classId 分组统计，避免拉全量记录。
 */
export async function getClassComparison(
  gradeId: string,
  startDate?: string,
  endDate?: string
): Promise<ClassComparisonSummary | null> {
  const gradeName = await getGradeNameById(gradeId)
  if (!gradeName) return null

  // 获取年级所有班级
  const gradeClasses = await getClassesByGradeId(gradeId)
  if (gradeClasses.length === 0) {
    return {
      gradeId,
      gradeName,
      startDate: startDate ?? new Date().toISOString().slice(0, 10),
      endDate: endDate ?? new Date().toISOString().slice(0, 10),
      items: [],
      averagePresentRate: 0,
    }
  }

  const classIds = gradeClasses.map((c) => c.id)
  const classNameMap = new Map(gradeClasses.map((c) => [c.id, c.name]))

  // 构建时间范围条件
  const conditions = [inArray(attendanceRecords.classId, classIds)]
  if (startDate) conditions.push(gte(attendanceRecords.date, safeParseDate(startDate, "startDate")))
  if (endDate) conditions.push(lte(attendanceRecords.date, safeParseDate(endDate, "endDate")))
  const where = and(...conditions)

  // SQL 聚合：按 classId 分组统计
  const rows = await db
    .select({
      classId: attendanceRecords.classId,
      total: count(),
      present: sql<number>`COALESCE(SUM(CASE WHEN ${attendanceRecords.status} = 'present' THEN 1 ELSE 0 END), 0)`,
      late: sql<number>`COALESCE(SUM(CASE WHEN ${attendanceRecords.status} = 'late' THEN 1 ELSE 0 END), 0)`,
      absent: sql<number>`COALESCE(SUM(CASE WHEN ${attendanceRecords.status} = 'absent' THEN 1 ELSE 0 END), 0)`,
    })
    .from(attendanceRecords)
    .where(where)
    .groupBy(attendanceRecords.classId)

  // 构建对比项（含无记录的班级，total=0）
  const items: ClassComparisonItem[] = classIds.map((cid) => {
    const row = rows.find((r) => r.classId === cid)
    const total = Number(row?.total ?? 0)
    const present = Number(row?.present ?? 0)
    const late = Number(row?.late ?? 0)
    const absent = Number(row?.absent ?? 0)
    return {
      classId: cid,
      className: classNameMap.get(cid) ?? "Unknown",
      total,
      presentRate: total > 0 ? Math.round((present / total) * 10000) / 100 : 0,
      lateRate: total > 0 ? Math.round((late / total) * 10000) / 100 : 0,
      absentRate: total > 0 ? Math.round((absent / total) * 10000) / 100 : 0,
    }
  })

  // 按出勤率降序排序
  items.sort((a, b) => b.presentRate - a.presentRate)

  // 计算年级平均出勤率（加权平均，按 total 加权）
  const totalRecords = items.reduce((sum, i) => sum + i.total, 0)
  const totalPresent = items.reduce((sum, i) => sum + Math.round(i.presentRate * i.total) / 100, 0)
  const averagePresentRate = totalRecords > 0
    ? Math.round((totalPresent / totalRecords) * 10000) / 100
    : 0

  return {
    gradeId,
    gradeName,
    startDate: startDate ?? new Date().toISOString().slice(0, 10),
    endDate: endDate ?? new Date().toISOString().slice(0, 10),
    items,
    averagePresentRate,
  }
}
