import "server-only"

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

import { db } from "@/shared/db"
import {
  attendanceRecords,
  attendanceRules,
} from "@/shared/db/schema"
import { getClassActiveStudentsWithInfo, getClassNamesByIds } from "@/modules/classes/data-access"
import { getUserNamesByIds } from "@/modules/users/data-access"
import { safeParseDate } from "@/shared/lib/action-utils"
import type { DataScope } from "@/shared/types/permissions"

import type {
  AttendanceListItem,
  AttendancePeriod,
  AttendanceQueryParams,
  AttendanceRule,
  AttendanceStats,
  PaginatedAttendanceResult,
} from "./types"
import type {
  AttendanceRuleInput,
  BatchRecordAttendanceInput,
  RecordAttendanceInput,
  UpdateAttendanceInput,
} from "./schema"

const buildScopeFilter = (scope: DataScope): SQL | null => {
  if (scope.type === "all") return null
  if (scope.type === "class_taught") {
    return scope.classIds.length > 0
      ? inArray(attendanceRecords.classId, scope.classIds)
      : sql`1=0`
  }
  if (scope.type === "grade_managed") return sql`1=0`
  if (scope.type === "class_members") return null
  if (scope.type === "children") {
    return scope.childrenIds.length > 0
      ? inArray(attendanceRecords.studentId, scope.childrenIds)
      : sql`1=0`
  }
  if (scope.type === "owned") return eq(attendanceRecords.studentId, scope.userId)
  return sql`1=0`
}

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

/**
 * L-6 节次考勤：构建 period 过滤条件。
 * - period = "full_day"：匹配 period IS NULL OR period = 'full_day'（向后兼容）
 * - period = 其他值：精确匹配
 * - period 未传：返回 null（不按节次过滤）
 */
function buildPeriodFilter(period?: AttendancePeriod): SQL | null {
  if (!period) return null
  if (period === "full_day") {
    return or(isNull(attendanceRecords.period), eq(attendanceRecords.period, "full_day")) as SQL
  }
  return eq(attendanceRecords.period, period)
}

const mapListItem = (
  r: typeof attendanceRecords.$inferSelect,
  studentName: string | null,
  className: string | null,
  recorderName: string
): AttendanceListItem => ({
  id: r.id,
  studentId: r.studentId,
  studentName: studentName ?? "Unknown",
  classId: r.classId,
  className: className ?? "Unknown",
  scheduleId: r.scheduleId ?? null,
  date: serializeDate(r.date),
  status: r.status,
  // L-6：null 视为 full_day（向后兼容），对外暴露统一为 "full_day"
  period: r.period ?? "full_day",
  remark: r.remark ?? null,
  reason: r.reason ?? null,
  recordedBy: r.recordedBy,
  recorderName,
  createdAt: r.createdAt.toISOString(),
})

const resolveRecorderNames = async (
  rows: { record: typeof attendanceRecords.$inferSelect }[]
): Promise<Map<string, string>> => {
  const ids = Array.from(new Set(rows.map((r) => r.record.recordedBy)))
  if (ids.length === 0) return new Map()
  // 委托 users data-access 查询，避免跨模块直查 users 表
  const userMap = await getUserNamesByIds(ids)
  const result = new Map<string, string>()
  for (const [id, user] of userMap) {
    result.set(id, user.name ?? "Unknown")
  }
  return result
}

export async function getAttendanceRecords(
  params: AttendanceQueryParams & { scope: DataScope; currentUserId?: string }
): Promise<PaginatedAttendanceResult> {
  const page = Math.max(1, params.page ?? 1)
  const pageSize = Math.max(1, Math.min(100, params.pageSize ?? 20))
  const conditions: SQL[] = []

  const scopeFilter = buildScopeFilter(params.scope)
  if (scopeFilter) conditions.push(scopeFilter)
  if (params.scope.type === "class_members" && params.currentUserId) {
    conditions.push(eq(attendanceRecords.studentId, params.currentUserId))
  }
  if (params.classId) conditions.push(eq(attendanceRecords.classId, params.classId))
  if (params.studentId) conditions.push(eq(attendanceRecords.studentId, params.studentId))
  if (params.date) conditions.push(eq(attendanceRecords.date, safeParseDate(params.date, "date")))
  if (params.startDate) conditions.push(gte(attendanceRecords.date, safeParseDate(params.startDate, "startDate")))
  if (params.endDate) conditions.push(lte(attendanceRecords.date, safeParseDate(params.endDate, "endDate")))
  if (params.status) conditions.push(eq(attendanceRecords.status, params.status))
  // L-6：按节次过滤
  const periodFilter = buildPeriodFilter(params.period)
  if (periodFilter) conditions.push(periodFilter)

  const where = conditions.length > 0 ? and(...conditions) : undefined

  const [totalRow] = await db.select({ c: count() }).from(attendanceRecords).where(where)
  const total = Number(totalRow?.c ?? 0)
  const totalPages = Math.max(1, Math.ceil(total / pageSize))

  // 仅查询 attendanceRecords 表，姓名通过 data-access 委托查询（避免跨模块 JOIN）
  const rows = await db
    .select({
      record: attendanceRecords,
    })
    .from(attendanceRecords)
    .where(where)
    .orderBy(desc(attendanceRecords.date), desc(attendanceRecords.createdAt))
    .limit(pageSize)
    .offset((page - 1) * pageSize)

  // 批量解析 studentName / className / recorderName（委托 users/classes data-access）
  const studentIds = Array.from(new Set(rows.map((r) => r.record.studentId)))
  const classIds = Array.from(new Set(rows.map((r) => r.record.classId)))
  const [studentNameMap, classNameMap, recorderMap] = await Promise.all([
    getUserNamesByIds(studentIds),
    getClassNamesByIds(classIds),
    resolveRecorderNames(rows),
  ])

  return {
    items: rows.map((r) =>
      mapListItem(
        r.record,
        studentNameMap.get(r.record.studentId)?.name ?? null,
        classNameMap.get(r.record.classId) ?? null,
        recorderMap.get(r.record.recordedBy) ?? "Unknown"
      )
    ),
    total,
    page,
    pageSize,
    totalPages,
  }
}

export async function getClassAttendanceForDate(
  classId: string,
  date: string,
  /** L-6：按节次过滤，未传则返回当日所有节次记录 */
  period?: AttendancePeriod
): Promise<AttendanceListItem[]> {
  const conditions: SQL[] = [
    eq(attendanceRecords.classId, classId),
    eq(attendanceRecords.date, new Date(date)),
  ]
  const periodFilter = buildPeriodFilter(period)
  if (periodFilter) conditions.push(periodFilter)

  const rows = await db
    .select({
      record: attendanceRecords,
    })
    .from(attendanceRecords)
    .where(and(...conditions))
    .orderBy(asc(attendanceRecords.studentId))

  // 批量解析姓名（委托 users/classes data-access）
  const studentIds = Array.from(new Set(rows.map((r) => r.record.studentId)))
  const [studentNameMap, classNameMap] = await Promise.all([
    getUserNamesByIds(studentIds),
    getClassNamesByIds([classId]),
  ])
  const className = classNameMap.get(classId) ?? null

  return rows.map((r) =>
    mapListItem(
      r.record,
      studentNameMap.get(r.record.studentId)?.name ?? null,
      className,
      "Unknown"
    )
  )
}

export async function createAttendanceRecord(
  data: RecordAttendanceInput,
  recordedBy: string
): Promise<string> {
  const id = createId()
  await db.insert(attendanceRecords).values({
    id,
    studentId: data.studentId,
    classId: data.classId,
    scheduleId: data.scheduleId ?? null,
    date: safeParseDate(data.date, "date"),
    status: data.status,
    // L-6：节次考勤，schema 默认 full_day
    period: data.period,
    remark: data.remark ?? null,
    reason: data.reason ?? null,
    recordedBy,
  })
  return id
}

export async function batchCreateAttendanceRecords(
  data: BatchRecordAttendanceInput,
  recordedBy: string
): Promise<number> {
  if (data.records.length === 0) return 0
  const rows = data.records.map((r) => ({
    id: createId(),
    studentId: r.studentId,
    classId: r.classId,
    scheduleId: r.scheduleId ?? null,
    date: safeParseDate(r.date, "date"),
    status: r.status,
    // L-6：节次考勤，schema 默认 full_day
    period: r.period,
    remark: r.remark ?? null,
    reason: r.reason ?? null,
    recordedBy,
  }))
  await db.insert(attendanceRecords).values(rows)
  return rows.length
}

export async function updateAttendanceRecord(
  id: string,
  data: UpdateAttendanceInput
): Promise<void> {
  const update: Partial<typeof attendanceRecords.$inferSelect> = { updatedAt: new Date() }
  if (data.status !== undefined) update.status = data.status
  // L-6：支持更新节次
  if (data.period !== undefined) update.period = data.period
  if (data.remark !== undefined) update.remark = data.remark
  if (data.reason !== undefined) update.reason = data.reason || null
  if (data.scheduleId !== undefined) update.scheduleId = data.scheduleId
  await db.update(attendanceRecords).set(update).where(eq(attendanceRecords.id, id))
}

export async function deleteAttendanceRecord(id: string): Promise<void> {
  await db.delete(attendanceRecords).where(eq(attendanceRecords.id, id))
}

/**
 * 获取考勤记录的 classId（用于资源归属校验）。
 * 返回 null 表示记录不存在。
 */
export async function getAttendanceRecordClassId(id: string): Promise<string | null> {
  const [row] = await db
    .select({ classId: attendanceRecords.classId })
    .from(attendanceRecords)
    .where(eq(attendanceRecords.id, id))
    .limit(1)
  return row?.classId ?? null
}

export async function getClassStudentsForAttendance(
  classId: string
): Promise<Array<{ id: string; name: string; email: string }>> {
  // 通过 classes data-access 获取班级学生，避免跨模块直查 classEnrollments 表
  return getClassActiveStudentsWithInfo(classId)
}

export async function getAttendanceRules(classId?: string): Promise<AttendanceRule[]> {
  const conditions: SQL[] = []
  if (classId) {
    const classCondition = or(eq(attendanceRules.classId, classId), sql`${attendanceRules.classId} IS NULL`)
    if (classCondition) conditions.push(classCondition)
  }
  const rows = await db
    .select()
    .from(attendanceRules)
    .where(conditions.length > 0 ? and(...conditions) : undefined)
    .orderBy(desc(attendanceRules.createdAt))

  return rows.map((r) => ({
    id: r.id,
    classId: r.classId ?? null,
    lateThresholdMinutes: r.lateThresholdMinutes ?? null,
    earlyLeaveThresholdMinutes: r.earlyLeaveThresholdMinutes ?? null,
    enableAutoMark: r.enableAutoMark ?? null,
    attendanceRateThreshold: r.attendanceRateThreshold ?? null,
    consecutiveAbsenceThreshold: r.consecutiveAbsenceThreshold ?? null,
    createdAt: r.createdAt.toISOString(),
    updatedAt: r.updatedAt.toISOString(),
  }))
}

export async function upsertAttendanceRules(data: AttendanceRuleInput): Promise<string> {
  const [existing] = await db
    .select()
    .from(attendanceRules)
    .where(eq(attendanceRules.classId, data.classId))
    .limit(1)

  if (existing) {
    await db
      .update(attendanceRules)
      .set({
        lateThresholdMinutes: data.lateThresholdMinutes ?? 15,
        earlyLeaveThresholdMinutes: data.earlyLeaveThresholdMinutes ?? 15,
        enableAutoMark: data.enableAutoMark ?? false,
        attendanceRateThreshold: data.attendanceRateThreshold ?? 90,
        consecutiveAbsenceThreshold: data.consecutiveAbsenceThreshold ?? 3,
        updatedAt: new Date(),
      })
      .where(eq(attendanceRules.id, existing.id))
    return existing.id
  }

  const id = createId()
  await db.insert(attendanceRules).values({
    id,
    classId: data.classId,
    lateThresholdMinutes: data.lateThresholdMinutes ?? 15,
    earlyLeaveThresholdMinutes: data.earlyLeaveThresholdMinutes ?? 15,
    enableAutoMark: data.enableAutoMark ?? false,
    attendanceRateThreshold: data.attendanceRateThreshold ?? 90,
    consecutiveAbsenceThreshold: data.consecutiveAbsenceThreshold ?? 3,
  })
  return id
}

/**
 * 获取考勤总览统计。
 * 返回统一的 `AttendanceStats` 类型（P2-5 修复：消除数据结构分裂）。
 */
export async function getAttendanceStats(params: {
  scope: DataScope
  currentUserId: string
  classId?: string
  date?: string
}): Promise<AttendanceStats> {
  // 直接使用 SQL 聚合查询，避免分页截断导致统计失真（P0 修复）
  const conditions: SQL[] = []

  const scopeFilter = buildScopeFilter(params.scope)
  if (scopeFilter) conditions.push(scopeFilter)
  if (params.scope.type === "class_members" && params.currentUserId) {
    conditions.push(eq(attendanceRecords.studentId, params.currentUserId))
  }
  if (params.classId) conditions.push(eq(attendanceRecords.classId, params.classId))
  if (params.date) conditions.push(eq(attendanceRecords.date, safeParseDate(params.date, "date")))

  const where = conditions.length > 0 ? and(...conditions) : undefined

  const [row] = 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 total = Number(row?.total ?? 0)
  const present = Number(row?.present ?? 0)
  const absent = Number(row?.absent ?? 0)
  const late = Number(row?.late ?? 0)
  const earlyLeave = Number(row?.earlyLeave ?? 0)
  const excused = Number(row?.excused ?? 0)
  const schoolActivity = Number(row?.schoolActivity ?? 0)

  return {
    total,
    present,
    absent,
    late,
    earlyLeave,
    excused,
    schoolActivity,
    presentRate: total > 0 ? Math.round((present / total) * 10000) / 100 : 0,
    lateRate: total > 0 ? Math.round((late / total) * 10000) / 100 : 0,
  }
}

/**
 * L-5：将请假申请同步为考勤记录（status='excused'）。
 *
 * 同步策略：
 * - 遍历 [startDate, endDate] 区间每一天
 * - 查询当天是否已有该学生的考勤记录
 * - 无记录 → 插入 excused 记录（recordedBy = reviewerId，reason 注明请假类型）
 * - 已存在非 excused 记录 → 跳过（不覆盖教师手动录入）
 * - 已存在 excused 记录 → 跳过（幂等）
 *
 * @returns 实际插入的记录数
 */
export async function syncExcusedFromLeaveRequest(params: {
  studentId: string
  classId: string
  startDate: string
  endDate: string
  reason: string
  reviewerId: string
}): Promise<number> {
  const { studentId, classId, startDate, endDate, reason, reviewerId } = params
  const start = new Date(startDate)
  const end = new Date(endDate)
  if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime()) || start > end) {
    return 0
  }

  // 构造日期区间（含两端，按天递增）
  const dates: Date[] = []
  const cursor = new Date(start)
  cursor.setUTCHours(0, 0, 0, 0)
  const endBoundary = new Date(end)
  endBoundary.setUTCHours(0, 0, 0, 0)
  while (cursor <= endBoundary) {
    dates.push(new Date(cursor))
    cursor.setUTCDate(cursor.getUTCDate() + 1)
  }

  // 一次性查询该学生区间内所有现有考勤记录
  const existing = await db
    .select({
      date: attendanceRecords.date,
      status: attendanceRecords.status,
    })
    .from(attendanceRecords)
    .where(
      and(
        eq(attendanceRecords.studentId, studentId),
        eq(attendanceRecords.classId, classId),
        gte(attendanceRecords.date, start),
        lte(attendanceRecords.date, end),
      ),
    )

  const existingDateKeys = new Set(
    existing.map((r) => new Date(r.date).toISOString().slice(0, 10)),
  )

  // 仅插入不存在的日期
  const toInsert = dates
    .filter((d) => !existingDateKeys.has(d.toISOString().slice(0, 10)))
    .map((d) => ({
      id: createId(),
      studentId,
      classId,
      scheduleId: null,
      date: d,
      status: "excused" as const,
      // L-6：请假同步为全日 excused 记录
      period: "full_day" as const,
      remark: null,
      reason,
      recordedBy: reviewerId,
    }))

  if (toInsert.length === 0) return 0
  await db.insert(attendanceRecords).values(toInsert)
  return toInsert.length
}
