import "server-only"

import { cache } from "react"
import { and, desc, eq, inArray, sql } from "drizzle-orm"

import { db } from "@/shared/db"
import {
  homeworkAssignmentTargets,
  homeworkAssignments,
  homeworkSubmissions,
} from "@/shared/db/schema"
import { getExamSubjectIdMap } from "@/modules/exams/data-access"
import { getSubjectNameMapByIds } from "@/modules/school/data-access"

/**
 * This file exposes homework data needed by the classes module.
 * It exists to preserve the three-layer architecture: classes module
 * must not directly query homework/exams tables.
 *
 * All functions return plain data records; callers are responsible for
 * any further aggregation/statistics.
 *
 * P1-1 修复：不再直接 import exams/subjects 表，改通过 exams/school
 * 模块的 data-access 跨模块函数获取科目信息。
 */

export type HomeworkAssignmentWithSubject = {
  id: string
  title: string
  status: string | null
  createdAt: Date
  dueAt: Date | null
  subjectId: string | null
  subjectName: string | null
}

export type HomeworkAssignmentBrief = {
  id: string
  title: string
  status: string | null
  createdAt: Date
  dueAt: Date | null
}

export type HomeworkSubmissionRecord = {
  id: string
  assignmentId: string
  studentId: string
  status: string | null
  score: number | null
  createdAt: Date
}

export type HomeworkSubmissionScoreRecord = {
  studentId: string
  assignmentId: string
  score: number | null
  createdAt: Date
}

export type HomeworkAssignmentSubjectRow = {
  id: string
  createdAt: Date
  subjectId: string | null
  subjectName: string | null
}

/**
 * Returns assignment IDs that target any of the given students.
 */
export const getAssignmentIdsForStudents = cache(
  async (studentIds: string[]): Promise<string[]> => {
    if (studentIds.length === 0) return []
    const rows = await db
      .selectDistinct({ assignmentId: homeworkAssignmentTargets.assignmentId })
      .from(homeworkAssignmentTargets)
      .where(inArray(homeworkAssignmentTargets.studentId, studentIds))
    return rows.map((r) => r.assignmentId)
  }
)

/**
 * Returns homework assignments joined with subject info (via source exam),
 * optionally filtered by subject IDs. Used by class-level homework insights.
 *
 * P1-1 修复：不再 JOIN exams/subjects 表，改为：
 * 1. 查 homeworkAssignments（含 sourceExamId）
 * 2. 通过 exams data-access 批量获取 examId→subjectId 映射
 * 3. 通过 school data-access 批量获取 subjectId→name 映射
 * 4. 在内存中合并与过滤
 */
export const getHomeworkAssignmentsWithSubject = cache(
  async (params: {
    assignmentIds: string[]
    subjectIdFilter?: string[]
    limit?: number
  }): Promise<HomeworkAssignmentWithSubject[]> => {
    if (params.assignmentIds.length === 0) return []
    const limit = typeof params.limit === "number" && params.limit > 0 ? params.limit : 50

    // Step 1: 查 homeworkAssignments（含 sourceExamId）
    const rows = await db
      .select({
        id: homeworkAssignments.id,
        title: homeworkAssignments.title,
        status: homeworkAssignments.status,
        createdAt: homeworkAssignments.createdAt,
        dueAt: homeworkAssignments.dueAt,
        sourceExamId: homeworkAssignments.sourceExamId,
      })
      .from(homeworkAssignments)
      .where(inArray(homeworkAssignments.id, params.assignmentIds))
      .orderBy(desc(homeworkAssignments.createdAt))
      .limit(limit)

    if (rows.length === 0) return []

    // Step 2: 通过 exams data-access 批量获取 examId→subjectId
    const examIds = rows
      .map((r) => r.sourceExamId)
      .filter((id): id is string => id !== null && id.length > 0)
    const examSubjectMap = await getExamSubjectIdMap(examIds)

    // Step 3: 通过 school data-access 批量获取 subjectId→name
    const subjectIds = Array.from(examSubjectMap.values()).filter(
      (id): id is string => id !== null && id.length > 0
    )
    const subjectNameMap = await getSubjectNameMapByIds(subjectIds)

    // Step 4: 在内存中合并与过滤
    const subjectIdFilterSet = params.subjectIdFilter && params.subjectIdFilter.length > 0
      ? new Set(params.subjectIdFilter)
      : null

    return rows
      .map((r) => {
        const subjectId = r.sourceExamId ? (examSubjectMap.get(r.sourceExamId) ?? null) : null
        const subjectName = subjectId ? (subjectNameMap.get(subjectId) ?? null) : null
        return {
          id: r.id,
          title: r.title,
          status: r.status,
          createdAt: r.createdAt,
          dueAt: r.dueAt,
          subjectId,
          subjectName,
        }
      })
      .filter((item) => {
        if (!subjectIdFilterSet) return true
        return item.subjectId !== null && subjectIdFilterSet.has(item.subjectId)
      })
  }
)

/**
 * Returns homework assignments (without subject info) by IDs.
 * Used by grade-level homework insights where subject filtering is not needed.
 */
export const getHomeworkAssignmentsByIds = cache(
  async (params: { assignmentIds: string[]; limit?: number }): Promise<HomeworkAssignmentBrief[]> => {
    if (params.assignmentIds.length === 0) return []
    const limit = typeof params.limit === "number" && params.limit > 0 ? params.limit : 50
    const rows = await db.query.homeworkAssignments.findMany({
      where: inArray(homeworkAssignments.id, params.assignmentIds),
      orderBy: [desc(homeworkAssignments.createdAt)],
      limit,
      columns: {
        id: true,
        title: true,
        status: true,
        createdAt: true,
        dueAt: true,
      },
    })
    return rows
  }
)

/**
 * Returns max score per assignment (sum of question scores).
 * Re-exported from data-access for classes module convenience.
 */
export { getAssignmentMaxScoreById } from "./data-access"

/**
 * Returns target counts per assignment for the given students.
 */
export const getAssignmentTargetCounts = cache(
  async (params: { assignmentIds: string[]; studentIds: string[] }): Promise<Map<string, number>> => {
    if (params.assignmentIds.length === 0 || params.studentIds.length === 0) return new Map()
    const rows = await db
      .select({
        assignmentId: homeworkAssignmentTargets.assignmentId,
        targetCount: sql<number>`COUNT(*)`,
      })
      .from(homeworkAssignmentTargets)
      .where(
        and(
          inArray(homeworkAssignmentTargets.assignmentId, params.assignmentIds),
          inArray(homeworkAssignmentTargets.studentId, params.studentIds)
        )
      )
      .groupBy(homeworkAssignmentTargets.assignmentId)
    const map = new Map<string, number>()
    for (const r of rows) map.set(r.assignmentId, Number(r.targetCount ?? 0))
    return map
  }
)

/**
 * Returns homework submissions for given assignments and students,
 * ordered by createdAt desc so callers can pick the latest per
 * (assignmentId, studentId) pair.
 */
export const getHomeworkSubmissionsForStudents = cache(
  async (params: { assignmentIds: string[]; studentIds: string[] }): Promise<HomeworkSubmissionRecord[]> => {
    if (params.assignmentIds.length === 0 || params.studentIds.length === 0) return []
    const rows = await db.query.homeworkSubmissions.findMany({
      where: and(
        inArray(homeworkSubmissions.assignmentId, params.assignmentIds),
        inArray(homeworkSubmissions.studentId, params.studentIds)
      ),
      orderBy: [desc(homeworkSubmissions.createdAt)],
      columns: {
        id: true,
        assignmentId: true,
        studentId: true,
        status: true,
        score: true,
        createdAt: true,
      },
    })
    return rows
  }
)

/**
 * Returns published homework assignments joined with subject info (via source exam).
 * Used by student subject score aggregation.
 *
 * P1-1 修复：不再 JOIN exams/subjects 表，改用跨模块 data-access。
 */
export const getPublishedHomeworkAssignmentsWithSubject = cache(
  async (params: { assignmentIds: string[] }): Promise<HomeworkAssignmentSubjectRow[]> => {
    if (params.assignmentIds.length === 0) return []

    // Step 1: 查 published homeworkAssignments（含 sourceExamId）
    const rows = await db
      .select({
        id: homeworkAssignments.id,
        createdAt: homeworkAssignments.createdAt,
        sourceExamId: homeworkAssignments.sourceExamId,
      })
      .from(homeworkAssignments)
      .where(
        and(
          inArray(homeworkAssignments.id, params.assignmentIds),
          eq(homeworkAssignments.status, "published")
        )
      )
      .orderBy(desc(homeworkAssignments.createdAt))

    if (rows.length === 0) return []

    // Step 2: 通过 exams data-access 批量获取 examId→subjectId
    const examIds = rows
      .map((r) => r.sourceExamId)
      .filter((id): id is string => id !== null && id.length > 0)
    const examSubjectMap = await getExamSubjectIdMap(examIds)

    // Step 3: 通过 school data-access 批量获取 subjectId→name
    const subjectIds = Array.from(examSubjectMap.values()).filter(
      (id): id is string => id !== null && id.length > 0
    )
    const subjectNameMap = await getSubjectNameMapByIds(subjectIds)

    // Step 4: 在内存中合并
    return rows.map((r) => {
      const subjectId = r.sourceExamId ? (examSubjectMap.get(r.sourceExamId) ?? null) : null
      const subjectName = subjectId ? (subjectNameMap.get(subjectId) ?? null) : null
      return {
        id: r.id,
        createdAt: r.createdAt,
        subjectId,
        subjectName,
      }
    })
  }
)

/**
 * Returns homework submissions for the given assignments,
 * ordered by createdAt desc so callers can pick the latest per
 * (assignmentId, studentId) pair.
 */
export const getHomeworkSubmissionsForAssignments = cache(
  async (assignmentIds: string[]): Promise<HomeworkSubmissionScoreRecord[]> => {
    if (assignmentIds.length === 0) return []
    const rows = await db
      .select({
        studentId: homeworkSubmissions.studentId,
        assignmentId: homeworkSubmissions.assignmentId,
        score: homeworkSubmissions.score,
        createdAt: homeworkSubmissions.createdAt,
      })
      .from(homeworkSubmissions)
      .where(inArray(homeworkSubmissions.assignmentId, assignmentIds))
      .orderBy(desc(homeworkSubmissions.createdAt))
    return rows
  }
)
