import { 
  mysqlTable, 
  varchar, 
  text, 
  timestamp, 
  int, 
  primaryKey,
  index,
  uniqueIndex,
  json,
  mysqlEnum,
  boolean,
  foreignKey,
  date,
  datetime,
  decimal,
  bigint
} from "drizzle-orm/mysql-core";
import { createId } from "@paralleldrive/cuid2";
import type { AdapterAccountType } from "next-auth/adapters";

// --- Helper for ID generation (CUID2) ---
const id = (name: string) => varchar(name, { length: 128 }).notNull().$defaultFn(() => createId());

// --- 1. Users & Auth (Auth.js v5 Standard + RBAC) ---

export const users = mysqlTable("users", {
  id: id("id").primaryKey(),
  name: varchar("name", { length: 255 }),
  email: varchar("email", { length: 255 }).notNull().unique(),
  emailVerified: timestamp("emailVerified", { mode: "date" }),
  image: varchar("image", { length: 255 }),
  // Credentials Auth (Optional)
  password: varchar("password", { length: 255 }), 

  phone: varchar("phone", { length: 30 }),
  address: varchar("address", { length: 255 }),
  gender: varchar("gender", { length: 20 }),
  age: int("age"),
  gradeId: varchar("grade_id", { length: 128 }),
  departmentId: varchar("department_id", { length: 128 }),
  onboardedAt: timestamp("onboarded_at", { mode: "date" }),

  // 未成年人信息保护
  birthDate: date("birth_date"),
  guardianName: varchar("guardian_name", { length: 255 }),
  guardianPhone: varchar("guardian_phone", { length: 20 }),
  guardianRelation: varchar("guardian_relation", { length: 50 }),
  consentAcceptedAt: datetime("consent_accepted_at"),
  
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  emailIdx: index("email_idx").on(table.email),
  gradeIdIdx: index("users_grade_id_idx").on(table.gradeId),
  departmentIdIdx: index("users_department_id_idx").on(table.departmentId),
}));

// Auth.js: Accounts (OAuth providers)
export const accounts = mysqlTable("accounts", {
    userId: varchar("userId", { length: 128 })
      .notNull()
      .references(() => users.id, { onDelete: "cascade" }),
    type: varchar("type", { length: 255 }).$type<AdapterAccountType>().notNull(),
    provider: varchar("provider", { length: 255 }).notNull(),
    providerAccountId: varchar("providerAccountId", { length: 255 }).notNull(),
    refresh_token: text("refresh_token"),
    access_token: text("access_token"),
    expires_at: int("expires_at"),
    token_type: varchar("token_type", { length: 255 }),
    scope: varchar("scope", { length: 255 }),
    id_token: text("id_token"),
    session_state: varchar("session_state", { length: 255 }),
  },
  (account) => ({
    compoundKey: primaryKey({
      columns: [account.provider, account.providerAccountId],
    }),
    userIdIdx: index("account_userId_idx").on(account.userId),
  })
);

/**
 * @deprecated audit-P1-9：此表在 JWT 策略下从未被写入数据。
 *
 * NextAuth 配置为 `session: { strategy: "jwt" }`（见 `src/auth.ts`），
 * 所有 session 信息存储在 JWT cookie 中，不写入此表。
 * `revokeAllOtherSessionsAction` 已改为 no-op（返回 0 并记录日志）。
 * `getUsersDashboardStats` 已不再查询此表。
 *
 * 此表定义暂时保留是为了：
 * 1. 避免删除表定义后 drizzle-kit 生成 DROP TABLE 迁移误删生产数据
 * 2. 未来若切换为 `strategy: "database"` 或引入 JWT 黑名单可复用
 *
 * 后续应在显式迁移中删除此表（同时删除 relations.ts 中的 sessionsRelations）。
 */
// Auth.js: Sessions
export const sessions = mysqlTable("sessions", {
  sessionToken: varchar("sessionToken", { length: 255 }).primaryKey(),
  userId: varchar("userId", { length: 128 })
    .notNull()
    .references(() => users.id, { onDelete: "cascade" }),
  expires: timestamp("expires", { mode: "date" }).notNull(),
}, (table) => ({
  userIdIdx: index("session_userId_idx").on(table.userId),
}));

// Auth.js: Verification Tokens
export const verificationTokens = mysqlTable("verificationTokens", {
  identifier: varchar("identifier", { length: 255 }).notNull(),
  token: varchar("token", { length: 255 }).notNull(),
  expires: timestamp("expires", { mode: "date" }).notNull(),
}, (vt) => ({
  compoundKey: primaryKey({ columns: [vt.identifier, vt.token] }),
}));

// --- Custom RBAC Extensions ---

export const roles = mysqlTable("roles", {
  id: id("id").primaryKey(),
  name: varchar("name", { length: 50 }).notNull().unique(), // e.g., 'admin', 'teacher', 'student', 'grade_head'
  description: varchar("description", { length: 255 }),
  // Builtin roles (admin/teacher/student/parent/grade_head/teaching_head) are
  // seeded with is_system = true and cannot be deleted. The `admin` role is
  // additionally fully locked (no permission edits, no disable, no rename).
  isSystem: boolean("is_system").default(false).notNull(),
  // Disabled roles do not contribute permissions at login time.
  isEnabled: boolean("is_enabled").default(true).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
});

// Many-to-Many: Users <-> Roles
// Solves: "A teacher can also be a Grade Head"
export const usersToRoles = mysqlTable("users_to_roles", {
  userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  roleId: varchar("role_id", { length: 128 }).notNull().references(() => roles.id, { onDelete: "cascade" }),
}, (table) => ({
  pk: primaryKey({ columns: [table.userId, table.roleId] }),
  userIdIdx: index("user_id_idx").on(table.userId),
}));

// Role -> Permissions (fine-grained RBAC)
export const rolePermissions = mysqlTable("role_permissions", {
  roleId: varchar("role_id", { length: 128 }).notNull().references(() => roles.id, { onDelete: "cascade" }),
  permission: varchar("permission", { length: 100 }).notNull(),
}, (table) => ({
  pk: primaryKey({ columns: [table.roleId, table.permission] }),
  roleIdIdx: index("role_permissions_role_idx").on(table.roleId),
}));

// --- 2. Knowledge Points (Tree Structure) ---

export const knowledgePoints = mysqlTable("knowledge_points", {
  id: id("id").primaryKey(),
  name: varchar("name", { length: 255 }).notNull(),
  description: text("description"),
  anchorText: varchar("anchor_text", { length: 255 }),
  
  // Tree Structure: Parent KP
  parentId: varchar("parent_id", { length: 128 }), // Self-reference defined in relations

  chapterId: varchar("chapter_id", { length: 128 }),
  
  // Metadata for ordering or level
  level: int("level").default(0),
  order: int("order").default(0),

  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  parentIdIdx: index("parent_id_idx").on(table.parentId),
  chapterIdIdx: index("kp_chapter_id_idx").on(table.chapterId),
}));

// --- 知识点前置依赖（知识图谱） ---
export const knowledgePointPrerequisites = mysqlTable("knowledge_point_prerequisites", {
  knowledgePointId: varchar("knowledge_point_id", { length: 128 }).notNull(),
  prerequisiteKpId: varchar("prerequisite_kp_id", { length: 128 }).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  kpPairPk: primaryKey({ columns: [table.knowledgePointId, table.prerequisiteKpId], name: "kp_prereq_pk" }),
  kpIdx: index("kp_prereq_kp_idx").on(table.knowledgePointId),
  prereqIdx: index("kp_prereq_prereq_idx").on(table.prerequisiteKpId),
  kpFk: foreignKey({
    columns: [table.knowledgePointId],
    foreignColumns: [knowledgePoints.id],
    name: "kp_prereq_kp_fk",
  }).onDelete("cascade"),
  prereqFk: foreignKey({
    columns: [table.prerequisiteKpId],
    foreignColumns: [knowledgePoints.id],
    name: "kp_prereq_prereq_fk",
  }).onDelete("cascade"),
}));

// --- 3. Question Bank (Core) ---

export const questionTypeEnum = mysqlEnum("type", ["single_choice", "multiple_choice", "text", "judgment", "composite"]);

export const questions = mysqlTable("questions", {
  id: id("id").primaryKey(),
  
  // Content can be JSON to store rich text, images, etc. or just text.
  // Using JSON for flexibility in a modern ed-tech app (e.g. SlateJS nodes).
  content: json("content").notNull(), 
  
  type: questionTypeEnum.notNull(),
  difficulty: int("difficulty").default(1), // 1-5
  
  // Self-reference for "Infinite Nesting" (Parent Question)
  // e.g., A reading comprehension passage (Parent) -> 5 sub-questions (Children)
  parentId: varchar("parent_id", { length: 128 }), 

  authorId: varchar("author_id", { length: 128 }).notNull().references(() => users.id),
  
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  parentIdIdx: index("parent_id_idx").on(table.parentId), // Critical for querying children
  authorIdIdx: index("author_id_idx").on(table.authorId),
  // In a real large-scale system, we might add Full-Text Search index on content
}));

// Many-to-Many: Questions <-> Knowledge Points
export const questionsToKnowledgePoints = mysqlTable("questions_to_knowledge_points", {
  questionId: varchar("question_id", { length: 128 }).notNull(),
  knowledgePointId: varchar("knowledge_point_id", { length: 128 }).notNull(),
}, (table) => ({
  pk: primaryKey({ columns: [table.questionId, table.knowledgePointId] }),
  kpIdx: index("kp_idx").on(table.knowledgePointId), // For querying "All questions under this KP"
  qFk: foreignKey({
    columns: [table.questionId],
    foreignColumns: [questions.id],
    name: "q_kp_qid_fk"
  }).onDelete("cascade"),
  kpFk: foreignKey({
    columns: [table.knowledgePointId],
    foreignColumns: [knowledgePoints.id],
    name: "q_kp_kpid_fk"
  }).onDelete("cascade"),
}));

// --- 4. Academic / Teaching Flow ---

export const subjects = mysqlTable("subjects", {
  id: id("id").primaryKey(),
  name: varchar("name", { length: 100 }).notNull().unique(),
  code: varchar("code", { length: 50 }).unique(),
  order: int("order").default(0),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  nameIdx: index("subjects_name_idx").on(table.name),
}));

export const textbooks = mysqlTable("textbooks", {
  id: id("id").primaryKey(),
  title: varchar("title", { length: 255 }).notNull(),
  subject: varchar("subject", { length: 100 }).notNull(),
  grade: varchar("grade", { length: 50 }),
  publisher: varchar("publisher", { length: 100 }),
  
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
});

export const chapters = mysqlTable("chapters", {
  id: id("id").primaryKey(),
  textbookId: varchar("textbook_id", { length: 128 }).notNull().references(() => textbooks.id, { onDelete: "cascade" }),
  title: varchar("title", { length: 255 }).notNull(),
  order: int("order").default(0),
  
  // Chapters can also be nested
  parentId: varchar("parent_id", { length: 128 }),

  content: text("content"),
  
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  textbookIdx: index("textbook_idx").on(table.textbookId),
  parentIdIdx: index("parent_id_idx").on(table.parentId),
}));

// --- 5. School Management ---

export const departments = mysqlTable("departments", {
  id: id("id").primaryKey(),
  name: varchar("name", { length: 255 }).notNull().unique(),
  description: text("description"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  nameIdx: index("departments_name_idx").on(table.name),
}))

export const classrooms = mysqlTable("classrooms", {
  id: id("id").primaryKey(),
  name: varchar("name", { length: 255 }).notNull().unique(),
  building: varchar("building", { length: 100 }),
  floor: int("floor"),
  capacity: int("capacity"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  nameIdx: index("classrooms_name_idx").on(table.name),
}))

export const academicYears = mysqlTable("academic_years", {
  id: id("id").primaryKey(),
  name: varchar("name", { length: 100 }).notNull().unique(),
  startDate: timestamp("start_date", { mode: "date" }).notNull(),
  endDate: timestamp("end_date", { mode: "date" }).notNull(),
  isActive: boolean("is_active").default(false).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  nameIdx: index("academic_years_name_idx").on(table.name),
  activeIdx: index("academic_years_active_idx").on(table.isActive),
}))

export const schools = mysqlTable("schools", {
  id: id("id").primaryKey(),
  name: varchar("name", { length: 255 }).notNull().unique(),
  code: varchar("code", { length: 50 }).unique(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  nameIdx: index("schools_name_idx").on(table.name),
  codeIdx: index("schools_code_idx").on(table.code),
}))

export const grades = mysqlTable("grades", {
  id: id("id").primaryKey(),
  schoolId: varchar("school_id", { length: 128 }).notNull(),
  name: varchar("name", { length: 100 }).notNull(),
  order: int("order").default(0).notNull(),
  gradeHeadId: varchar("grade_head_id", { length: 128 }),
  teachingHeadId: varchar("teaching_head_id", { length: 128 }),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  schoolIdx: index("grades_school_idx").on(table.schoolId),
  schoolNameUnique: index("grades_school_name_uniq").on(table.schoolId, table.name),
  gradeHeadIdx: index("grades_grade_head_idx").on(table.gradeHeadId),
  teachingHeadIdx: index("grades_teaching_head_idx").on(table.teachingHeadId),
  schoolFk: foreignKey({
    columns: [table.schoolId],
    foreignColumns: [schools.id],
    name: "g_s_fk",
  }).onDelete("cascade"),
  gradeHeadFk: foreignKey({
    columns: [table.gradeHeadId],
    foreignColumns: [users.id],
    name: "g_gh_fk",
  }).onDelete("set null"),
  teachingHeadFk: foreignKey({
    columns: [table.teachingHeadId],
    foreignColumns: [users.id],
    name: "g_th_fk",
  }).onDelete("set null"),
}))

// --- 6. Classes / Enrollment / Schedule ---

export const classEnrollmentStatusEnum = mysqlEnum("class_enrollment_status", ["active", "inactive"]);

export const classes = mysqlTable("classes", {
  id: id("id").primaryKey(),
  schoolName: varchar("school_name", { length: 255 }),
  schoolId: varchar("school_id", { length: 128 }),
  name: varchar("name", { length: 255 }).notNull(),
  grade: varchar("grade", { length: 50 }).notNull(),
  gradeId: varchar("grade_id", { length: 128 }),
  homeroom: varchar("homeroom", { length: 50 }),
  room: varchar("room", { length: 50 }),
  invitationCode: varchar("invitation_code", { length: 6 }).unique(),
  teacherId: varchar("teacher_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  teacherIdx: index("classes_teacher_idx").on(table.teacherId),
  gradeIdx: index("classes_grade_idx").on(table.grade),
  schoolIdx: index("classes_school_idx").on(table.schoolId),
  gradeIdIdx: index("classes_grade_id_idx").on(table.gradeId),
  schoolFk: foreignKey({
    columns: [table.schoolId],
    foreignColumns: [schools.id],
    name: "c_s_fk",
  }).onDelete("set null"),
  gradeFk: foreignKey({
    columns: [table.gradeId],
    foreignColumns: [grades.id],
    name: "c_g_fk",
  }).onDelete("set null"),
}));

export const classSubjectTeachers = mysqlTable("class_subject_teachers", {
  classId: varchar("class_id", { length: 128 }).notNull(),
  subjectId: varchar("subject_id", { length: 128 }).notNull(),
  teacherId: varchar("teacher_id", { length: 128 }).references(() => users.id, { onDelete: "set null" }),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  pk: primaryKey({ columns: [table.classId, table.subjectId] }),
  classIdx: index("class_subject_teachers_class_idx").on(table.classId),
  teacherIdx: index("class_subject_teachers_teacher_idx").on(table.teacherId),
  subjectIdIdx: index("class_subject_teachers_subject_id_idx").on(table.subjectId),
  classFk: foreignKey({
    columns: [table.classId],
    foreignColumns: [classes.id],
    name: "cst_c_fk",
  }).onDelete("cascade"),
  subjectFk: foreignKey({
    columns: [table.subjectId],
    foreignColumns: [subjects.id],
    name: "cst_s_fk",
  }).onDelete("cascade"),
}));

/**
 * 班级邀请码表（v3 新增，对标 Google Classroom / 钉钉教育 / 智学网）。
 *
 * 设计要点：
 * - 独立表而非挂在 classes 表上，支持有效期/次数/审计/多码并存
 * - 6 位字母数字（剔除歧义字符 0/O/1/I/L），空间 22^6 ≈ 1.13 亿
 * - status 枚举：active/disabled/expired/exhausted
 * - max_uses NULL=无限；expires_at NULL=永久
 * - 软删除：revoke 时设置 status=disabled + revoked_at，不物理删除（审计需要）
 *
 * 与 classes.invitationCode 的关系：
 * - 新表上线后，enrollStudentByInvitationCode / enrollTeacherByInvitationCode 优先查新表
 * - classes.invitationCode 保留作为 fallback，下个版本移除
 */
export const classInvitationCodeStatusEnum = mysqlEnum("class_invitation_code_status", [
  "active",
  "disabled",
  "expired",
  "exhausted",
]);

export const classInvitationCodes = mysqlTable("class_invitation_codes", {
  id: id("id").primaryKey(),
  classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }),
  code: varchar("code", { length: 8 }).notNull().unique(),
  status: classInvitationCodeStatusEnum.default("active").notNull(),
  maxUses: int("max_uses"),
  usedCount: int("used_count").default(0).notNull(),
  expiresAt: timestamp("expires_at"),
  createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
  revokedAt: timestamp("revoked_at"),
  revokedBy: varchar("revoked_by", { length: 128 }),
  note: varchar("note", { length: 255 }),
}, (table) => ({
  codeIdx: uniqueIndex("class_invitation_codes_code_idx").on(table.code),
  classIdx: index("class_invitation_codes_class_idx").on(table.classId),
  statusExpiresIdx: index("class_invitation_codes_status_expires_idx").on(table.status, table.expiresAt),
  classFk: foreignKey({
    columns: [table.classId],
    foreignColumns: [classes.id],
    name: "cic_c_fk",
  }).onDelete("cascade"),
}));

export const classEnrollments = mysqlTable("class_enrollments", {
  classId: varchar("class_id", { length: 128 }).notNull(),
  studentId: varchar("student_id", { length: 128 }).notNull(),
  status: classEnrollmentStatusEnum.default("active").notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  pk: primaryKey({ columns: [table.classId, table.studentId] }),
  classIdx: index("class_enrollments_class_idx").on(table.classId),
  studentIdx: index("class_enrollments_student_idx").on(table.studentId),
  classFk: foreignKey({
    columns: [table.classId],
    foreignColumns: [classes.id],
    name: "ce_c_fk",
  }).onDelete("cascade"),
  studentFk: foreignKey({
    columns: [table.studentId],
    foreignColumns: [users.id],
    name: "ce_s_fk",
  }).onDelete("cascade"),
}));

export const classSchedule = mysqlTable("class_schedule", {
  id: id("id").primaryKey(),
  classId: varchar("class_id", { length: 128 }).notNull(),
  weekday: int("weekday").notNull(),
  startTime: varchar("start_time", { length: 5 }).notNull(),
  endTime: varchar("end_time", { length: 5 }).notNull(),
  course: varchar("course", { length: 255 }).notNull(),
  location: varchar("location", { length: 100 }),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  classIdx: index("class_schedule_class_idx").on(table.classId),
  classDayIdx: index("class_schedule_class_day_idx").on(table.classId, table.weekday),
  classFk: foreignKey({
    columns: [table.classId],
    foreignColumns: [classes.id],
    name: "cs_c_fk",
  }).onDelete("cascade"),
}));

// --- 7. Exam Proctoring Config (考试监考配置) ---

export const examModeEnum = mysqlEnum("exam_mode", ["homework", "timed", "proctored"]);
export const proctoringEventTypeEnum = mysqlEnum("event_type", [
  "tab_switch", "window_blur", "copy_attempt", "paste_attempt",
  "right_click", "devtools_open", "fullscreen_exit", "idle_timeout"
]);

export const exams = mysqlTable("exams", {
  id: id("id").primaryKey(),
  title: varchar("title", { length: 255 }).notNull(),
  description: text("description"),
  
  /**
   * Stores the hierarchical structure of the exam.
   * Expected JSON Schema (3-level: section > group > question):
   * type ExamStructure = Array<
   *   | { type: 'section', title: string, level?: number, children: ExamStructure }
   *   | { type: 'group', title: string, instruction?: string, children: ExamStructure }
   *   | { type: 'question', questionId: string, score: number }
   * >
   *
   * Note: This is for UI presentation/ordering.
   * Real relational integrity is maintained in 'exam_questions' table.
   */
  structure: json("structure"), 
  
  creatorId: varchar("creator_id", { length: 128 }).notNull().references(() => users.id),
  
  // Link to Subjects and Grades
  subjectId: varchar("subject_id", { length: 128 }).references(() => subjects.id),
  gradeId: varchar("grade_id", { length: 128 }).references(() => grades.id),

  startTime: timestamp("start_time"),
  endTime: timestamp("end_time"),

  // P2: Online exam mode + proctoring settings
  examMode: examModeEnum.default("homework"),
  durationMinutes: int("duration_minutes"),
  shuffleQuestions: boolean("shuffle_questions").default(false),
  allowLateStart: boolean("allow_late_start").default(false),
  lateStartGraceMinutes: int("late_start_grace_minutes").default(0),
  antiCheatEnabled: boolean("anti_cheat_enabled").default(false),

  // Status: draft, published, ongoing, finished
  status: varchar("status", { length: 50 }).default("draft"),
  
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  subjectIdx: index("exams_subject_idx").on(table.subjectId),
  gradeIdx: index("exams_grade_idx").on(table.gradeId),
}));

// Linking Exams to Questions (Many-to-Many often, or One-to-Many if specific to exam)
// Usually questions are reused, so Many-to-Many
export const examQuestions = mysqlTable("exam_questions", {
  examId: varchar("exam_id", { length: 128 }).notNull().references(() => exams.id, { onDelete: "cascade" }),
  questionId: varchar("question_id", { length: 128 }).notNull().references(() => questions.id, { onDelete: "cascade" }),
  score: int("score").default(0),
  order: int("order").default(0),
}, (table) => ({
  pk: primaryKey({ columns: [table.examId, table.questionId] }),
}));

export const examSubmissions = mysqlTable("exam_submissions", {
  id: id("id").primaryKey(),
  examId: varchar("exam_id", { length: 128 }).notNull().references(() => exams.id, { onDelete: "cascade" }),
  studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  
  score: int("score"), // Total score
  status: varchar("status", { length: 50 }).default("started"), // started, submitted, graded
  
  submittedAt: timestamp("submitted_at"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  examStudentIdx: index("exam_student_idx").on(table.examId, table.studentId),
}));

export const submissionAnswers = mysqlTable("submission_answers", {
  id: id("id").primaryKey(),
  submissionId: varchar("submission_id", { length: 128 }).notNull().references(() => examSubmissions.id, { onDelete: "cascade" }),
  questionId: varchar("question_id", { length: 128 }).notNull().references(() => questions.id),
  
  answerContent: json("answer_content"), // Student's answer
  score: int("score"), // Score for this specific question
  feedback: text("feedback"), // Teacher's feedback
  
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  submissionIdx: index("submission_idx").on(table.submissionId),
}));

export const homeworkAssignments = mysqlTable("homework_assignments", {
  id: id("id").primaryKey(),
  sourceExamId: varchar("source_exam_id", { length: 128 }),
  title: varchar("title", { length: 255 }).notNull(),
  description: text("description"),
  structure: json("structure"),
  status: varchar("status", { length: 50 }).default("draft"),
  creatorId: varchar("creator_id", { length: 128 }).notNull(),
  availableAt: timestamp("available_at"),
  dueAt: timestamp("due_at"),
  allowLate: boolean("allow_late").default(false).notNull(),
  lateDueAt: timestamp("late_due_at"),
  maxAttempts: int("max_attempts").default(1).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  creatorIdx: index("hw_assignment_creator_idx").on(table.creatorId),
  sourceExamIdx: index("hw_assignment_source_exam_idx").on(table.sourceExamId),
  statusIdx: index("hw_assignment_status_idx").on(table.status),
  sourceExamFk: foreignKey({
    columns: [table.sourceExamId],
    foreignColumns: [exams.id],
    name: "hw_asg_exam_fk",
  }).onDelete("cascade"),
  creatorFk: foreignKey({
    columns: [table.creatorId],
    foreignColumns: [users.id],
    name: "hw_asg_creator_fk",
  }).onDelete("cascade"),
}));

export const homeworkAssignmentQuestions = mysqlTable("homework_assignment_questions", {
  assignmentId: varchar("assignment_id", { length: 128 }).notNull(),
  questionId: varchar("question_id", { length: 128 }).notNull(),
  score: int("score").default(0),
  order: int("order").default(0),
}, (table) => ({
  pk: primaryKey({ columns: [table.assignmentId, table.questionId] }),
  assignmentIdx: index("hw_assignment_questions_assignment_idx").on(table.assignmentId),
  assignmentFk: foreignKey({
    columns: [table.assignmentId],
    foreignColumns: [homeworkAssignments.id],
    name: "hw_aq_a_fk",
  }).onDelete("cascade"),
  questionFk: foreignKey({
    columns: [table.questionId],
    foreignColumns: [questions.id],
    name: "hw_aq_q_fk",
  }).onDelete("cascade"),
}));

export const homeworkAssignmentTargets = mysqlTable("homework_assignment_targets", {
  assignmentId: varchar("assignment_id", { length: 128 }).notNull(),
  studentId: varchar("student_id", { length: 128 }).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  pk: primaryKey({ columns: [table.assignmentId, table.studentId] }),
  assignmentIdx: index("hw_assignment_targets_assignment_idx").on(table.assignmentId),
  studentIdx: index("hw_assignment_targets_student_idx").on(table.studentId),
  assignmentFk: foreignKey({
    columns: [table.assignmentId],
    foreignColumns: [homeworkAssignments.id],
    name: "hw_at_a_fk",
  }).onDelete("cascade"),
  studentFk: foreignKey({
    columns: [table.studentId],
    foreignColumns: [users.id],
    name: "hw_at_s_fk",
  }).onDelete("cascade"),
}));

export const homeworkSubmissions = mysqlTable("homework_submissions", {
  id: id("id").primaryKey(),
  assignmentId: varchar("assignment_id", { length: 128 }).notNull(),
  studentId: varchar("student_id", { length: 128 }).notNull(),
  attemptNo: int("attempt_no").default(1).notNull(),
  score: int("score"),
  status: varchar("status", { length: 50 }).default("started"),
  startedAt: timestamp("started_at").defaultNow().notNull(),
  submittedAt: timestamp("submitted_at"),
  isLate: boolean("is_late").default(false).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  assignmentStudentIdx: index("hw_assignment_student_idx").on(table.assignmentId, table.studentId),
  assignmentFk: foreignKey({
    columns: [table.assignmentId],
    foreignColumns: [homeworkAssignments.id],
    name: "hw_sub_a_fk",
  }).onDelete("cascade"),
  studentFk: foreignKey({
    columns: [table.studentId],
    foreignColumns: [users.id],
    name: "hw_sub_student_fk",
  }).onDelete("cascade"),
}));

export const homeworkAnswers = mysqlTable("homework_answers", {
  id: id("id").primaryKey(),
  submissionId: varchar("submission_id", { length: 128 }).notNull(),
  questionId: varchar("question_id", { length: 128 }).notNull(),
  answerContent: json("answer_content"),
  score: int("score"),
  feedback: text("feedback"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  submissionIdx: index("hw_answer_submission_idx").on(table.submissionId),
  submissionQuestionIdx: index("hw_answer_submission_question_idx").on(table.submissionId, table.questionId),
  submissionFk: foreignKey({
    columns: [table.submissionId],
    foreignColumns: [homeworkSubmissions.id],
    name: "hw_ans_sub_fk",
  }).onDelete("cascade"),
  questionFk: foreignKey({
    columns: [table.questionId],
    foreignColumns: [questions.id],
    name: "hw_ans_q_fk",
  }),
}));

export const aiProviderVisibilityEnum = mysqlEnum("visibility", ["public", "private"]);

export const aiProviders = mysqlTable("ai_providers", {
  id: id("id").primaryKey(),
  provider: mysqlEnum("provider", ["zhipu", "openai", "gemini", "custom", "ollama"]).notNull(),
  baseUrl: varchar("base_url", { length: 512 }),
  model: varchar("model", { length: 128 }).notNull(),
  apiKeyEncrypted: text("api_key_encrypted").notNull(),
  apiKeyLast4: varchar("api_key_last4", { length: 4 }),
  isDefault: boolean("is_default").default(false).notNull(),
  // V3: 可见性 — public 由管理员发布，全员可用；private 仅创建者可见
  visibility: aiProviderVisibilityEnum.default("private").notNull(),
  createdBy: varchar("created_by", { length: 128 }),
  updatedBy: varchar("updated_by", { length: 128 }),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  providerIdx: index("ai_provider_idx").on(table.provider),
  defaultIdx: index("ai_provider_default_idx").on(table.isDefault),
  visibilityIdx: index("ai_provider_visibility_idx").on(table.visibility),
  createdByIdx: index("ai_provider_created_by_idx").on(table.createdBy),
}));

// --- 8. Announcements ---

export const announcementTypeEnum = mysqlEnum("type", ["school", "grade", "class"]);
export const announcementStatusEnum = mysqlEnum("status", ["draft", "published", "archived"]);

export const announcements = mysqlTable("announcements", {
  id: id("id").primaryKey(),
  title: varchar("title", { length: 255 }).notNull(),
  content: text("content").notNull(),
  type: announcementTypeEnum.default("school").notNull(),
  status: announcementStatusEnum.default("draft").notNull(),
  targetGradeId: varchar("target_grade_id", { length: 128 }),
  targetClassId: varchar("target_class_id", { length: 128 }),
  authorId: varchar("author_id", { length: 128 }).notNull().references(() => users.id),
  publishedAt: datetime("published_at", { mode: "date" }),
  // V2-P2-13d: 公告置顶（置顶公告在列表中优先显示）
  isPinned: boolean("is_pinned").default(false).notNull(),
  createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
  updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  authorIdx: index("announcements_author_idx").on(table.authorId),
  statusIdx: index("announcements_status_idx").on(table.status),
  typeIdx: index("announcements_type_idx").on(table.type),
  targetGradeIdx: index("announcements_target_grade_idx").on(table.targetGradeId),
  targetClassIdx: index("announcements_target_class_idx").on(table.targetClassId),
  // V2-P2-13d: 置顶索引
  statusPinnedIdx: index("announcements_status_pinned_idx").on(table.status, table.isPinned),
}));

// --- 8b. Announcement Reads (公告已读回执) ---

export const announcementReads = mysqlTable("announcement_reads", {
  id: id("id").primaryKey(),
  announcementId: varchar("announcement_id", { length: 128 }).notNull().references(() => announcements.id, { onDelete: "cascade" }),
  userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  readAt: timestamp("read_at", { mode: "date" }).defaultNow().notNull(),
}, (table) => ({
  announcementIdx: index("announcement_reads_announcement_idx").on(table.announcementId),
  userIdx: index("announcement_reads_user_idx").on(table.userId),
  // 唯一约束：一个用户对一条公告只能有一条已读记录
  uniqueAnnouncementUser: uniqueIndex("announcement_reads_unique_idx").on(table.announcementId, table.userId),
}));

// --- 9. Audit & Login Logs ---

export const auditLogStatusEnum = mysqlEnum("status", ["success", "failure"]);

export const auditLogs = mysqlTable("audit_logs", {
  id: id("id").primaryKey(),
  userId: varchar("user_id", { length: 128 }).notNull(),
  userName: varchar("user_name", { length: 255 }).notNull(),
  action: varchar("action", { length: 255 }).notNull(),
  module: varchar("module", { length: 128 }).notNull(),
  targetId: varchar("target_id", { length: 128 }),
  targetType: varchar("target_type", { length: 128 }),
  detail: text("detail"),
  ipAddress: varchar("ip_address", { length: 45 }),
  userAgent: varchar("user_agent", { length: 512 }),
  status: auditLogStatusEnum.default("success").notNull(),
  createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
}, (table) => ({
  userIdIdx: index("audit_logs_user_id_idx").on(table.userId),
  moduleIdx: index("audit_logs_module_idx").on(table.module),
  actionIdx: index("audit_logs_action_idx").on(table.action),
  statusIdx: index("audit_logs_status_idx").on(table.status),
  createdAtIdx: index("audit_logs_created_at_idx").on(table.createdAt),
}));

export const loginLogActionEnum = mysqlEnum("action", ["signin", "signout", "signup"]);
export const loginLogStatusEnum = mysqlEnum("status", ["success", "failure"]);

export const loginLogs = mysqlTable("login_logs", {
  id: id("id").primaryKey(),
  userId: varchar("user_id", { length: 128 }),
  userEmail: varchar("user_email", { length: 255 }).notNull(),
  action: loginLogActionEnum.notNull(),
  status: loginLogStatusEnum.default("success").notNull(),
  ipAddress: varchar("ip_address", { length: 45 }),
  userAgent: varchar("user_agent", { length: 512 }),
  errorMessage: text("error_message"),
  createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
}, (table) => ({
  userIdIdx: index("login_logs_user_id_idx").on(table.userId),
  userEmailIdx: index("login_logs_user_email_idx").on(table.userEmail),
  actionIdx: index("login_logs_action_idx").on(table.action),
  statusIdx: index("login_logs_status_idx").on(table.status),
  createdAtIdx: index("login_logs_created_at_idx").on(table.createdAt),
}));

// --- 9b. Invitation Codes (注册邀请码，audit-P2-3 新增) ---

/**
 * 注册邀请码表（audit-P2-3）
 *
 * 管理员批量生成，用于：
 * - 预分配注册角色（student/teacher/parent/admin）
 * - 可选绑定邮箱（限定指定用户使用）
 * - 可选绑定班级（注册后自动加入班级）
 * - 可选过期时间
 */
export const invitationCodes = mysqlTable("invitation_codes", {
  id: id("id").primaryKey(),
  /** 邀请码明文（8-12 位大写字母+数字组合，unique） */
  code: varchar("code", { length: 64 }).notNull().unique(),
  /** 可选：限定邮箱使用（未设置则任意邮箱可用） */
  email: varchar("email", { length: 255 }),
  /** 注册后分配的角色（student/teacher/parent/admin） */
  role: varchar("role", { length: 50 }).notNull(),
  /** 可选：注册后自动加入的班级 ID */
  classId: varchar("class_id", { length: 128 }),
  /** 可选：管理员备注 */
  notes: varchar("notes", { length: 500 }),
  createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
  /** 创建者（管理员 userId） */
  createdBy: varchar("created_by", { length: 128 }),
  /** 可选：过期时间（未设置则永久有效） */
  expiresAt: timestamp("expires_at", { mode: "date" }),
  /** 已使用者 userId（未使用为 null） */
  usedBy: varchar("used_by", { length: 128 }),
  /** 使用时间（未使用为 null） */
  usedAt: timestamp("used_at", { mode: "date" }),
}, (table) => ({
  codeIdx: index("invitation_codes_code_idx").on(table.code),
  emailIdx: index("invitation_codes_email_idx").on(table.email),
  usedByIdx: index("invitation_codes_used_by_idx").on(table.usedBy),
}));

// --- 10. Data Change Logs (数据变更日志) ---

export const dataChangeLogActionEnum = mysqlEnum("action", ["create", "update", "delete"]);

export const dataChangeLogs = mysqlTable("data_change_logs", {
  id: varchar("id", { length: 128 }).primaryKey(),
  tableName: varchar("table_name", { length: 128 }).notNull(),
  recordId: varchar("record_id", { length: 128 }).notNull(),
  action: dataChangeLogActionEnum.notNull(),
  oldValue: text("old_value"),
  newValue: text("new_value"),
  changedBy: varchar("changed_by", { length: 128 }).notNull(),
  changedByName: varchar("changed_by_name", { length: 255 }).notNull(),
  ipAddress: varchar("ip_address", { length: 45 }),
  createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
}, (table) => ({
  tableNameIdx: index("data_change_logs_table_name_idx").on(table.tableName),
  recordIdIdx: index("data_change_logs_record_id_idx").on(table.recordId),
  actionIdx: index("data_change_logs_action_idx").on(table.action),
  changedByIdx: index("data_change_logs_changed_by_idx").on(table.changedBy),
  createdAtIdx: index("data_change_logs_created_at_idx").on(table.createdAt),
}));

// Re-export old courses table if needed or deprecate it.
// Assuming we are replacing the old simple schema with this robust one.
// But if there were existing tables, we might keep them or comment them out.
// For this task, I will overwrite completely as this is a "System Architect" redesign.

// --- 11. Grade Records (成绩录入) ---

export const gradeRecordTypeEnum = mysqlEnum("type", ["exam", "quiz", "homework", "other"]);
export const gradeRecordSemesterEnum = mysqlEnum("semester", ["1", "2"]);

export const gradeRecords = mysqlTable("grade_records", {
  id: id("id").primaryKey(),
  studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }),
  subjectId: varchar("subject_id", { length: 128 }).notNull().references(() => subjects.id, { onDelete: "cascade" }),
  examId: varchar("exam_id", { length: 128 }),
  academicYearId: varchar("academic_year_id", { length: 128 }),
  title: varchar("title", { length: 255 }).notNull(),
  score: decimal("score", { precision: 6, scale: 2 }).notNull(),
  fullScore: decimal("full_score", { precision: 6, scale: 2 }).default("100").notNull(),
  type: gradeRecordTypeEnum.default("exam").notNull(),
  semester: gradeRecordSemesterEnum.default("1").notNull(),
  recordedBy: varchar("recorded_by", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  remark: text("remark"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  studentIdx: index("grade_records_student_idx").on(table.studentId),
  classIdx: index("grade_records_class_idx").on(table.classId),
  subjectIdx: index("grade_records_subject_idx").on(table.subjectId),
  examIdx: index("grade_records_exam_idx").on(table.examId),
  classSubjectIdx: index("grade_records_class_subject_idx").on(table.classId, table.subjectId),
  recordedByIdx: index("grade_records_recorded_by_idx").on(table.recordedBy),
  classFk: foreignKey({
    columns: [table.classId],
    foreignColumns: [classes.id],
    name: "gr_c_fk",
  }).onDelete("cascade"),
  studentFk: foreignKey({
    columns: [table.studentId],
    foreignColumns: [users.id],
    name: "gr_s_fk",
  }).onDelete("cascade"),
  subjectFk: foreignKey({
    columns: [table.subjectId],
    foreignColumns: [subjects.id],
    name: "gr_sub_fk",
  }).onDelete("cascade"),
  recordedByFk: foreignKey({
    columns: [table.recordedBy],
    foreignColumns: [users.id],
    name: "gr_rb_fk",
  }).onDelete("cascade"),
}));

// --- 11.1 Grade Record Answers (成绩每题得分) ---

export const gradeRecordAnswers = mysqlTable("grade_record_answers", {
  id: id("id").primaryKey(),
  gradeRecordId: varchar("grade_record_id", { length: 128 }).notNull().references(() => gradeRecords.id, { onDelete: "cascade" }),
  questionId: varchar("question_id", { length: 128 }).notNull().references(() => questions.id, { onDelete: "cascade" }),
  score: decimal("score", { precision: 6, scale: 2 }).notNull(),
  fullScore: decimal("full_score", { precision: 6, scale: 2 }).notNull(),
  feedback: text("feedback"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  gradeRecordIdx: index("grade_record_answers_record_idx").on(table.gradeRecordId),
  questionIdx: index("grade_record_answers_question_idx").on(table.questionId),
  gradeRecordFk: foreignKey({
    columns: [table.gradeRecordId],
    foreignColumns: [gradeRecords.id],
    name: "gra_gr_fk",
  }).onDelete("cascade"),
  questionFk: foreignKey({
    columns: [table.questionId],
    foreignColumns: [questions.id],
    name: "gra_q_fk",
  }).onDelete("cascade"),
}));

// --- 12. File Attachments (文件附件) ---

export const fileAttachments = mysqlTable("file_attachments", {
  id: varchar("id", { length: 128 }).primaryKey(),
  filename: varchar("filename", { length: 255 }).notNull(),
  originalName: varchar("original_name", { length: 255 }).notNull(),
  mimeType: varchar("mime_type", { length: 128 }).notNull(),
  size: bigint("size", { mode: "number" }).notNull(),
  storagePath: varchar("storage_path", { length: 512 }).notNull(),
  url: varchar("url", { length: 512 }),
  uploaderId: varchar("uploader_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  targetType: varchar("target_type", { length: 128 }),
  targetId: varchar("target_id", { length: 128 }),
  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  uploaderIdx: index("file_attachments_uploader_idx").on(table.uploaderId),
  targetIdx: index("file_attachments_target_idx").on(table.targetType, table.targetId),
  createdAtIdx: index("file_attachments_created_at_idx").on(table.createdAt),
}));

// --- 13. Course Plans (课程计划) ---

export const coursePlanStatusEnum = mysqlEnum("status", ["planning", "active", "completed", "paused"]);
export const coursePlanSemesterEnum = mysqlEnum("semester", ["1", "2"]);

export const coursePlans = mysqlTable("course_plans", {
  id: id("id").primaryKey(),
  classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }),
  subjectId: varchar("subject_id", { length: 128 }).notNull().references(() => subjects.id, { onDelete: "cascade" }),
  teacherId: varchar("teacher_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  academicYearId: varchar("academic_year_id", { length: 128 }),
  semester: coursePlanSemesterEnum.default("1").notNull(),
  totalHours: int("total_hours").default(0).notNull(),
  completedHours: int("completed_hours").default(0).notNull(),
  weeklyHours: int("weekly_hours").default(0).notNull(),
  startDate: date("start_date"),
  endDate: date("end_date"),
  syllabus: text("syllabus"),
  objectives: text("objectives"),
  status: coursePlanStatusEnum.default("planning").notNull(),
  createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  classIdx: index("course_plans_class_idx").on(table.classId),
  teacherIdx: index("course_plans_teacher_idx").on(table.teacherId),
  subjectIdx: index("course_plans_subject_idx").on(table.subjectId),
  statusIdx: index("course_plans_status_idx").on(table.status),
  classSubjectIdx: index("course_plans_class_subject_idx").on(table.classId, table.subjectId),
  classFk: foreignKey({
    columns: [table.classId],
    foreignColumns: [classes.id],
    name: "cp_c_fk",
  }).onDelete("cascade"),
  subjectFk: foreignKey({
    columns: [table.subjectId],
    foreignColumns: [subjects.id],
    name: "cp_s_fk",
  }).onDelete("cascade"),
  teacherFk: foreignKey({
    columns: [table.teacherId],
    foreignColumns: [users.id],
    name: "cp_t_fk",
  }).onDelete("cascade"),
  createdByFk: foreignKey({
    columns: [table.createdBy],
    foreignColumns: [users.id],
    name: "cp_cb_fk",
  }).onDelete("cascade"),
}));

export const coursePlanItems = mysqlTable("course_plan_items", {
  id: id("id").primaryKey(),
  planId: varchar("plan_id", { length: 128 }).notNull().references(() => coursePlans.id, { onDelete: "cascade" }),
  week: int("week").notNull(),
  topic: varchar("topic", { length: 255 }).notNull(),
  content: text("content"),
  hours: int("hours").default(2).notNull(),
  textbookChapter: varchar("textbook_chapter", { length: 255 }),
  notes: text("notes"),
  isCompleted: boolean("is_completed").default(false).notNull(),
  completedAt: date("completed_at"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  planIdx: index("course_plan_items_plan_idx").on(table.planId),
  planWeekIdx: index("course_plan_items_plan_week_idx").on(table.planId, table.week),
  planFk: foreignKey({
    columns: [table.planId],
    foreignColumns: [coursePlans.id],
    name: "cpi_p_fk",
  }).onDelete("cascade"),
}));

// --- 14. Messages (站内消息) ---

export const messages = mysqlTable("messages", {
  id: id("id").primaryKey(),
  senderId: varchar("sender_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  receiverId: varchar("receiver_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  subject: varchar("subject", { length: 255 }),
  content: text("content").notNull(),
  isRead: boolean("is_read").default(false).notNull(),
  readAt: timestamp("read_at", { mode: "date" }),
  parentMessageId: varchar("parent_message_id", { length: 128 }), // 回复链
  // 软删除：发送方/接收方各自独立删除，互不影响
  senderDeletedAt: timestamp("sender_deleted_at", { mode: "date" }),
  receiverDeletedAt: timestamp("receiver_deleted_at", { mode: "date" }),
  // V2-P2-13c: 消息星标（接收方可标记重要消息）
  isStarred: boolean("is_starred").default(false).notNull(),
  // P2-4: 消息撤回（发送方在 2 分钟窗口内可撤回；非空即表示已撤回）
  recalledAt: timestamp("recalled_at", { mode: "date" }),
  // P2-2: 群组消息 ID（fan-out on write：教师群发时每条消息共享同一 groupMessageId）
  groupMessageId: varchar("group_message_id", { length: 128 }),
  createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
}, (table) => ({
  senderIdx: index("messages_sender_idx").on(table.senderId),
  receiverIdx: index("messages_receiver_idx").on(table.receiverId),
  isReadIdx: index("messages_is_read_idx").on(table.isRead),
  parentIdx: index("messages_parent_idx").on(table.parentMessageId),
  receiverReadIdx: index("messages_receiver_read_idx").on(table.receiverId, table.isRead),
  // V2-P2-13c: 星标索引
  receiverStarredIdx: index("messages_receiver_starred_idx").on(table.receiverId, table.isStarred),
  // P2-2: 群组消息索引（按 groupMessageId 聚合查询）
  groupMessageIdx: index("messages_group_message_idx").on(table.groupMessageId),
}));

// --- 14b. Message Drafts (消息草稿) ---

export const messageDrafts = mysqlTable("message_drafts", {
  id: id("id").primaryKey(),
  userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  receiverId: varchar("receiver_id", { length: 128 }).references(() => users.id, { onDelete: "cascade" }),
  subject: varchar("subject", { length: 255 }),
  content: text("content"),
  parentMessageId: varchar("parent_message_id", { length: 128 }),
  // P2-10: 跨设备草稿同步 - 设备 ID（标识来源设备）
  deviceId: varchar("device_id", { length: 128 }),
  // P2-10: 跨设备草稿同步 - 版本号（乐观锁，冲突检测）
  version: int("version").default(1).notNull(),
  updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().onUpdateNow().notNull(),
  createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
}, (table) => ({
  userIdx: index("message_drafts_user_idx").on(table.userId),
  userUpdatedIdx: index("message_drafts_user_updated_idx").on(table.userId, table.updatedAt),
  // P2-10: 支持按 userId + parentMessageId 查询最新版本草稿
  userParentIdx: index("message_drafts_user_parent_idx").on(table.userId, table.parentMessageId),
}));

// --- 14c. Message Templates (快捷回复模板, P2-6) ---

export const messageTemplates = mysqlTable("message_templates", {
  id: id("id").primaryKey(),
  userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  title: varchar("title", { length: 100 }).notNull(),
  content: text("content").notNull(),
  // 排序权重（越小越靠前），默认 0
  sortOrder: int("sort_order").default(0).notNull(),
  updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().onUpdateNow().notNull(),
  createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
}, (table) => ({
  userIdx: index("message_templates_user_idx").on(table.userId),
  userSortIdx: index("message_templates_user_sort_idx").on(table.userId, table.sortOrder),
}));

// --- 14d. Message Reports (消息举报, P2-5) ---

export const messageReports = mysqlTable("message_reports", {
  id: id("id").primaryKey(),
  // 举报人
  reporterId: varchar("reporter_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  // 被举报消息 ID
  messageId: varchar("message_id", { length: 128 }).notNull().references(() => messages.id, { onDelete: "cascade" }),
  // 被举报人（冗余，便于按被举报人查询）
  reportedUserId: varchar("reported_user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  // 举报理由分类：spam / harassment / inappropriate / other
  reason: varchar("reason", { length: 50 }).notNull(),
  // 详细描述（可选）
  description: text("description"),
  // 举报状态：pending / reviewed / dismissed / actioned
  status: varchar("status", { length: 20 }).notNull().default("pending"),
  createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
  updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  reporterIdx: index("message_reports_reporter_idx").on(table.reporterId),
  messageIdx: index("message_reports_message_idx").on(table.messageId),
  reportedUserIdx: index("message_reports_reported_user_idx").on(table.reportedUserId),
  statusIdx: index("message_reports_status_idx").on(table.status),
}));

// --- 14e. User Blocks (用户屏蔽, P2-5) ---

export const userBlocks = mysqlTable("user_blocks", {
  id: id("id").primaryKey(),
  // 屏蔽发起人
  blockerId: varchar("blocker_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  // 被屏蔽人
  blockedId: varchar("blocked_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
}, (table) => ({
  // 同一对 (blocker, blocked) 只能存在一条记录
  blockerBlockedUnique: uniqueIndex("user_blocks_blocker_blocked_uniq").on(table.blockerId, table.blockedId),
  blockerIdx: index("user_blocks_blocker_idx").on(table.blockerId),
}));

// --- 15. Message Notifications (消息通知) ---

export const messageNotifications = mysqlTable("message_notifications", {
  id: id("id").primaryKey(),
  userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  type: varchar("type", { length: 128 }).notNull(), // "message", "announcement", "homework", "grade", "diagnostic"
  title: varchar("title", { length: 255 }).notNull(),
  content: text("content"),
  link: varchar("link", { length: 512 }),
  isRead: boolean("is_read").default(false).notNull(),
  // V2-P2-13b: 通知优先级（low/normal/high/urgent），默认 normal
  priority: varchar("priority", { length: 16 }).default("normal").notNull(),
  // V2-P2-13b: 通知归档标记，归档后不在默认列表显示
  isArchived: boolean("is_archived").default(false).notNull(),
  createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
}, (table) => ({
  userIdx: index("message_notifications_user_idx").on(table.userId),
  isReadIdx: index("message_notifications_is_read_idx").on(table.isRead),
  userReadIdx: index("message_notifications_user_read_idx").on(table.userId, table.isRead),
  createdAtIdx: index("message_notifications_created_at_idx").on(table.createdAt),
  // V2-P2-13b: 新增优先级和归档索引
  priorityIdx: index("message_notifications_priority_idx").on(table.priority),
  userArchivedIdx: index("message_notifications_user_archived_idx").on(table.userId, table.isArchived),
}));

// --- 17. Notification Logs (通知发送日志) ---

export const notificationLogs = mysqlTable("notification_logs", {
  id: id("id").primaryKey(),
  // 关联用户（通知接收人）
  userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  // 通知标题（冗余存储，便于查询）
  title: varchar("title", { length: 255 }).notNull(),
  // 发送渠道: in_app | email | sms | wechat
  channel: varchar("channel", { length: 32 }).notNull(),
  // 发送状态: success | failure
  status: varchar("status", { length: 16 }).notNull(),
  // 渠道返回的消息 ID（用于追踪）
  messageId: varchar("message_id", { length: 255 }),
  // 失败时的错误信息
  error: text("error"),
  // 发送时间
  sentAt: timestamp("sent_at", { mode: "date" }).defaultNow().notNull(),
}, (table) => ({
  userIdx: index("notification_logs_user_idx").on(table.userId),
  channelIdx: index("notification_logs_channel_idx").on(table.channel),
  statusIdx: index("notification_logs_status_idx").on(table.status),
  sentAtIdx: index("notification_logs_sent_at_idx").on(table.sentAt),
}));

// --- 16. Notification Preferences (通知偏好) ---

export const notificationPreferences = mysqlTable("notification_preferences", {
  id: varchar("id", { length: 128 }).primaryKey(),
  userId: varchar("user_id", { length: 128 }).notNull().unique().references(() => users.id, { onDelete: "cascade" }),
  emailEnabled: boolean("email_enabled").default(false).notNull(),
  smsEnabled: boolean("sms_enabled").default(false).notNull(),
  pushEnabled: boolean("push_enabled").default(true).notNull(),
  homeworkNotifications: boolean("homework_notifications").default(true).notNull(),
  gradeNotifications: boolean("grade_notifications").default(true).notNull(),
  announcementNotifications: boolean("announcement_notifications").default(true).notNull(),
  messageNotifications: boolean("message_notifications").default(true).notNull(),
  attendanceNotifications: boolean("attendance_notifications").default(true).notNull(),
  // 免打扰时段（格式 "HH:mm"，如 "22:00"）
  quietHoursEnabled: boolean("quiet_hours_enabled").default(false).notNull(),
  quietHoursStart: varchar("quiet_hours_start", { length: 5 }),
  quietHoursEnd: varchar("quiet_hours_end", { length: 5 }),
  createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(),
  updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  userIdIdx: index("notification_preferences_user_idx").on(table.userId),
  userFk: foreignKey({
    columns: [table.userId],
    foreignColumns: [users.id],
    name: "np_u_fk",
  }).onDelete("cascade"),
}));

// --- 17. Parent-Student Relations (家长-子女关联) ---

export const parentStudentRelations = mysqlTable("parent_student_relations", {
  id: varchar("id", { length: 128 }).primaryKey().$defaultFn(() => createId()),
  parentId: varchar("parent_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  relation: varchar("relation", { length: 50 }), // 父亲/母亲/其他
  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  parentIdx: index("parent_student_relations_parent_idx").on(table.parentId),
  studentIdx: index("parent_student_relations_student_idx").on(table.studentId),
  parentFk: foreignKey({
    columns: [table.parentId],
    foreignColumns: [users.id],
    name: "psr_p_fk",
  }).onDelete("cascade"),
  studentFk: foreignKey({
    columns: [table.studentId],
    foreignColumns: [users.id],
    name: "psr_s_fk",
  }).onDelete("cascade"),
}));

// --- 18. Attendance (考勤管理) ---

export const attendanceStatusEnum = mysqlEnum("status", ["present", "absent", "late", "early_leave", "excused", "school_activity"]);

/**
 * L-6 节次考勤：节次枚举。
 * - morning_reading: 早读
 * - morning: 上午
 * - afternoon: 下午
 * - evening: 晚自习
 * - full_day: 全日（默认值，向后兼容：null 也表示全日）
 */
export const attendancePeriodEnum = mysqlEnum("period", ["morning_reading", "morning", "afternoon", "evening", "full_day"]);

export const attendanceRecords = mysqlTable("attendance_records", {
  id: id("id").primaryKey(),
  studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }),
  scheduleId: varchar("schedule_id", { length: 128 }),
  date: date("date").notNull(),
  status: attendanceStatusEnum.notNull(),
  // L-6：节次考勤字段。null 表示全日记录（向后兼容已有数据）。
  period: attendancePeriodEnum,
  remark: text("remark"),
  reason: varchar("reason", { length: 255 }),
  recordedBy: varchar("recorded_by", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  studentIdx: index("attendance_records_student_idx").on(table.studentId),
  classIdx: index("attendance_records_class_idx").on(table.classId),
  dateIdx: index("attendance_records_date_idx").on(table.date),
  classDateIdx: index("attendance_records_class_date_idx").on(table.classId, table.date),
  studentDateIdx: index("attendance_records_student_date_idx").on(table.studentId, table.date),
  scheduleIdx: index("attendance_records_schedule_idx").on(table.scheduleId),
  recordedByIdx: index("attendance_records_recorded_by_idx").on(table.recordedBy),
  // L-6：班级 + 日期 + 节次复合索引，支持按节次查询班级当日点名
  classDatePeriodIdx: index("attendance_records_class_date_period_idx").on(table.classId, table.date, table.period),
  classFk: foreignKey({
    columns: [table.classId],
    foreignColumns: [classes.id],
    name: "ar_c_fk",
  }).onDelete("cascade"),
  studentFk: foreignKey({
    columns: [table.studentId],
    foreignColumns: [users.id],
    name: "ar_s_fk",
  }).onDelete("cascade"),
  recordedByFk: foreignKey({
    columns: [table.recordedBy],
    foreignColumns: [users.id],
    name: "ar_rb_fk",
  }).onDelete("cascade"),
}));

export const attendanceRules = mysqlTable("attendance_rules", {
  id: id("id").primaryKey(),
  classId: varchar("class_id", { length: 128 }).references(() => classes.id, { onDelete: "cascade" }),
  lateThresholdMinutes: int("late_threshold_minutes").default(15),
  earlyLeaveThresholdMinutes: int("early_leave_threshold_minutes").default(15),
  enableAutoMark: boolean("enable_auto_mark").default(false),
  // L-3：出勤率阈值预警（教师端配置驱动）
  attendanceRateThreshold: int("attendance_rate_threshold").default(90),
  consecutiveAbsenceThreshold: int("consecutive_absence_threshold").default(3),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").onUpdateNow().notNull(),
}, (table) => ({
  classIdx: index("attendance_rules_class_idx").on(table.classId),
  classFk: foreignKey({
    columns: [table.classId],
    foreignColumns: [classes.id],
    name: "atr_c_fk",
  }).onDelete("cascade"),
}));

// --- 18b. Leave Requests (L-5 在线请假流程) ---

/**
 * 请假类型。
 * - sick: 病假
 * - personal: 事假
 * - family: 家庭事务
 * - other: 其他
 */
export const leaveTypeEnum = mysqlEnum("leave_type", [
  "sick",
  "personal",
  "family",
  "other",
]);

/**
 * 请假审批状态机：pending → approved/rejected；approved/pending 可被 requester 取消为 cancelled。
 * - pending: 待审批
 * - approved: 已批准（自动同步考勤为 excused）
 * - rejected: 已拒绝
 * - cancelled: 已撤销（申请人在审批前主动撤销）
 */
export const leaveStatusEnum = mysqlEnum("leave_status", [
  "pending",
  "approved",
  "rejected",
  "cancelled",
]);

export const leaveRequests = mysqlTable("leave_requests", {
  id: id("id").primaryKey(),
  /** 请假学生 ID */
  studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  /** 提交人 ID（家长代子女提交时为家长 userId；学生本人提交时等于 studentId） */
  requesterId: varchar("requester_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  /** 学生所属班级 ID（提交时快照，便于按班级筛选审批） */
  classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }),
  leaveType: leaveTypeEnum.notNull(),
  /** 起始日期（含） */
  startDate: date("start_date").notNull(),
  /** 结束日期（含） */
  endDate: date("end_date").notNull(),
  /** 请假事由 */
  reason: text("reason").notNull(),
  status: leaveStatusEnum.default("pending").notNull(),
  /** 审批人 ID（pending/cancelled 状态为 null） */
  reviewerId: varchar("reviewer_id", { length: 128 }).references(() => users.id, { onDelete: "set null" }),
  /** 审批意见（拒绝时必填，批准时可选） */
  reviewComment: text("review_comment"),
  reviewedAt: timestamp("reviewed_at"),
  /** 是否已同步考勤记录（approved 后异步写入 attendance_records，置为 true） */
  attendanceSynced: boolean("attendance_synced").default(false).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  studentIdx: index("leave_requests_student_idx").on(table.studentId),
  requesterIdx: index("leave_requests_requester_idx").on(table.requesterId),
  classIdx: index("leave_requests_class_idx").on(table.classId),
  statusIdx: index("leave_requests_status_idx").on(table.status),
  reviewerIdx: index("leave_requests_reviewer_idx").on(table.reviewerId),
  /** 班级+状态查询索引（教师审批列表常用） */
  classStatusIdx: index("leave_requests_class_status_idx").on(table.classId, table.status),
  /** 学生+状态查询索引（家长/学生列表常用） */
  studentStatusIdx: index("leave_requests_student_status_idx").on(table.studentId, table.status),
  classFk: foreignKey({
    columns: [table.classId],
    foreignColumns: [classes.id],
    name: "lr_c_fk",
  }).onDelete("cascade"),
  studentFk: foreignKey({
    columns: [table.studentId],
    foreignColumns: [users.id],
    name: "lr_s_fk",
  }).onDelete("cascade"),
  requesterFk: foreignKey({
    columns: [table.requesterId],
    foreignColumns: [users.id],
    name: "lr_rq_fk",
  }).onDelete("cascade"),
  reviewerFk: foreignKey({
    columns: [table.reviewerId],
    foreignColumns: [users.id],
    name: "lr_rv_fk",
  }).onDelete("set null"),
}));

// --- 19. Password Security (密码安全策略) ---

export const passwordSecurity = mysqlTable("password_security", {
  id: varchar("id", { length: 128 }).primaryKey().$defaultFn(() => createId()),
  userId: varchar("user_id", { length: 128 }).notNull().unique().references(() => users.id, { onDelete: "cascade" }),
  failedLoginAttempts: int("failed_login_attempts").default(0).notNull(),
  lockedUntil: timestamp("locked_until", { mode: "date" }),
  passwordChangedAt: timestamp("password_changed_at").defaultNow().notNull(),
  mustChangePassword: boolean("must_change_password").default(false).notNull(),
  lastPasswordChange: timestamp("last_password_change", { mode: "date" }),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  userIdIdx: index("password_security_user_idx").on(table.userId),
  userFk: foreignKey({
    columns: [table.userId],
    foreignColumns: [users.id],
    name: "ps_u_fk",
  }).onDelete("cascade"),
}));

// --- 20. Scheduling Rules & Schedule Changes (排课规则与调课) ---

export const schedulingRules = mysqlTable("scheduling_rules", {
  id: id("id").primaryKey(),
  classId: varchar("class_id", { length: 128 }), // null=全局规则
  maxDailyHours: int("max_daily_hours").default(8),
  maxContinuousHours: int("max_continuous_hours").default(2),
  lunchBreakStart: varchar("lunch_break_start", { length: 10 }).default("12:00"),
  lunchBreakEnd: varchar("lunch_break_end", { length: 10 }).default("13:00"),
  morningStart: varchar("morning_start", { length: 10 }).default("08:00"),
  afternoonEnd: varchar("afternoon_end", { length: 10 }).default("17:00"),
  avoidBackToBack: boolean("avoid_back_to_back").default(false),
  balancedSubjects: boolean("balanced_subjects").default(true),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  classIdx: index("scheduling_rules_class_idx").on(table.classId),
}));

export const scheduleChangeStatusEnum = mysqlEnum("status", ["pending", "approved", "rejected", "completed"]);

export const scheduleChanges = mysqlTable("schedule_changes", {
  id: id("id").primaryKey(),
  originalScheduleId: varchar("original_schedule_id", { length: 128 }),
  classId: varchar("class_id", { length: 128 }).notNull(),
  originalTeacherId: varchar("original_teacher_id", { length: 128 }),
  substituteTeacherId: varchar("substitute_teacher_id", { length: 128 }),
  originalDate: date("original_date"),
  newDate: date("new_date"),
  newStartTime: varchar("new_start_time", { length: 10 }),
  newEndTime: varchar("new_end_time", { length: 10 }),
  reason: text("reason"),
  status: scheduleChangeStatusEnum.default("pending").notNull(),
  requestedBy: varchar("requested_by", { length: 128 }).notNull(),
  approvedBy: varchar("approved_by", { length: 128 }),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  classIdx: index("schedule_changes_class_idx").on(table.classId),
  statusIdx: index("schedule_changes_status_idx").on(table.status),
  requestedByIdx: index("schedule_changes_requested_by_idx").on(table.requestedBy),
  originalScheduleIdx: index("schedule_changes_original_schedule_idx").on(table.originalScheduleId),
}));

// --- 21. Elective Course Management (选课管理) ---

export const electiveCourseStatusEnum = mysqlEnum("status", ["draft", "open", "closed", "cancelled"]);
export const electiveSelectionModeEnum = mysqlEnum("selection_mode", ["fcfs", "lottery"]);
export const courseSelectionStatusEnum = mysqlEnum("selection_status", ["selected", "enrolled", "waitlist", "dropped", "rejected"]);

export const electiveCourses = mysqlTable("elective_courses", {
  id: id("id").primaryKey(),
  name: varchar("name", { length: 255 }).notNull(),
  subjectId: varchar("subject_id", { length: 128 }).references(() => subjects.id, { onDelete: "set null" }),
  teacherId: varchar("teacher_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  gradeId: varchar("grade_id", { length: 128 }).references(() => grades.id, { onDelete: "set null" }),
  description: text("description"),
  capacity: int("capacity").default(30).notNull(),
  enrolledCount: int("enrolled_count").default(0).notNull(),
  classroom: varchar("classroom", { length: 100 }),
  schedule: varchar("schedule", { length: 255 }),
  startDate: date("start_date"),
  endDate: date("end_date"),
  selectionStartAt: datetime("selection_start_at"),
  selectionEndAt: datetime("selection_end_at"),
  /** 退课截止时间（P2-4 新增）：超过此时间学生不可退课；为 null 表示不限制 */
  dropDeadline: datetime("drop_deadline"),
  status: electiveCourseStatusEnum.default("draft").notNull(),
  selectionMode: electiveSelectionModeEnum.default("fcfs").notNull(),
  credit: decimal("credit", { precision: 3, scale: 1 }).default("1.0"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  teacherIdx: index("elective_courses_teacher_idx").on(table.teacherId),
  subjectIdx: index("elective_courses_subject_idx").on(table.subjectId),
  gradeIdx: index("elective_courses_grade_idx").on(table.gradeId),
  statusIdx: index("elective_courses_status_idx").on(table.status),
}));

export const courseSelections = mysqlTable("course_selections", {
  id: id("id").primaryKey(),
  courseId: varchar("course_id", { length: 128 }).notNull().references(() => electiveCourses.id, { onDelete: "cascade" }),
  studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  status: courseSelectionStatusEnum.default("selected").notNull(),
  priority: int("priority").default(1),
  selectedAt: timestamp("selected_at").defaultNow().notNull(),
  enrolledAt: timestamp("enrolled_at"),
  droppedAt: timestamp("dropped_at"),
  /** 退课理由（P2-4 新增）：学生退课时可选填写的理由，便于学校运营分析 */
  dropReason: varchar("drop_reason", { length: 255 }),
  lotteryRank: int("lottery_rank"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  courseStudentPk: primaryKey({ columns: [table.courseId, table.studentId] }),
  courseIdx: index("course_selections_course_idx").on(table.courseId),
  studentIdx: index("course_selections_student_idx").on(table.studentId),
  statusIdx: index("course_selections_status_idx").on(table.status),
}));

// --- 22. Exam Proctoring Events (考试监考事件) ---

export const examProctoringEvents = mysqlTable("exam_proctoring_events", {
  id: id("id").primaryKey(),
  submissionId: varchar("submission_id", { length: 128 }).notNull().references(() => examSubmissions.id, { onDelete: "cascade" }),
  studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  examId: varchar("exam_id", { length: 128 }).notNull().references(() => exams.id, { onDelete: "cascade" }),
  eventType: proctoringEventTypeEnum.notNull(),
  eventDetail: text("event_detail"),
  occurredAt: timestamp("occurred_at").defaultNow().notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  submissionIdx: index("proctoring_submission_idx").on(table.submissionId),
  studentIdx: index("proctoring_student_idx").on(table.studentId),
  examIdx: index("proctoring_exam_idx").on(table.examId),
  eventTypeIdx: index("proctoring_event_type_idx").on(table.eventType),
}));

// --- 23. Learning Diagnostic (学情诊断报告) ---

export const knowledgePointMastery = mysqlTable("knowledge_point_mastery", {
  id: id("id").primaryKey(),
  // 显式 foreignKey 见表参数（避免自动生成 FK 名过长 > 64 字符）
  studentId: varchar("student_id", { length: 128 }).notNull(),
  knowledgePointId: varchar("knowledge_point_id", { length: 128 }).notNull(),
  masteryLevel: decimal("mastery_level", { precision: 5, scale: 2 }).default("0").notNull(),
  totalQuestions: int("total_questions").default(0).notNull(),
  correctQuestions: int("correct_questions").default(0).notNull(),
  lastAssessedAt: timestamp("last_assessed_at").defaultNow().notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  studentKpPk: primaryKey({ columns: [table.studentId, table.knowledgePointId] }),
  studentIdx: index("mastery_student_idx").on(table.studentId),
  kpIdx: index("mastery_kp_idx").on(table.knowledgePointId),
  studentFk: foreignKey({
    columns: [table.studentId],
    foreignColumns: [users.id],
    name: "kpm_student_fk",
  }).onDelete("cascade"),
  kpFk: foreignKey({
    columns: [table.knowledgePointId],
    foreignColumns: [knowledgePoints.id],
    name: "kpm_kp_fk",
  }).onDelete("cascade"),
}));

export const diagnosticReportStatusEnum = mysqlEnum("report_status", ["draft", "published", "archived"]);
export const diagnosticReportTypeEnum = mysqlEnum("report_type", ["individual", "class", "grade"]);

export const learningDiagnosticReports = mysqlTable("learning_diagnostic_reports", {
  id: id("id").primaryKey(),
  studentId: varchar("student_id", { length: 128 }).references(() => users.id, { onDelete: "cascade" }),
  generatedBy: varchar("generated_by", { length: 128 }).references(() => users.id, { onDelete: "set null" }),
  // v4-P1-4: 班级报告关联的 classId，用于发布时批量通知全班学生
  classId: varchar("class_id", { length: 128 }).references(() => classes.id, { onDelete: "set null" }),
  // v4-P2-3: 年级报告关联的 gradeId，用于年级级别诊断
  gradeId: varchar("grade_id", { length: 128 }).references(() => grades.id, { onDelete: "set null" }),
  reportType: diagnosticReportTypeEnum.default("individual").notNull(),
  period: varchar("period", { length: 50 }),
  summary: text("summary"),
  strengths: json("strengths"),
  weaknesses: json("weaknesses"),
  recommendations: json("recommendations"),
  overallScore: decimal("overall_score", { precision: 5, scale: 2 }),
  status: diagnosticReportStatusEnum.default("draft").notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  studentIdx: index("diagnostic_student_idx").on(table.studentId),
  generatedByIdx: index("diagnostic_generated_by_idx").on(table.generatedBy),
  statusIdx: index("diagnostic_status_idx").on(table.status),
  reportTypeIdx: index("diagnostic_report_type_idx").on(table.reportType),
  classIdx: index("diagnostic_class_idx").on(table.classId),
  gradeIdx: index("diagnostic_grade_idx").on(table.gradeId),
}));

// --- 24. Lesson Preparation (备课) ---

export const lessonPlans = mysqlTable("lesson_plans", {
  id: id("id").primaryKey(),
  title: varchar("title", { length: 255 }).notNull(),
  textbookId: varchar("textbook_id", { length: 128 }).references(() => textbooks.id),
  chapterId: varchar("chapter_id", { length: 128 }).references(() => chapters.id),
  coursePlanItemId: varchar("course_plan_item_id", { length: 128 }),
  subjectId: varchar("subject_id", { length: 128 }).references(() => subjects.id),
  gradeId: varchar("grade_id", { length: 128 }).references(() => grades.id),
  templateId: varchar("template_id", { length: 128 }),
  templateName: varchar("template_name", { length: 100 }),
  content: json("content").notNull(),
  status: varchar("status", { length: 50 }).default("draft").notNull(),
  creatorId: varchar("creator_id", { length: 128 }).notNull().references(() => users.id),
  lastSavedAt: timestamp("last_saved_at"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  creatorIdx: index("lp_creator_idx").on(table.creatorId),
  statusIdx: index("lp_status_idx").on(table.status),
  textbookChapterIdx: index("lp_textbook_chapter_idx").on(table.textbookId, table.chapterId),
  subjectGradeIdx: index("lp_subject_grade_idx").on(table.subjectId, table.gradeId),
}));

export const lessonPlanVersions = mysqlTable("lesson_plan_versions", {
  id: id("id").primaryKey(),
  planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }),
  versionNo: int("version_no").notNull(),
  label: varchar("label", { length: 100 }),
  content: json("content").notNull(),
  isAuto: boolean("is_auto").default(false).notNull(),
  creatorId: varchar("creator_id", { length: 128 }).notNull().references(() => users.id),
  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  planVersionIdx: index("lpv_plan_version_idx").on(table.planId, table.versionNo),
  planCreatedIdx: index("lpv_plan_created_idx").on(table.planId, table.createdAt),
}));

export const lessonPlanTemplates = mysqlTable("lesson_plan_templates", {
  id: id("id").primaryKey(),
  name: varchar("name", { length: 100 }).notNull(),
  type: varchar("type", { length: 50 }).notNull(),
  scope: varchar("scope", { length: 50 }).notNull(),
  blocks: json("blocks").notNull(),
  creatorId: varchar("creator_id", { length: 128 }).references(() => users.id),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  typeCreatorIdx: index("lpt_type_creator_idx").on(table.type, table.creatorId),
}));

// M2 协同备课：Block 级评论表
export const lessonPlanComments = mysqlTable("lesson_plan_comments", {
  id: id("id").primaryKey(),
  planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }),
  blockId: varchar("block_id", { length: 128 }).notNull(),
  content: text("content").notNull(),
  authorId: varchar("author_id", { length: 128 }).notNull().references(() => users.id),
  resolved: boolean("resolved").default(false).notNull(),
  parentCommentId: varchar("parent_comment_id", { length: 128 }),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  planBlockIdx: index("lpc_plan_block_idx").on(table.planId, table.blockId),
  authorIdx: index("lpc_author_idx").on(table.authorId),
}));

// M3 审核工作流：审核记录表
export const lessonPlanReviewRecords = mysqlTable("lesson_plan_review_records", {
  id: id("id").primaryKey(),
  planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }),
  reviewerId: varchar("reviewer_id", { length: 128 }).notNull().references(() => users.id),
  decision: varchar("decision", { length: 20 }).notNull(), // approved | rejected
  comment: text("comment"),
  previousStatus: varchar("previous_status", { length: 50 }).notNull(),
  newStatus: varchar("new_status", { length: 50 }).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  planCreatedIdx: index("lprr_plan_created_idx").on(table.planId, table.createdAt),
  reviewerIdx: index("lprr_reviewer_idx").on(table.reviewerId),
}));

// M6 资源附件库：附件关联表
export const lessonPlanAttachments = mysqlTable("lesson_plan_attachments", {
  id: id("id").primaryKey(),
  planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }),
  blockId: varchar("block_id", { length: 128 }),
  fileId: varchar("file_id", { length: 128 }).notNull(),
  displayName: varchar("display_name", { length: 255 }).notNull(),
  attachmentType: varchar("attachment_type", { length: 50 }).default("reference").notNull(), // reference | material | supplementary
  uploadedBy: varchar("uploaded_by", { length: 128 }).notNull().references(() => users.id),
  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  planIdx: index("lpa_plan_idx").on(table.planId),
  blockIdx: index("lpa_block_idx").on(table.blockId),
  fileIdx: index("lpa_file_idx").on(table.fileId),
}));

// M12 代课教师机制：代课教师映射表
export const lessonPlanSubstitutes = mysqlTable("lesson_plan_substitutes", {
  id: id("id").primaryKey(),
  planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }),
  originalTeacherId: varchar("original_teacher_id", { length: 128 }).notNull().references(() => users.id),
  substituteTeacherId: varchar("substitute_teacher_id", { length: 128 }).notNull().references(() => users.id),
  startDate: date("start_date").notNull(),
  endDate: date("end_date"),
  reason: varchar("reason", { length: 255 }),
  status: varchar("status", { length: 20 }).default("active").notNull(), // active | expired | cancelled
  createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  planIdx: index("lps_plan_idx").on(table.planId),
  originalTeacherIdx: index("lps_original_teacher_idx").on(table.originalTeacherId),
  substituteTeacherIdx: index("lps_substitute_teacher_idx").on(table.substituteTeacherId),
  dateRangeIdx: index("lps_date_range_idx").on(table.startDate, table.endDate),
}));

// V5-7：课案-课时绑定表（将课案绑定到具体班级的某个日期/节次）
export const lessonPlanSchedules = mysqlTable("lesson_plan_schedules", {
  id: id("id").primaryKey(),
  planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }),
  classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }),
  /** 排课日期 YYYY-MM-DD */
  scheduledDate: date("scheduled_date").notNull(),
  /** 节次序号（1-12，对应学校节次安排） */
  period: int("period").notNull(),
  /** 关联 class_schedule.id（如使用课表）*/
  classScheduleId: varchar("class_schedule_id", { length: 128 }),
  /** 教学时长（分钟），默认 40 */
  durationMin: int("duration_min").default(40).notNull(),
  createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  planIdx: index("lpsc_plan_idx").on(table.planId),
  classDateIdx: index("lpsc_class_date_idx").on(table.classId, table.scheduledDate),
  teacherDateIdx: index("lpsc_plan_date_idx").on(table.planId, table.scheduledDate),
}));

// --- 25. System Settings (系统设置 - 键值对存储) ---

export const systemSettings = mysqlTable("system_settings", {
  id: id("id").primaryKey(),
  /** 设置分组：school_info / security_policy / file_upload / notification_config */
  category: varchar("category", { length: 50 }).notNull(),
  /** 设置键名，如 schoolName / passwordMinLength / maxFileSize */
  key: varchar("key", { length: 100 }).notNull(),
  /** 设置值（JSON 字符串，支持字符串/数字/布尔/对象） */
  value: text("value").notNull(),
  /** 值类型：string / number / boolean / json */
  valueType: varchar("value_type", { length: 20 }).default("string").notNull(),
  updatedBy: varchar("updated_by", { length: 128 }),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  categoryKeyIdx: uniqueIndex("ss_category_key_idx").on(table.category, table.key),
  categoryIdx: index("ss_category_idx").on(table.category),
}));

// --- 26. Error Book (错题本) ---

export const errorBookSourceTypeEnum = mysqlEnum("source_type", ["exam", "homework", "manual"]);
export const errorBookStatusEnum = mysqlEnum("error_status", ["new", "learning", "mastered", "archived"]);
export const errorBookReviewResultEnum = mysqlEnum("review_result", ["again", "hard", "good", "easy"]);

/**
 * 错题本条目 - 存储学生的错题记录。
 * 来源可以是考试提交、作业提交，或学生手动添加。
 * 采用简化版 SM-2 间隔重复算法调度复习。
 */
export const errorBookItems = mysqlTable("error_book_items", {
  id: id("id").primaryKey(),
  studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  questionId: varchar("question_id", { length: 128 }).notNull().references(() => questions.id),

  // 来源
  sourceType: errorBookSourceTypeEnum.default("manual").notNull(),
  /** exam_submission_id / homework_submission_id / null（手动添加） */
  sourceId: varchar("source_id", { length: 128 }),

  // 作答快照（冗余存储，避免源记录被删除后丢失上下文）
  studentAnswer: json("student_answer"),
  correctAnswer: json("correct_answer"),

  // 分类元数据（冗余，加速筛选）
  subjectId: varchar("subject_id", { length: 128 }),
  knowledgePointIds: json("knowledge_point_ids"),

  // 状态与掌握度
  status: errorBookStatusEnum.default("new").notNull(),
  /** 0-5 掌握程度，0=未学，5=已掌握 */
  masteryLevel: int("mastery_level").default(0).notNull(),

  // SM-2 间隔重复参数
  nextReviewAt: timestamp("next_review_at", { mode: "date" }),
  /** 复习间隔（天） */
  reviewInterval: int("review_interval").default(1).notNull(),
  reviewCount: int("review_count").default(0).notNull(),
  /** 连续答对次数（用于判断是否标记为已掌握） */
  correctStreak: int("correct_streak").default(0).notNull(),

  // 学生笔记与反思
  note: text("note"),
  /** 错误原因标签（JSON 数组，如 ["粗心", "概念不清", "计算错误"]） */
  errorTags: json("error_tags"),

  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  studentIdx: index("eb_item_student_idx").on(table.studentId),
  studentStatusIdx: index("eb_item_student_status_idx").on(table.studentId, table.status),
  studentReviewIdx: index("eb_item_student_review_idx").on(table.studentId, table.nextReviewAt),
  questionIdx: index("eb_item_question_idx").on(table.questionId),
  subjectIdx: index("eb_item_subject_idx").on(table.subjectId),
  sourceIdx: index("eb_item_source_idx").on(table.sourceType, table.sourceId),
}));

/**
 * 错题复习记录 - 每次复习的历史快照。
 * 用于追踪复习进度和算法调参。
 */
export const errorBookReviews = mysqlTable("error_book_reviews", {
  id: id("id").primaryKey(),
  itemId: varchar("item_id", { length: 128 }).notNull().references(() => errorBookItems.id, { onDelete: "cascade" }),
  studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),

  /** 复习自评结果：again(重来) / hard(困难) / good(良好) / easy(简单) */
  result: errorBookReviewResultEnum.notNull(),
  reviewedAt: timestamp("reviewed_at").defaultNow().notNull(),

  /** 本次复习后的新间隔（天） */
  newInterval: int("new_interval"),
  newMasteryLevel: int("new_mastery_level"),

  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  itemIdx: index("eb_review_item_idx").on(table.itemId),
  studentIdx: index("eb_review_student_idx").on(table.studentId),
  studentReviewedIdx: index("eb_review_student_reviewed_idx").on(table.studentId, table.reviewedAt),
}));

// --- 27. Grade Drafts (成绩录入草稿 - 服务端自动保存) ---

/**
 * 成绩录入草稿 - 用于跨设备恢复未保存的成绩。
 * v3-P2-10: 替代纯 localStorage 方案，支持换设备恢复。
 * 唯一键：userId + classId + subjectId + type，确保每位教师每组合只有一个草稿。
 */
export const gradeDrafts = mysqlTable("grade_drafts", {
  id: id("id").primaryKey(),
  userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }),
  subjectId: varchar("subject_id", { length: 128 }).notNull().references(() => subjects.id, { onDelete: "cascade" }),
  type: varchar("type", { length: 20 }).notNull(),
  /** 草稿内容：{ scores: Record<string, string>, timestamp: number } */
  content: json("content").notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
  /**
   * P3-6 协同录入锁机制：
   * - lockedBy: 当前持有编辑锁的教师 ID（null 表示无锁）
   * - lockedAt: 锁获取时间（用于过期判定，默认 5 分钟自动释放）
   * - lockToken: 锁定令牌（前端续期时校验，防止伪造他人续期）
   */
  lockedBy: varchar("locked_by", { length: 128 }),
  lockedAt: timestamp("locked_at"),
  lockToken: varchar("lock_token", { length: 64 }),
}, (table) => ({
  userDraftIdx: uniqueIndex("gd_user_class_subject_type_idx").on(
    table.userId,
    table.classId,
    table.subjectId,
    table.type
  ),
  userUpdatedIdx: index("gd_user_updated_idx").on(table.userId, table.updatedAt),
  // P3-6: 班级-科目-类型粒度的锁查询索引（用于跨用户协同冲突检测）
  classSubjectLockIdx: index("gd_class_subject_lock_idx").on(
    table.classId,
    table.subjectId,
    table.type,
    table.lockedBy
  ),
}));

/**
 * P3-7 成绩申诉状态机枚举。
 * - pending: 学生已提交申诉，等待教师复核
 * - approved: 教师批准申诉（可能调整分数）
 * - rejected: 教师驳回申诉（维持原分数）
 * - withdrawn: 学生主动撤回申诉
 */
export const gradeAppealStatusEnum = mysqlEnum("appeal_status", [
  "pending",
  "approved",
  "rejected",
  "withdrawn",
]);

/**
 * 成绩申诉记录 - 学生对成绩提出异议，教师复核后修改并留痕。
 *
 * P3-7: 满足合规性要求，成绩修改全程留痕。
 * 状态机：pending → approved/rejected/withdrawn
 * 一条 grade_record 只允许有一条 pending 申诉，避免重复申诉。
 */
export const gradeAppeals = mysqlTable("grade_appeals", {
  id: id("id").primaryKey(),
  gradeRecordId: varchar("grade_record_id", { length: 128 }).notNull().references(() => gradeRecords.id, { onDelete: "cascade" }),
  studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  /** 申诉快照：申诉时的原始分数（审计留痕，防止后续修改后追溯困难） */
  originalScore: decimal("original_score", { precision: 6, scale: 2 }).notNull(),
  /** 学生期望的分数（选填，便于教师判断诉求） */
  expectedScore: decimal("expected_score", { precision: 6, scale: 2 }),
  /** 申诉理由 */
  reason: text("reason").notNull(),
  status: gradeAppealStatusEnum.default("pending").notNull(),
  /** 复核教师 ID（pending 状态为 null） */
  reviewerId: varchar("reviewer_id", { length: 128 }).references(() => users.id, { onDelete: "set null" }),
  /** 教师复核意见 */
  reviewComment: text("review_comment"),
  /** 审核后的调整分数（approved 且分数有变时填写） */
  adjustedScore: decimal("adjusted_score", { precision: 6, scale: 2 }),
  reviewedAt: timestamp("reviewed_at"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  gradeRecordIdx: index("ga_grade_record_idx").on(table.gradeRecordId),
  studentIdx: index("ga_student_idx").on(table.studentId),
  statusIdx: index("ga_status_idx").on(table.status),
  reviewerIdx: index("ga_reviewer_idx").on(table.reviewerId),
  /** pending 状态查询索引（应用层校验唯一性，避免多次申诉） */
  pendingLookupIdx: index("ga_pending_lookup_idx").on(
    table.gradeRecordId,
    table.status
  ),
}));

// --- 28. Adaptive Practice (专项练习闭环) ---

/**
 * 专项练习类型。
 * - error_variant: 错题变式练习（从错题本发起，生成变式题）
 * - knowledge_point: 知识点专项练习（按知识点抽题）
 * - weak_chapter: 薄弱章节练习（按掌握度自动推荐）
 * - ai_recommended: AI 推荐练习（AI 根据学情综合推荐）
 */
export const practiceTypeEnum = mysqlEnum("practice_type", [
  "error_variant",
  "knowledge_point",
  "weak_chapter",
  "ai_recommended",
]);

export const practiceStatusEnum = mysqlEnum("practice_status", [
  "in_progress",
  "completed",
  "abandoned",
]);

export const practiceAnswerStatusEnum = mysqlEnum("practice_answer_status", [
  "pending",
  "answered",
  "skipped",
]);

/**
 * 专项练习会话 - 学生发起的一次专项练习。
 *
 * 记录练习类型、来源元数据（知识点/错题/章节）、状态和统计。
 * 练习题目存储在 practiceAnswers 表中。
 */
export const practiceSessions = mysqlTable("practice_sessions", {
  id: id("id").primaryKey(),
  studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  subjectId: varchar("subject_id", { length: 128 }),

  practiceType: practiceTypeEnum.notNull(),
  /** 来源元数据：{ knowledgePointIds?, errorBookItemIds?, chapterId?, difficulty?, sourceQuestionId? } */
  sourceMeta: json("source_meta"),

  status: practiceStatusEnum.default("in_progress").notNull(),

  totalQuestions: int("total_questions").default(0).notNull(),
  answeredQuestions: int("answered_questions").default(0).notNull(),
  correctCount: int("correct_count").default(0).notNull(),

  startedAt: timestamp("started_at", { mode: "date" }).defaultNow().notNull(),
  completedAt: timestamp("completed_at", { mode: "date" }),

  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  studentIdx: index("ps_student_idx").on(table.studentId),
  studentStatusIdx: index("ps_student_status_idx").on(table.studentId, table.status),
  subjectIdx: index("ps_subject_idx").on(table.subjectId),
}));

/**
 * 专项练习答题记录 - 会话中每道题的作答情况。
 *
 * 支持两种题目来源：
 * 1. 题库中的题目（questionId 指向 questions 表）
 * 2. AI 生成的变式题（variantContent 存储 JSON 内容，questionId 指向原题）
 */
export const practiceAnswers = mysqlTable("practice_answers", {
  id: id("id").primaryKey(),
  sessionId: varchar("session_id", { length: 128 }).notNull().references(() => practiceSessions.id, { onDelete: "cascade" }),
  studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),

  questionId: varchar("question_id", { length: 128 }).notNull().references(() => questions.id),
  /** 变式题内容（AI 生成的变式题，未入库时存储在此字段） */
  variantContent: json("variant_content"),
  /** 是否为变式题 */
  isVariant: boolean("is_variant").default(false).notNull(),

  orderIndex: int("order_index").notNull(),
  status: practiceAnswerStatusEnum.default("pending").notNull(),

  studentAnswer: json("student_answer"),
  isCorrect: boolean("is_correct"),
  score: int("score"),
  maxScore: int("max_score").default(1).notNull(),

  answeredAt: timestamp("answered_at", { mode: "date" }),

  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  sessionIdx: index("pa_session_idx").on(table.sessionId),
  studentIdx: index("pa_student_idx").on(table.studentId),
  questionIdx: index("pa_question_idx").on(table.questionId),
  sessionOrderIdx: index("pa_session_order_idx").on(table.sessionId, table.orderIndex),
}));

// --- 29. Standards (M1 课标对标体系) ---

/**
 * M1 课标库：支持国家标准/课标/自定义三层级。
 * 课案通过 lessonPlanStandards 关联表实现多对多关系。
 */
export const standards = mysqlTable("standards", {
  id: id("id").primaryKey(),
  code: varchar("code", { length: 100 }).notNull(),
  title: varchar("title", { length: 255 }).notNull(),
  description: text("description"),
  /** 层级：national（国家标准）/ curriculum（课标）/ custom（自定义） */
  level: varchar("level", { length: 20 }).notNull(),
  parentId: varchar("parent_id", { length: 128 }),
  subjectId: varchar("subject_id", { length: 128 }).references(() => subjects.id),
  gradeId: varchar("grade_id", { length: 128 }).references(() => grades.id),
  /** 学段：primary / junior_high / senior_high */
  stage: varchar("stage", { length: 20 }),
  sortOrder: int("sort_order").default(0).notNull(),
  isActive: boolean("is_active").default(true).notNull(),
  createdBy: varchar("created_by", { length: 128 }).references(() => users.id),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  codeIdx: uniqueIndex("std_code_idx").on(table.code),
  parentIdx: index("std_parent_idx").on(table.parentId),
  subjectGradeIdx: index("std_subject_grade_idx").on(table.subjectId, table.gradeId),
  levelIdx: index("std_level_idx").on(table.level),
}));

/** M1 课案 ↔ 课标 多对多关联 */
export const lessonPlanStandards = mysqlTable("lesson_plan_standards", {
  id: id("id").primaryKey(),
  planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }),
  standardId: varchar("standard_id", { length: 128 }).notNull().references(() => standards.id, { onDelete: "cascade" }),
  /** 关联类型：primary（主要对标）/ related（相关对标） */
  relationType: varchar("relation_type", { length: 20 }).default("primary").notNull(),
  createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id),
  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  planStandardIdx: uniqueIndex("lps_plan_standard_idx").on(table.planId, table.standardId),
  standardIdx: index("lps_standard_idx").on(table.standardId),
}));

// --- 30. Formative Assessment (M5 形成性评价闭环) ---

/**
 * M5 互动组件：发布课案时嵌入的 poll/quiz。
 * 学生作答后结果回写到 lessonPlanFormativeResponses。
 */
export const lessonPlanFormativeItems = mysqlTable("lesson_plan_formative_items", {
  id: id("id").primaryKey(),
  planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }),
  blockId: varchar("block_id", { length: 128 }).notNull(),
  /** 互动类型：poll / quiz / exit_ticket */
  interactionType: varchar("interaction_type", { length: 20 }).notNull(),
  /** 题目内容（JSON：题目/选项/正确答案） */
  payload: json("payload").notNull(),
  /** 是否启用实时反馈 */
  instantFeedback: boolean("instant_feedback").default(false).notNull(),
  orderIndex: int("order_index").default(0).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
  planBlockIdx: index("lpfi_plan_block_idx").on(table.planId, table.blockId),
}));

/** M5 学生作答记录 */
export const lessonPlanFormativeResponses = mysqlTable("lesson_plan_formative_responses", {
  id: id("id").primaryKey(),
  // 显式 foreignKey 见表参数（避免自动生成 FK 名过长 > 64 字符）
  itemId: varchar("item_id", { length: 128 }).notNull(),
  studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
  classId: varchar("class_id", { length: 128 }),
  /** 学生作答（JSON） */
  response: json("response").notNull(),
  /** 是否正确（quiz 类型适用） */
  isCorrect: boolean("is_correct"),
  /** 作答耗时（秒） */
  durationSec: int("duration_sec"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  itemIdx: index("lpfr_item_idx").on(table.itemId),
  studentIdx: index("lpfr_student_idx").on(table.studentId),
  classIdx: index("lpfr_class_idx").on(table.classId),
  itemFk: foreignKey({
    columns: [table.itemId],
    foreignColumns: [lessonPlanFormativeItems.id],
    name: "lpfr_item_fk",
  }).onDelete("cascade"),
}));

// --- 31. Lesson Plan Analytics (M10 备课分析仪表盘) ---

/**
 * M10 备课分析快照：按日聚合的备课投入/质量统计。
 * 用于仪表盘展示"教师备课投入""模板使用率""课标覆盖热力图"。
 */
export const lessonPlanAnalyticsDaily = mysqlTable("lesson_plan_analytics_daily", {
  id: id("id").primaryKey(),
  snapshotDate: date("snapshot_date").notNull(),
  teacherId: varchar("teacher_id", { length: 128 }).notNull().references(() => users.id),
  subjectId: varchar("subject_id", { length: 128 }),
  gradeId: varchar("grade_id", { length: 128 }),
  /** 当日新建课案数 */
  newPlansCount: int("new_plans_count").default(0).notNull(),
  /** 当日编辑时长（分钟） */
  editDurationMin: int("edit_duration_min").default(0).notNull(),
  /** 当日保存版本数 */
  savedVersionsCount: int("saved_versions_count").default(0).notNull(),
  /** 当日提交审核数 */
  submittedCount: int("submitted_count").default(0).notNull(),
  /** 当日发布数 */
  publishedCount: int("published_count").default(0).notNull(),
  /** 关联的课标数量 */
  standardsLinked: int("standards_linked").default(0).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  dateTeacherIdx: uniqueIndex("lpad_date_teacher_idx").on(table.snapshotDate, table.teacherId),
  dateIdx: index("lpad_date_idx").on(table.snapshotDate),
  subjectGradeIdx: index("lpad_subject_grade_idx").on(table.subjectId, table.gradeId),
}));

// --- 32. Lesson Plan AI Evaluation (M8 AI 评估) ---

/**
 * M8 AI 课案质量评估记录：保存 AI 对课案的质量评分与建议。
 */
export const lessonPlanAiEvaluations = mysqlTable("lesson_plan_ai_evaluations", {
  id: id("id").primaryKey(),
  planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }),
  versionNo: int("version_no").notNull(),
  /** 总体评分（0-100） */
  overallScore: int("overall_score"),
  /** 维度评分（JSON：{ clarity, alignment, engagement, differentiation, assessment }） */
  dimensionScores: json("dimension_scores"),
  /** AI 建议文本 */
  suggestions: text("suggestions"),
  /** 推荐的课标 ID 列表（JSON） */
  recommendedStandardIds: json("recommended_standard_ids"),
  evaluatedBy: varchar("evaluated_by", { length: 128 }).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  planVersionIdx: index("lpae_plan_version_idx").on(table.planId, table.versionNo),
  planIdx: index("lpae_plan_idx").on(table.planId),
}));
