import "server-only"

import { createId } from "@paralleldrive/cuid2"
import { and, count, eq } from "drizzle-orm"

import { db } from "@/shared/db"
import {
  homeworkAnswers,
  homeworkAssignmentQuestions,
  homeworkAssignmentTargets,
  homeworkAssignments,
  homeworkSubmissions,
} from "@/shared/db/schema"
import {
  getActiveStudentIdsByClassId,
  getClassTeacherById as getClassTeacherIdFromClass,
  getTeacherSubjectIdsByClass,
} from "@/modules/classes/data-access"
import {
  getExamWithQuestionsForHomework as getExamWithQuestionsFromExams,
  type ExamWithQuestionsForHomework,
} from "@/modules/exams/data-access"
import type { DataScope } from "@/shared/types/permissions"
import {
  autoGradeSubmission,
  type AutoGradableAnswer,
} from "./lib/question-content-utils"

// ---- Types ----

export type HomeworkExamQuestionData = {
  questionId: string
  score: number | null
  order: number | null
}

export type HomeworkExamData = ExamWithQuestionsForHomework

export type HomeworkSubmissionPermissionData = {
  id: string
  studentId: string
  status: string | null
  assignment: {
    dueAt: Date | null
    allowLate: boolean
    lateDueAt: Date | null
  }
}

export type CreateHomeworkAssignmentData = {
  assignmentId: string
  sourceExamId: string | null
  title: string
  description: string | null
  structure: unknown
  status: string
  creatorId: string
  availableAt: Date | null
  dueAt: Date | null
  allowLate: boolean
  lateDueAt: Date | null
  maxAttempts: number
  publish: boolean
  questions: HomeworkExamQuestionData[]
  targetStudentIds: string[]
}

// ---- Query helpers (for permission/validation in actions) ----
// These delegate to cross-module data-access interfaces to avoid direct DB queries.

export const getClassTeacherById = async (
  classId: string
): Promise<{ id: string; teacherId: string | null } | null> => {
  const teacherId = await getClassTeacherIdFromClass(classId)
  if (teacherId === null) return null
  return { id: classId, teacherId }
}

export const getExamWithQuestionsForHomework = async (
  examId: string
): Promise<HomeworkExamData | null> => {
  return await getExamWithQuestionsFromExams(examId)
}

export const getTeacherAssignedSubjectIds = async (
  classId: string,
  teacherId: string
): Promise<string[]> => {
  return await getTeacherSubjectIdsByClass(classId, teacherId)
}

export const getActiveClassStudentIdsForHomework = async (
  classId: string,
  _dataScope: DataScope,
  _userId: string,
  _classTeacherId: string | null
): Promise<string[]> => {
  // Permission/scope filtering is handled by requirePermission in actions.ts.
  // This function returns active students for the class via the classes data-access interface.
  return await getActiveStudentIdsByClassId(classId)
}

export const getHomeworkSubmissionForPermission = async (
  submissionId: string
): Promise<HomeworkSubmissionPermissionData | null> => {
  const submission = await db.query.homeworkSubmissions.findFirst({
    where: eq(homeworkSubmissions.id, submissionId),
    with: { assignment: true },
  })
  if (!submission) return null
  return {
    id: submission.id,
    studentId: submission.studentId,
    status: submission.status,
    assignment: {
      dueAt: submission.assignment.dueAt,
      allowLate: submission.assignment.allowLate,
      lateDueAt: submission.assignment.lateDueAt,
    },
  }
}

/**
 * 批改权限校验：获取提交记录及其作业的创建者信息
 * 用于 gradeHomeworkSubmissionAction 校验教师是否有权批改该提交
 * 返回 null 表示提交记录不存在
 */
export const getHomeworkSubmissionForGrading = async (
  submissionId: string
): Promise<{
  id: string
  assignmentId: string
  creatorId: string
  sourceExamId: string | null
  studentId: string
} | null> => {
  const submission = await db.query.homeworkSubmissions.findFirst({
    where: eq(homeworkSubmissions.id, submissionId),
    with: { assignment: true },
  })
  if (!submission) return null
  return {
    id: submission.id,
    assignmentId: submission.assignmentId,
    creatorId: submission.assignment.creatorId,
    sourceExamId: submission.assignment.sourceExamId,
    studentId: submission.studentId,
  }
}

// ---- Write functions ----

export const createHomeworkAssignment = async (
  input: CreateHomeworkAssignmentData
): Promise<string> => {
  await db.transaction(async (tx) => {
    await tx.insert(homeworkAssignments).values({
      id: input.assignmentId,
      sourceExamId: input.sourceExamId,
      title: input.title,
      description: input.description,
      structure: input.publish ? input.structure : null,
      status: input.status,
      creatorId: input.creatorId,
      availableAt: input.availableAt,
      dueAt: input.dueAt,
      allowLate: input.allowLate,
      lateDueAt: input.lateDueAt,
      maxAttempts: input.maxAttempts,
    })

    if (input.publish && input.questions.length > 0) {
      await tx.insert(homeworkAssignmentQuestions).values(
        input.questions.map((q) => ({
          assignmentId: input.assignmentId,
          questionId: q.questionId,
          score: q.score ?? 0,
          order: q.order ?? 0,
        }))
      )
    }

    if (input.publish && input.targetStudentIds.length > 0) {
      await tx.insert(homeworkAssignmentTargets).values(
        input.targetStudentIds.map((studentId) => ({
          assignmentId: input.assignmentId,
          studentId,
        }))
      )
    }
  })

  return input.assignmentId
}

export const startHomeworkSubmission = async (
  assignmentId: string,
  studentId: string
): Promise<{ submissionId: string } | { error: string }> => {
  const assignment = await db.query.homeworkAssignments.findFirst({
    where: eq(homeworkAssignments.id, assignmentId),
  })
  if (!assignment) return { error: "Assignment not found" }
  if (assignment.status !== "published") return { error: "Assignment not available" }

  const target = await db.query.homeworkAssignmentTargets.findFirst({
    where: and(
      eq(homeworkAssignmentTargets.assignmentId, assignmentId),
      eq(homeworkAssignmentTargets.studentId, studentId)
    ),
  })
  if (!target) return { error: "Not assigned" }

  if (assignment.availableAt && assignment.availableAt > new Date()) {
    return { error: "Not available yet" }
  }

  const [attemptRow] = await db
    .select({ c: count() })
    .from(homeworkSubmissions)
    .where(
      and(
        eq(homeworkSubmissions.assignmentId, assignmentId),
        eq(homeworkSubmissions.studentId, studentId)
      )
    )

  const attemptNo = (attemptRow?.c ?? 0) + 1
  if (attemptNo > assignment.maxAttempts) return { error: "No attempts left" }

  const submissionId = createId()
  await db.insert(homeworkSubmissions).values({
    id: submissionId,
    assignmentId,
    studentId,
    attemptNo,
    status: "started",
    startedAt: new Date(),
  })

  return { submissionId }
}

export const saveHomeworkAnswer = async (
  submissionId: string,
  questionId: string,
  answerContent: unknown
): Promise<void> => {
  await db.transaction(async (tx) => {
    const existing = await tx.query.homeworkAnswers.findFirst({
      where: and(
        eq(homeworkAnswers.submissionId, submissionId),
        eq(homeworkAnswers.questionId, questionId)
      ),
    })

    if (existing) {
      await tx
        .update(homeworkAnswers)
        .set({ answerContent, updatedAt: new Date() })
        .where(eq(homeworkAnswers.id, existing.id))
    } else {
      await tx.insert(homeworkAnswers).values({
        id: createId(),
        submissionId,
        questionId,
        answerContent,
      })
    }
  })
}

export const markHomeworkSubmitted = async (
  submissionId: string,
  isLate: boolean
): Promise<{ isFullyAutoGraded: boolean; totalScore: number }> => {
  const now = new Date()

  // V3-2: 即时自动批改回写
  // 1. 获取提交的所有答案 + 题目元数据
  // 2. 调用 autoGradeSubmission 计算分数
  // 3. 回写答案分数 + 提交状态 + 总分
  const submission = await db.query.homeworkSubmissions.findFirst({
    where: eq(homeworkSubmissions.id, submissionId),
    columns: { id: true, assignmentId: true },
  })

  if (!submission) {
    throw new Error(`Submission not found: ${submissionId}`)
  }

  const [answers, assignmentQuestions] = await Promise.all([
    db.query.homeworkAnswers.findMany({
      where: eq(homeworkAnswers.submissionId, submissionId),
      columns: { id: true, questionId: true, answerContent: true, score: true, feedback: true },
    }),
    db.query.homeworkAssignmentQuestions.findMany({
      where: eq(homeworkAssignmentQuestions.assignmentId, submission.assignmentId),
      with: { question: { columns: { type: true, content: true } } },
      columns: { questionId: true, score: true, order: true },
    }),
  ])

  // 构建 AutoGradableAnswer 数组
  const questionMetaMap = new Map(assignmentQuestions.map((aq) => [aq.questionId, aq]))
  const autoGradableInputs: AutoGradableAnswer[] = answers.map((ans) => {
    const meta = questionMetaMap.get(ans.questionId)
    return {
      id: ans.id,
      questionId: ans.questionId,
      questionType: meta?.question.type ?? "text",
      questionContent: meta?.question.content ?? null,
      maxScore: meta?.score ?? 0,
      studentAnswer: ans.answerContent,
      score: ans.score,
      feedback: ans.feedback,
      order: meta?.order ?? 0,
    }
  })

  // 执行自动批改
  const { answers: gradedAnswers, isFullyAutoGraded } = autoGradeSubmission(autoGradableInputs)
  const totalScore = gradedAnswers.reduce((sum, a) => sum + a.score, 0)

  // 回写 DB：答案分数 + 提交状态
  await db.transaction(async (tx) => {
    // 批量更新答案分数
    for (const ans of gradedAnswers) {
      await tx
        .update(homeworkAnswers)
        .set({ score: ans.score, updatedAt: now })
        .where(
          and(
            eq(homeworkAnswers.id, ans.id),
            eq(homeworkAnswers.submissionId, submissionId)
          )
        )
    }

    // 更新提交状态：全部可自动判分 → graded；含主观题 → submitted
    await tx
      .update(homeworkSubmissions)
      .set({
        status: isFullyAutoGraded ? "graded" : "submitted",
        submittedAt: now,
        isLate,
        score: totalScore,
        updatedAt: now,
      })
      .where(eq(homeworkSubmissions.id, submissionId))
  })

  return { isFullyAutoGraded, totalScore }
}

export const gradeHomeworkAnswers = async (
  submissionId: string,
  answers: Array<{ id: string; score: number; feedback: string | null }>
): Promise<void> => {
  await db.transaction(async (tx) => {
    let totalScore = 0
    for (const ans of answers) {
      // 关键安全约束：WHERE 子句同时匹配 answer.id 和 submissionId，
      // 防止恶意客户端篡改 answer ID 批改其他 submission 的答案
      await tx
        .update(homeworkAnswers)
        .set({ score: ans.score, feedback: ans.feedback, updatedAt: new Date() })
        .where(
          and(
            eq(homeworkAnswers.id, ans.id),
            eq(homeworkAnswers.submissionId, submissionId)
          )
        )
      totalScore += ans.score
    }

    await tx
      .update(homeworkSubmissions)
      .set({ score: totalScore, status: "graded", updatedAt: new Date() })
      .where(eq(homeworkSubmissions.id, submissionId))
  })
}

/**
 * V3-7: 批量自动批改提交
 *
 * 对多个提交执行自动批改（复用 autoGradeSubmission 逻辑）。
 * 仅批改客观题（选择题/判断题），主观题保持原分数。
 *
 * @param submissionIds 要批量批改的提交 ID 列表
 * @returns 每个提交的批改结果摘要
 */
export const batchAutoGradeSubmissions = async (
  submissionIds: string[]
): Promise<Array<{
  submissionId: string
  success: boolean
  isFullyAutoGraded: boolean
  totalScore: number
  studentId?: string
  message?: string
}>> => {
  const now = new Date()
  const results: Array<{
    submissionId: string
    success: boolean
    isFullyAutoGraded: boolean
    totalScore: number
    studentId?: string
    message?: string
  }> = []

  for (const submissionId of submissionIds) {
    try {
      const submission = await db.query.homeworkSubmissions.findFirst({
        where: eq(homeworkSubmissions.id, submissionId),
        columns: { id: true, assignmentId: true, studentId: true },
      })

      if (!submission) {
        results.push({
          submissionId,
          success: false,
          isFullyAutoGraded: false,
          totalScore: 0,
          message: "Submission not found",
        })
        continue
      }

      const [answers, assignmentQuestions] = await Promise.all([
        db.query.homeworkAnswers.findMany({
          where: eq(homeworkAnswers.submissionId, submissionId),
          columns: { id: true, questionId: true, answerContent: true, score: true, feedback: true },
        }),
        db.query.homeworkAssignmentQuestions.findMany({
          where: eq(homeworkAssignmentQuestions.assignmentId, submission.assignmentId),
          with: { question: { columns: { type: true, content: true } } },
          columns: { questionId: true, score: true, order: true },
        }),
      ])

      const questionMetaMap = new Map(assignmentQuestions.map((aq) => [aq.questionId, aq]))
      const autoGradableInputs: AutoGradableAnswer[] = answers.map((ans) => {
        const meta = questionMetaMap.get(ans.questionId)
        return {
          id: ans.id,
          questionId: ans.questionId,
          questionType: meta?.question.type ?? "text",
          questionContent: meta?.question.content ?? null,
          maxScore: meta?.score ?? 0,
          studentAnswer: ans.answerContent,
          score: ans.score,
          feedback: ans.feedback,
          order: meta?.order ?? 0,
        }
      })

      const { answers: gradedAnswers, isFullyAutoGraded } = autoGradeSubmission(autoGradableInputs)
      const totalScore = gradedAnswers.reduce((sum, a) => sum + a.score, 0)

      await db.transaction(async (tx) => {
        for (const ans of gradedAnswers) {
          await tx
            .update(homeworkAnswers)
            .set({ score: ans.score, updatedAt: now })
            .where(
              and(
                eq(homeworkAnswers.id, ans.id),
                eq(homeworkAnswers.submissionId, submissionId)
              )
            )
        }

        await tx
          .update(homeworkSubmissions)
          .set({
            status: isFullyAutoGraded ? "graded" : "submitted",
            score: totalScore,
            updatedAt: now,
          })
          .where(eq(homeworkSubmissions.id, submissionId))
      })

      results.push({
        submissionId,
        success: true,
        isFullyAutoGraded,
        totalScore,
        studentId: submission.studentId,
      })
    } catch (error) {
      results.push({
        submissionId,
        success: false,
        isFullyAutoGraded: false,
        totalScore: 0,
        message: error instanceof Error ? error.message : "Unknown error",
      })
    }
  }

  return results
}
