import "server-only"

import { createId } from "@paralleldrive/cuid2"
import { and, count, desc, eq, inArray, type SQL } from "drizzle-orm"
import { cache } from "react"
import { getTranslations } from "next-intl/server"

import { db } from "@/shared/db"
import { learningDiagnosticReports } from "@/shared/db/schema"
import { getUserNamesByIds, getUserIdsByGradeId } from "@/modules/users/data-access"
import { getStudentIdsByClassIds } from "@/modules/classes/data-access"
import { toNumber } from "@/modules/grades/lib/grade-utils"
import { BusinessError } from "@/shared/lib/action-utils"
import type { DataScope } from "@/shared/types/permissions"

import { getClassMasterySummary, getGradeMasterySummary, getStudentMasterySummary } from "./data-access"
import {
  buildClassReportContent,
  buildGradeReportContent,
  buildStudentReportContent,
  type ReportContentTranslations,
} from "./stats-service"
import type {
  DiagnosticReport,
  DiagnosticReportListResult,
  DiagnosticReportQueryParams,
  DiagnosticReportWithDetails,
} from "./types"

/**
 * Build report content translations from next-intl.
 * Keeps stats-service free of i18n framework dependencies.
 */
async function getReportContentTranslations(): Promise<ReportContentTranslations> {
  const t = await getTranslations("diagnostic.reportContent")
  return {
    studentSummary: (vars) => t("studentSummary", vars),
    studentRecommendation: (vars) => t("studentRecommendation", vars),
    studentNoWeakness: t("studentNoWeakness"),
    classSummary: (vars) => t("classSummary", vars),
    classRecommendation: (vars) => t("classRecommendation", vars),
    classNoWeakness: t("classNoWeakness"),
    gradeSummary: (vars) => t("gradeSummary", vars),
    gradeRecommendation: (vars) => t("gradeRecommendation", vars),
    gradeNoWeakness: t("gradeNoWeakness"),
  }
}

/**
 * 诊断报告业务错误（P3-27 修复：结构化错误码，避免直接暴露内部错误）。
 * 继承 BusinessError 以便 handleActionError 安全地将 message 返回给客户端。
 */
export class DiagnosticReportError extends BusinessError {
  constructor(
    public readonly code:
      | "STUDENT_NOT_FOUND"
      | "NO_MASTERY_DATA"
      | "CLASS_NOT_FOUND"
      | "CLASS_NO_MASTERY_DATA"
      | "GRADE_NOT_FOUND"
      | "GRADE_NO_MASTERY_DATA",
    message: string,
  ) {
    super(message, code)
    this.name = "DiagnosticReportError"
  }
}

const isStringArray = (v: unknown): v is string[] =>
  Array.isArray(v) && v.every((item) => typeof item === "string")

const toStringArrayNullable = (v: unknown): string[] | null =>
  isStringArray(v) ? v : null

const serializeReport = (r: typeof learningDiagnosticReports.$inferSelect): DiagnosticReport => ({
  id: r.id,
  studentId: r.studentId,
  classId: r.classId,
  gradeId: r.gradeId,
  generatedBy: r.generatedBy,
  reportType: r.reportType,
  period: r.period,
  summary: r.summary,
  strengths: toStringArrayNullable(r.strengths),
  weaknesses: toStringArrayNullable(r.weaknesses),
  recommendations: toStringArrayNullable(r.recommendations),
  overallScore: r.overallScore !== null ? toNumber(r.overallScore) : null,
  status: r.status,
  createdAt: r.createdAt.toISOString(),
  updatedAt: r.updatedAt.toISOString(),
})

/** 生成个人诊断报告 */
export async function generateDiagnosticReport(
  studentId: string,
  period: string,
  generatedBy: string
): Promise<string> {
  const summary = await getStudentMasterySummary(studentId)
  if (!summary) throw new DiagnosticReportError("STUDENT_NOT_FOUND", "学生不存在")

  // P2-6 修复：当学生存在但无任何掌握度数据时，拒绝生成误导性报告
  if (summary.totalKnowledgePoints === 0) {
    throw new DiagnosticReportError("NO_MASTERY_DATA", "学生暂无掌握度数据，无法生成报告")
  }

  const translations = await getReportContentTranslations()
  const { summaryText, strengths, weaknesses, recommendations, overallScore } =
    buildStudentReportContent(summary, period, translations)

  const id = createId()
  await db.insert(learningDiagnosticReports).values({
    id,
    studentId,
    generatedBy,
    reportType: "individual",
    period,
    summary: summaryText,
    strengths,
    weaknesses,
    recommendations,
    overallScore: String(overallScore),
    status: "draft",
  })
  return id
}

/** 生成班级诊断报告 */
export async function generateClassDiagnosticReport(
  classId: string,
  period: string,
  generatedBy: string
): Promise<string> {
  const summary = await getClassMasterySummary(classId)
  if (!summary) throw new DiagnosticReportError("CLASS_NOT_FOUND", "班级不存在")

  // P2-6 修复：当班级存在但无任何掌握度数据时，拒绝生成误导性报告
  if (summary.studentCount === 0 || summary.knowledgePointStats.length === 0) {
    throw new DiagnosticReportError("CLASS_NO_MASTERY_DATA", "班级暂无掌握度数据，无法生成报告")
  }

  const translations = await getReportContentTranslations()
  const { summaryText, strengths, weaknesses, recommendations, overallScore } =
    buildClassReportContent(summary, period, translations)

  const id = createId()
  await db.insert(learningDiagnosticReports).values({
    id,
    studentId: null, // 班级报告无单个学生，studentId 置空（P2-3 修复：不再存生成者 ID）
    generatedBy,
    reportType: "class",
    period,
    summary: summaryText,
    strengths,
    weaknesses,
    recommendations,
    overallScore: String(overallScore),
    status: "draft",
  })
  return id
}

/** v4-P2-3: 生成年级诊断报告 */
export async function generateGradeDiagnosticReport(
  gradeId: string,
  period: string,
  generatedBy: string
): Promise<string> {
  const summary = await getGradeMasterySummary(gradeId)
  if (!summary) throw new DiagnosticReportError("GRADE_NOT_FOUND", "年级不存在")

  // 当年级存在但无任何掌握度数据时，拒绝生成误导性报告
  if (summary.studentCount === 0 || summary.knowledgePointStats.length === 0) {
    throw new DiagnosticReportError("GRADE_NO_MASTERY_DATA", "年级暂无掌握度数据，无法生成报告")
  }

  const translations = await getReportContentTranslations()
  const { summaryText, strengths, weaknesses, recommendations, overallScore } =
    buildGradeReportContent(summary, period, translations)

  const id = createId()
  await db.insert(learningDiagnosticReports).values({
    id,
    studentId: null,
    classId: null,
    gradeId,
    generatedBy,
    reportType: "grade",
    period,
    summary: summaryText,
    strengths,
    weaknesses,
    recommendations,
    overallScore: String(overallScore),
    status: "draft",
  })
  return id
}

/** 查询诊断报告列表（P3-15 修复：支持分页） */
export const getDiagnosticReports = cache(
  async (
    filters: DiagnosticReportQueryParams,
    scope?: DataScope,
  ): Promise<DiagnosticReportListResult> => {
    const conditions: SQL[] = []
    if (filters.studentId) conditions.push(eq(learningDiagnosticReports.studentId, filters.studentId))
    if (filters.reportType) conditions.push(eq(learningDiagnosticReports.reportType, filters.reportType))
    if (filters.status) conditions.push(eq(learningDiagnosticReports.status, filters.status))
    if (filters.period) conditions.push(eq(learningDiagnosticReports.period, filters.period))

    // v4-P1-1 + v2-P1-1: 应用 DataScope 行级权限过滤
    // - class_taught: 仅返回所教班级学生的个人报告 + 班级报告（班级报告 studentId 为 null，需通过 classId 关联）
    //   由于当前 schema 班级报告 studentId=null，无法直接按 classId 过滤，因此对 class_taught scope：
    //   个人报告按所教班级学生 ID 过滤；班级报告（studentId=null）保留（教师可查看自己生成的班级报告）
    // - class_members: 学生角色，调用方应在 filters.studentId 中传入 ctx.userId，此处兜底过滤
    // - children: 仅返回子女的报告
    // - grade_managed: v2-P1-1 修复，返回所辖年级所有学生的报告（通过 getUserIdsByGradeId 查询年级学生 ID）
    // - all: 不过滤（admin）
    if (scope) {
      if (scope.type === "children") {
        if (scope.childrenIds.length === 0) {
          return { reports: [], total: 0 }
        }
        conditions.push(inArray(learningDiagnosticReports.studentId, scope.childrenIds))
      } else if (scope.type === "class_taught") {
        if (scope.classIds.length === 0) {
          return { reports: [], total: 0 }
        }
        const studentIds = await getStudentIdsByClassIds(scope.classIds)
        if (studentIds.length === 0) {
          return { reports: [], total: 0 }
        }
        // 个人报告按学生 ID 过滤；班级报告（studentId=null）由 generatedBy 限制为当前教师
        // 这里简化：仅返回所教班级学生的个人报告
        conditions.push(inArray(learningDiagnosticReports.studentId, studentIds))
      } else if (scope.type === "grade_managed") {
        // v2-P1-1: 年级主任仅返回所辖年级学生的报告
        if (scope.gradeIds.length === 0) {
          return { reports: [], total: 0 }
        }
        const gradeStudentIds = (
          await Promise.all(scope.gradeIds.map((gid) => getUserIdsByGradeId(gid)))
        ).flat()
        if (gradeStudentIds.length === 0) {
          return { reports: [], total: 0 }
        }
        conditions.push(inArray(learningDiagnosticReports.studentId, gradeStudentIds))
      }
      // class_members scope: 调用方应在 filters.studentId 中传入 ctx.userId（学生页已正确传入）
      // owned 和 all 不在此过滤
    }

    const whereClause = conditions.length > 0 ? and(...conditions) : undefined
    const limit = filters.limit ?? 100
    const offset = filters.offset ?? 0

    // P3-15 修复：并行查询总数和分页数据
    const [totalRows, rows] = await Promise.all([
      db
        .select({ total: count() })
        .from(learningDiagnosticReports)
        .where(whereClause),
      db
        .select({ report: learningDiagnosticReports })
        .from(learningDiagnosticReports)
        .where(whereClause)
        .orderBy(desc(learningDiagnosticReports.createdAt))
        .limit(limit)
        .offset(offset),
    ])

    const total = totalRows[0]?.total ?? 0

    // 收集所有需要查询姓名的用户 ID（学生 + 生成者），通过 users data-access 统一获取
    const userIds = new Set<string>()
    for (const r of rows) {
      if (r.report.studentId) userIds.add(r.report.studentId)
      if (r.report.generatedBy) userIds.add(r.report.generatedBy)
    }
    const userMap = await getUserNamesByIds(Array.from(userIds))

    const reports: DiagnosticReportWithDetails[] = rows.map((r) => ({
      ...serializeReport(r.report),
      studentName: r.report.studentId
        ? userMap.get(r.report.studentId)?.name ?? "Unknown"
        : null,
      generatedByName: r.report.generatedBy
        ? userMap.get(r.report.generatedBy)?.name ?? "Unknown"
        : null,
    }))

    return { reports, total }
  },
)

/** 获取报告详情 */
export const getDiagnosticReportById = cache(
  async (id: string): Promise<DiagnosticReportWithDetails | null> => {
    const [row] = await db
      .select({ report: learningDiagnosticReports })
      .from(learningDiagnosticReports)
      .where(eq(learningDiagnosticReports.id, id))
      .limit(1)
    if (!row) return null

    // 通过 users data-access 获取学生姓名和生成者姓名
    const userIds: string[] = []
    if (row.report.studentId) userIds.push(row.report.studentId)
    if (row.report.generatedBy) userIds.push(row.report.generatedBy)
    const userMap = await getUserNamesByIds(userIds)

    return {
      ...serializeReport(row.report),
      studentName: row.report.studentId
        ? userMap.get(row.report.studentId)?.name ?? "Unknown"
        : null,
      generatedByName: row.report.generatedBy
        ? userMap.get(row.report.generatedBy)?.name ?? null
        : null,
    }
  },
)

/** 发布诊断报告 */
export async function publishDiagnosticReport(id: string): Promise<void> {
  await db
    .update(learningDiagnosticReports)
    .set({ status: "published", updatedAt: new Date() })
    .where(eq(learningDiagnosticReports.id, id))
}

/** 删除诊断报告 */
export async function deleteDiagnosticReport(id: string): Promise<void> {
  await db.delete(learningDiagnosticReports).where(eq(learningDiagnosticReports.id, id))
}
