import "server-only";

import { and, asc, eq, inArray } from "drizzle-orm"

import { db } from "@/shared/db"
import {
  classes,
  classEnrollments,
  classSubjectTeachers,
  roles,
  users,
  usersToRoles,
} from "@/shared/db/schema"
import { ROLE_NAMES } from "@/shared/types/permissions"
import { DEFAULT_CLASS_SUBJECTS } from "./types"

export const getSessionTeacherId = async (): Promise<string | null> => {
  const { auth } = await import("@/auth")
  const session = await auth()
  const userId = String(session?.user?.id ?? "").trim()
  if (!userId) return null

  const [teacher] = await db
    .select({ id: users.id })
    .from(users)
    .innerJoin(usersToRoles, eq(usersToRoles.userId, users.id))
    .innerJoin(roles, eq(usersToRoles.roleId, roles.id))
    .where(and(eq(users.id, userId), eq(roles.name, ROLE_NAMES.TEACHER)))
    .limit(1)
  return teacher?.id ?? null
}

export const getTeacherIdForMutations = async (): Promise<string> => {
  const teacherId = await getSessionTeacherId()
  if (!teacherId) throw new Error("Teacher not found")
  return teacherId
}

export const getClassSubjects = async (): Promise<string[]> => {
  const rows = await db.query.subjects.findMany({
    orderBy: (subjects, { asc }) => [asc(subjects.order), asc(subjects.name)],
  })

  const names = rows.map((r) => r.name.trim()).filter((n) => n.length > 0)
  // P1-5: subjects 表为空时回退到默认科目列表，避免班级创建流程中断
  return Array.from(new Set(names.length > 0 ? names : DEFAULT_CLASS_SUBJECTS))
}

const normalizeSortText = (v: string | null | undefined): string =>
  typeof v === "string" ? v.trim().toLowerCase() : ""

const parseFirstInt = (v: string): number | null => {
  const m = v.match(/\d+/)
  return m ? Number(m[0]) : null
}

const compareGradeLabel = (a: string, b: string): number => {
  const aNum = parseFirstInt(a)
  const bNum = parseFirstInt(b)
  if (typeof aNum === "number" && typeof bNum === "number" && aNum !== bNum) return aNum - bNum
  return a.localeCompare(b)
}

export const compareClassLike = (
  a: { schoolName?: string | null; grade: string; name: string; homeroom?: string | null; room?: string | null },
  b: { schoolName?: string | null; grade: string; name: string; homeroom?: string | null; room?: string | null }
): number => {
  const schoolCmp = normalizeSortText(a.schoolName).localeCompare(normalizeSortText(b.schoolName))
  if (schoolCmp !== 0) return schoolCmp

  const gradeCmp = compareGradeLabel(a.grade, b.grade)
  if (gradeCmp !== 0) return gradeCmp

  const nameCmp = normalizeSortText(a.name).localeCompare(normalizeSortText(b.name))
  if (nameCmp !== 0) return nameCmp

  const hrCmp = normalizeSortText(a.homeroom).localeCompare(normalizeSortText(b.homeroom))
  if (hrCmp !== 0) return hrCmp

  return normalizeSortText(a.room).localeCompare(normalizeSortText(b.room))
}

export const getAccessibleClassIdsForTeacher = async (teacherId: string): Promise<string[]> => {
  const [ownedIds, assignedIds] = await Promise.all([
    db.select({ id: classes.id }).from(classes).where(eq(classes.teacherId, teacherId)),
    db
      .select({ id: classSubjectTeachers.classId })
      .from(classSubjectTeachers)
      .where(eq(classSubjectTeachers.teacherId, teacherId)),
  ])
  return Array.from(new Set([...ownedIds.map((x) => x.id), ...assignedIds.map((x) => x.id)]))
}

/**
 * Verify that a teacher owns a class (teacherId match on classes row).
 * Used by scheduling module to gate classSchedule writes.
 */
export async function verifyTeacherOwnsClass(classId: string, teacherId: string): Promise<boolean> {
  const [owned] = await db
    .select({ id: classes.id })
    .from(classes)
    .where(and(eq(classes.id, classId), eq(classes.teacherId, teacherId)))
    .limit(1)
  return Boolean(owned)
}

export const getClassGradeIdsByClassIds = async (classIds: string[]): Promise<Map<string, string>> => {
  if (classIds.length === 0) return new Map()
  const rows = await db
    .select({ id: classes.id, gradeId: classes.gradeId })
    .from(classes)
    .where(inArray(classes.id, classIds))
  const map = new Map<string, string>()
  for (const row of rows) {
    if (typeof row.gradeId === "string" && row.gradeId.trim().length > 0) {
      map.set(row.id, row.gradeId)
    }
  }
  return map
}

export const getTeacherSubjectIdsForClass = async (teacherId: string, classId: string): Promise<string[]> => {
  const rows = await db
    .select({ subjectId: classSubjectTeachers.subjectId })
    .from(classSubjectTeachers)
    .where(and(eq(classSubjectTeachers.teacherId, teacherId), eq(classSubjectTeachers.classId, classId)))
  return Array.from(new Set(rows.map((r) => String(r.subjectId))))
}

/**
 * 获取班级的教师 ID（班主任）。
 * 供跨模块调用使用，避免直接查询 classes 表。
 */
export const getClassTeacherById = async (classId: string): Promise<string | null> => {
  const [row] = await db
    .select({ teacherId: classes.teacherId })
    .from(classes)
    .where(eq(classes.id, classId))
    .limit(1)
  return row?.teacherId ?? null
}

/**
 * 获取班级所有学生 ID（不限状态）。
 * 供跨模块调用使用，避免直接查询 classEnrollments 表。
 */
export const getStudentIdsByClassId = async (classId: string): Promise<string[]> => {
  const rows = await db
    .select({ studentId: classEnrollments.studentId })
    .from(classEnrollments)
    .where(eq(classEnrollments.classId, classId))
  return rows.map((r) => r.studentId)
}

/**
 * 获取多个班级的所有学生 ID（不限状态）。
 * 供跨模块调用使用，避免直接查询 classEnrollments 表。
 */
export const getStudentIdsByClassIds = async (classIds: string[]): Promise<string[]> => {
  if (classIds.length === 0) return []
  const rows = await db
    .select({ studentId: classEnrollments.studentId })
    .from(classEnrollments)
    .where(inArray(classEnrollments.classId, classIds))
  return Array.from(new Set(rows.map((r) => r.studentId)))
}

/**
 * 获取班级所有活跃学生 ID（status = 'active'）。
 * 供跨模块调用使用，避免直接查询 classEnrollments 表。
 */
export const getActiveStudentIdsByClassId = async (classId: string): Promise<string[]> => {
  const rows = await db
    .select({ studentId: classEnrollments.studentId })
    .from(classEnrollments)
    .where(and(eq(classEnrollments.classId, classId), eq(classEnrollments.status, "active")))
  return rows.map((r) => r.studentId)
}

/**
 * 获取班级所有活跃学生基本信息（id/name/email），按姓名升序。
 * 供跨模块调用使用（如考勤点名），避免直接查询 classEnrollments 表。
 */
export const getClassActiveStudentsWithInfo = async (
  classId: string
): Promise<Array<{ id: string; name: string; email: string }>> => {
  const rows = await db
    .select({ id: users.id, name: users.name, email: users.email })
    .from(classEnrollments)
    .innerJoin(users, eq(users.id, classEnrollments.studentId))
    .where(and(eq(classEnrollments.classId, classId), eq(classEnrollments.status, "active")))
    .orderBy(asc(users.name))
  return rows.map((r) => ({ id: r.id, name: r.name ?? "Unknown", email: r.email }))
}

/**
 * 获取教师在一个班级所教的科目 ID 列表。
 * 参数顺序为 (classId, teacherId)，供跨模块调用使用。
 */
export const getTeacherSubjectIdsByClass = async (classId: string, teacherId: string): Promise<string[]> => {
  return getTeacherSubjectIdsForClass(teacherId, classId)
}

/**
 * 获取多个班级的所有教师 ID（班主任 + 任课教师）。
 * 供跨模块调用使用，避免直接查询 classes / classSubjectTeachers 表。
 */
export const getTeacherIdsByClassIds = async (classIds: string[]): Promise<string[]> => {
  if (classIds.length === 0) return []
  const [homeroomRows, subjectRows] = await Promise.all([
    db
      .select({ teacherId: classes.teacherId })
      .from(classes)
      .where(inArray(classes.id, classIds)),
    db
      .select({ teacherId: classSubjectTeachers.teacherId })
      .from(classSubjectTeachers)
      .where(inArray(classSubjectTeachers.classId, classIds)),
  ])
  const set = new Set<string>()
  for (const r of homeroomRows) {
    if (r.teacherId) set.add(r.teacherId)
  }
  for (const r of subjectRows) {
    if (r.teacherId) set.add(r.teacherId)
  }
  return Array.from(set)
}

/**
 * 获取学生当前活跃班级的 ID。
 * 供跨模块调用使用，避免直接查询 classEnrollments 表。
 */
export const getStudentActiveClassId = async (studentId: string): Promise<string | null> => {
  const [row] = await db
    .select({ classId: classEnrollments.classId })
    .from(classEnrollments)
    .where(and(eq(classEnrollments.studentId, studentId), eq(classEnrollments.status, "active")))
    .orderBy(asc(classEnrollments.createdAt))
    .limit(1)
  return row?.classId ?? null
}

/**
 * 获取学生当前活跃班级的 ID 与名称（一次 JOIN 查询）。
 * 供跨模块调用使用，避免分别查询 classEnrollments 与 classes 表。
 */
export const getStudentActiveClass = async (
  studentId: string,
): Promise<{ classId: string; className: string } | null> => {
  const [row] = await db
    .select({ classId: classes.id, className: classes.name })
    .from(classEnrollments)
    .innerJoin(classes, eq(classes.id, classEnrollments.classId))
    .where(and(eq(classEnrollments.studentId, studentId), eq(classEnrollments.status, "active")))
    .orderBy(asc(classEnrollments.createdAt))
    .limit(1)
  return row ?? null
}

/**
 * 获取学生当前活跃班级对应的年级 ID。
 * 供跨模块调用使用，避免直接查询 classEnrollments/classes 表。
 */
export const getStudentActiveGradeId = async (studentId: string): Promise<string | null> => {
  const [row] = await db
    .select({ gradeId: classes.gradeId })
    .from(classEnrollments)
    .innerJoin(classes, eq(classes.id, classEnrollments.classId))
    .where(and(eq(classEnrollments.studentId, studentId), eq(classEnrollments.status, "active")))
    .orderBy(asc(classEnrollments.createdAt))
    .limit(1)
  return row?.gradeId ?? null
}

/**
 * 校验班级是否存在。
 * 供跨模块调用使用，避免直接查询 classes 表。
 */
export const getClassExists = async (classId: string): Promise<boolean> => {
  const [row] = await db
    .select({ id: classes.id })
    .from(classes)
    .where(eq(classes.id, classId))
    .limit(1)
  return Boolean(row)
}

/**
 * 获取班级名称。
 * 供跨模块调用使用，避免直接查询 classes 表。
 */
export const getClassNameById = async (classId: string): Promise<string | null> => {
  const [row] = await db
    .select({ name: classes.name })
    .from(classes)
    .where(eq(classes.id, classId))
    .limit(1)
  return row?.name ?? null
}

/**
 * 获取班级关联的年级 ID。
 * 供跨模块调用使用，避免直接查询 classes 表。
 */
export const getClassGradeId = async (classId: string): Promise<string | null> => {
  const [row] = await db
    .select({ gradeId: classes.gradeId })
    .from(classes)
    .where(eq(classes.id, classId))
    .limit(1)
  return row?.gradeId ?? null
}

/**
 * 获取多个班级关联的年级 ID 列表（去重，过滤空值）。
 * 供跨模块调用使用，避免直接查询 classes 表。
 */
export const getGradeIdsByClassIds = async (classIds: string[]): Promise<string[]> => {
  if (classIds.length === 0) return []
  const rows = await db
    .selectDistinct({ gradeId: classes.gradeId })
    .from(classes)
    .where(inArray(classes.id, classIds))
  return rows
    .map((r) => r.gradeId)
    .filter((id): id is string => typeof id === "string" && id.length > 0)
}

/**
 * 批量获取班级名称（Map<classId, name>）。
 * 供跨模块调用使用，避免直接查询 classes 表。
 */
export const getClassNamesByIds = async (classIds: string[]): Promise<Map<string, string>> => {
  const result = new Map<string, string>()
  const uniqueIds = Array.from(new Set(classIds.filter((v): v is string => typeof v === "string" && v.length > 0)))
  if (uniqueIds.length === 0) return result

  const rows = await db
    .select({ id: classes.id, name: classes.name })
    .from(classes)
    .where(inArray(classes.id, uniqueIds))

  for (const r of rows) result.set(r.id, r.name)
  return result
}

/**
 * 获取指定年级下的所有班级（id + name）。
 * 供跨模块调用使用，避免直接查询 classes 表。
 */
export const getClassesByGradeId = async (gradeId: string): Promise<Array<{ id: string; name: string }>> => {
  if (!gradeId) return []
  const rows = await db
    .select({ id: classes.id, name: classes.name })
    .from(classes)
    .where(eq(classes.gradeId, gradeId))
  return rows.map((r) => ({ id: r.id, name: r.name }))
}

/**
 * 获取多个年级下的所有班级 ID（供 grades 模块 grade_managed scope 过滤使用）。
 * 供跨模块调用使用，避免直接查询 classes 表。
 */
export const getClassIdsByGradeIds = async (gradeIds: string[]): Promise<string[]> => {
  const uniqueIds = Array.from(new Set(gradeIds.filter((v): v is string => typeof v === "string" && v.length > 0)))
  if (uniqueIds.length === 0) return []
  const rows = await db
    .select({ id: classes.id })
    .from(classes)
    .where(inArray(classes.gradeId, uniqueIds))
  return rows.map((r) => r.id)
}

/**
 * 构建一个 Drizzle 子查询 SQL，用于过滤 classId IN (SELECT id FROM classes WHERE grade_id IN (...))。
 * 供 grades 模块 grade_managed scope 同步构建 SQL 过滤条件使用，避免直接查询 classes 表。
 */
export const getClassIdsByGradeIdsSubquery = (gradeIds: string[]) => {
  return db.select({ id: classes.id }).from(classes).where(inArray(classes.gradeId, gradeIds))
}

// Re-export from split files for backward compatibility
export * from "./data-access-stats"
export * from "./data-access-schedule"
export * from "./data-access-students"
export * from "./data-access-admin"
export * from "./data-access-invitations"
export * from "./data-access-teacher"

// ---------------------------------------------------------------------------
// DataScope resolver helpers (P1-5/P1-6 audit fix)
//
// P0-1 audit fix (2026-06-25): 下列 `getTeacherScopeData` / `getStudentScopeData`
// / `getGradeIdsForStudentIds` 三个函数原本在本文件与拆分文件中重复定义，
// ES 模块语义下本地定义会覆盖 `export *` 的同名导出，导致：
//   - 通过 `@/modules/classes/data-access` 导入时得到本地版本
//   - 通过 `@/modules/classes/data-access-teacher` / `-students` 导入时得到另一份实现
// 两份实现逻辑一致，但任何一方修改都不会同步，是高风险维护陷阱。
//
// 修复：删除本文件中的本地定义，统一从拆分文件通过 `export *` 暴露。
// 实际定义见：
//   - getTeacherScopeData  → data-access-teacher.ts
//   - getStudentScopeData  → data-access-students.ts
//   - getGradeIdsForStudentIds → data-access-students.ts
// ---------------------------------------------------------------------------
